Как быстро выбрать уровень сжатия JPEG в C#
Пример
Этот метод является достаточно быстрым, но часто вызывает заметную задержку, особенно если конечный уровень сжатия мал, поэтому программа должна делать много попыток. В худшем случае, если программа должна использовать уровень сжатия 5, программа сжимает и сохраняет файл 20 раз с уровнями сжатия 100, 95, 90, 85, ... 5.
В этом примере используется следующий код для выполнения более быстрого бинарного поиска для лучшего уровня сжатия.
// Сохраните файл с указанным максимальным размером файла. // Возвращаем уровень сжатия. public static int SaveJpgAtFileSize(Image image, string file_name, long max_size) { // Мы проверяем уровни, кратные 5. const int delta = 5; // le_level & lt; = правильный уровень. // Начнем с дельта. Это будет использоваться, если ни один уровень не работает. int le_level = delta; // gt_level & gt; правильный уровень. // Начнем с первого кратного дельта больше 100. int gt_level = (100 / delta) * delta + delta; // Петля до тех пор, пока нет возможности // между le_level и gt_level. for (; ; ) { // Выбираем тестовый уровень посередине. int test_level = (le_level + gt_level) / 2; // Делаем его кратным дельта. test_level = (test_level / delta) * delta; // Если test_level == le_level, то le_level является победителем. if (test_level == le_level) break; // Попробуйте сохранить на этом уровне сжатия. SaveJpg(image, file_name, test_level); // Посмотрим, больше ли размер или // меньше целевого размера. if (GetFileSize(file_name) > max_size) { // Файл слишком большой. Ограничение на меньшие размеры. gt_level = test_level; } else { // Файл достаточно мал. Рассмотрим более крупные размеры. le_level = test_level; } } // Сохранить в конечном размере. SaveJpg(image, file_name, le_level); // Вернем размер. return le_level; }
В коде используется константа delta для определения коэффициентов, которые он считает для уровня сжатия. Эта версия использует кратность 5, поэтому она рассмотрит уровни сжатия 5, 10, 15, 20 и т. д. Код делает это значение постоянным, поэтому легко изменить кратность. Например, вы можете установить это значение 1, чтобы рассмотреть все возможные уровни сжатия. (Используемый здесь алгоритм достаточно быстр, чтобы сделать это практически. Предыдущий пример должен был сохранить файл до 20 раз в худшем случае. Эта версия должна сохранить файл всего 8 раз, чтобы найти лучший уровень сжатия, даже если вы установите delta = 1.)
Для управления поиском программа использует две ключевые переменные: le_level и gt_level. Значение le_level всегда меньше или равно оптимальному уровню. Значение gt_level всегда строго больше оптимального уровня. Когда он проходит через свой цикл, программа рассматривает уровни сжатия, где le_level & le; = level & lt; gt_level .
Изначально программа устанавливает le_level = delta и gt_level = следующий кратный delta больше 100. В этом примере это значение равно 105, поэтому изначально 5 & le; = уровень & lt; 105 . Это имеет смысл, потому что метод всегда должен возвращать значение от 5 до 100. (Я сделал delta наименьший уровень сжатия, который будет использовать метод, даже если файл по-прежнему больше желаемого.)
Установив le_level и gt_level, программа вводит бесконечный цикл. Внутри цикла код устанавливает test_level как кратное delta, ближайшего к середине между le_level и gt_level >. (Это шаг двоичного деления.)
Если test_level == le_level, тогда программа рассмотрит все возможные значения уровня, чтобы она вышла из цикла. Например, предположим le_level = 30, gt_level = 35 и delta = 5. Затем код устанавливает test_level = 30. На этом этапе мы знаем, что 30 & le; уровень & lt; 35, поэтому желаемый результат должен быть 30, что является значением le_level.
Если цикл не заканчивается в этот момент, программа сохраняет файл на уровне сжатия test_level и получает размер файла. Если файл слишком велик, программа понижает gt_level до test_level, поэтому он рассматривает меньшие уровни в следующем цикле. Если файл не слишком большой, то программа поднимает le_level на test_level, поэтому он рассматривает более крупные уровни в следующем цикле.
Цикл продолжается, опуская gt_level или поднимая le_level, пока test_level == le_level и код не выйдет из цикла. р>
Когда цикл завершается, программа, вероятно, просто сохранила файл на самом маленьком уровне, который делает файл слишком большим, поэтому он сохраняет файл на уровне le_level и возвращает le_level . р>