Сегодня для нас нет ничего необычного в том, чтобы отправить фотку другу и не волноваться по поводу того, какое устройство, браузер или операционную систему он использует. Все это благодаря формату JPEG, который может уменьшить размер исходного файла в 10, 50 и даже в 100 раз. Изображения формата JPEG встречаются повсюду в нашей жизни в интернете, но за простотой и удобством использования скрываются алгоритмы, устраняющие детали, не воспринимаемые человеческим глазом. В итоге получается высочайшее визуальное качество при наименьшем размере файла.
Вы когда-нибудь задумывались, сколько места фотки на наших устройствах?
Давате посмотрим на этот снимок:
Его разрешение 12.2 мегапикселя или 4032 × 3024 точек. Сколько он должна бы весить? Давайте посчитаем. Как известно, цвет каждого пикселя определяется тремя составляющими: красным, зелёным и синим. Каждая составляющая кодируется восьмибитным числом. Получим, что такое изображение будет весить 8х3х4032x3024 = 292 626 432 бит, то есть почти 300 мб!!!
В реальности же она весит 7.36 мб. То есть в 40 раз МЕНЬШЕ! А если пожать через Whatsapp, будут какие-то 200 килобайт. Все благодаря формату JPEG, с которым мы сталкиваемся постоянно.
Конечно, присутствует небольшая потеря качества, но по сравнению с тем, что размер файла был уменьшен в несколько десятков раз, это уже не играет большой роли. И новые форматы отвоевывают рынок - тот же hevc. JPEG все еще остается королем индустрии. Так что давайте разберемся, как конкретно всё это работает? Причем здесь психология зрения, и как хитрая математика экономит место.
К началу 80-х годов прошлого столетия компьютеры умели хранить и показывать цифровые изображения, однако для воплощения этого существовало множество различных решений. Например, нельзя было просто отправить изображение с одного компьютера на другой. Для решения этой проблемы в 1986 году был собран комитет экспертов со всего мира под названием "Объединённая группа экспертов по фотографии" (Joint Photographic Experts Group, JPEG). Эта группа людей и создала стандарт сжатия цифровых изображений JPEG в 1992 году, который спасает нас до сих пор.
Прежде всего давайте разберёмся, почему вообще возможно сжать изображение так, чтобы наши глаза при этом не замечали изменений, вносимых форматом в фотографию. JPEG удаляет информацию, которую человеческий глаз плохо воспринимает.
Теперь стоит поговорить о том, как устроено наше зрение. Как известно, в глазу есть два типа восприимчивых к свету клеток: палочки и колбочки (rods and cones). Палочки плохо восприимчивы к свету, но играют решающую роль для зрения в условиях низкой освещенности, в то время как колбочки имеют цветовые рецепторы. Именно они обеспечивают нашим глазам цветное зрение. В каждом глазу имеется около 100 миллионов палочек и всего лишь 6 миллионов колбочек.
Как следствие, глаз гораздо более восприимчив к изменениям в яркости изображения и менее восприимчив к изменению в цвете. Поэтому при сжатии картинки можно использовать такую хитрость: во первых, отделить цвет от яркости, во вторых, убрать немного цвета, и в таком случае никто ничего не заметит. Именно на данной хитрости и основывается первый и очень важный этап сжатия, называемый цветовой субдискредитацией.
Но как именно нам отделить яркость от цвета? Как известно, каждый пиксель характеризуется тремя компонентами: R,G,B - по одной переменной для каждого цвета, которые могут принимать значения в диапазоне от 0 до 255.
Хитрость в том, что эту же информацию мы можем сохранить, используя другие три переменные: Y, Cb, Cr. Тогда мы без проблем можем преобразовать изображение с пикселями формата RGB в новый формат с другими компонентами Y,Cb,Cr. Здесь Y отвечает за яркость, а Cb и Cr- цветовые каналы. Таким образом, вычислив эти переменные, мы получаем три новых изображения, составленных из этих трех компонент. Вычисление происходит по таким мудреным формулам, если интересно.
Такое разделение необходимо, чтобы дальше мы могли убрать часть деталей из цветовых каналов (составленных из Сb и Cr).Пока что никакие данные мы не удаляли, а математические преобразования из одного формата в другой являются обратимыми. Данные действия, конечно же, проделываются над всеми пикселями изображения.
Теперь мы и будем использовать особенность нашего зрения: воспринимать изменения в цвете хуже, чем изменения в яркости. Ранее мы получили из исходной картинки 2 цветовых переменных и одну яркостную.
Теперь делается еще один ход: каждые 4 пикселя в обоих цветовых изображениях объединяются в 1, тем самым удаляется повторяющаяся информация. В результате, детали, плохо воспринимаемые нашими глазами, т.е. обе цветовые составляющие (Cr и Сb), уменьшаются до ¼ своего исходного размера, а яркостная составляющая остаётся прежней. Таким образом, всего за два шага изображение становится в 2 раза меньше исходного размера
Было: 1+1+1=3
Стало: ¼+¼+1=1,5
При этом если сейчас собрать эти 3 составляющие в одно исходное изображение, то невооруженным глазом разницу между оригиналом и сжатым изображением заметить будет невозможно.
Теперь выполняется подготовка к самому важному, сложному и умному этапу. Для этого каждая из 3 наших картинок, составленных из яркостного и двух цветовых каналов, разбивается на блоки пикселей 8x8. Теперь алгоритмы сжатия должны каким-то образом понять, насколько много деталей в каждом из таких блоков. Если деталей мало, то можно закодировать меньшим количеством бит, уменьшая размер файла, а если деталей много, то наоборот.
Иными словами, если бы картины в музеях хранились в джипеге, то любая картина Ван Гога занимала бы меньше места, чем черный квадрат Малевича.
Такой анализ и производится с помощью штуки со страшным названием - дискретное косинусное преобразование. Прежде, чем переходить к нему, рассмотрим такой простой пример. У нас есть разноцветные полупрозрачные стеклышки. Накладывая их друг на друга, мы можем получить тот цвет, который нам нужен, т.е., например,чтобы получить фиолетовый цвет, нужно наложить синее стеклышко на красное. Такая же ситуация и в случае с алгоритмом.
Рассмотрим один из блоков 8х8. Все последующие действия будут выполняться для каждого из таких блоков. Такой блок содержит 64 пикселя. Оказывается, что любое простое монохромное изображение можно представить в виде комбинации вот таких 64 базовых картинок.
То есть, накладывая их друг на друга с разной степенью прозрачности, можно получить любую простую картинку 8х8 примерно так же, как и в примере со стеклышками.
Именно таким образом мы и можем перестроить любой наш блок 8х8 путём наложения этих базовых картинок друг на друга. При этом каждый из узоров умножается на число, являющееся показателем того, какая его часть используется при построении определенной картинки.
Суть алгоритма дискретного косинусного преобразования и заключается в вычислении данных чисел. На выходе получаем матрицу 8х8, состоящую из чисел, отображающих, насколько большой или не очень вклад каждого узора в данное изображение 8х8. Выглядит это примерно так. Прикольно, да?
Чем больше число, тем больший вклад базового изображения (узора), соответствующего данному числу. Коэффициенты, находящиеся в левой верхней части матрицы отвечают за плавные переходы и более общие черты, а коэффициенты, лежащие в правой нижней части- за частые переходы, т.е. за тонкие детали. Если деталей в данном блоке оказывается мало, то коэффициенты, отвечающие за них (находящиеся в правой нижней части матрицы) будут равны нулям или близким к нулю значениям. (Именно на данном этапе отбрасывается огромная часть информации, не видимой человеческим глазом.) Это важный момент запомним.
Это немного напоминает составление фоторобота: нос номер 9, рот номер 13, глаза среднее между нумером 6 и 42.
Прежде, чем идти дальше, подведем промежуточные итоги. Что мы имеем на данный момент?
У нас есть матрица из 64 чисел, содержащих информацию о том, как именно нужно использовать базовые изображения. Если не считать этап цветовой субдискредитации, то пока что мы не отбрасывали существенное количество информации, а только определяли, что нужно отбросить.
Как это делается?
Для этого этапа используется вот такая таблица. Называется таблица квантования. К квантовым компьютерам не имеет отношения. Не пугайтесь, какждется это послдений сложный термин на сегодня. Если по простому: она определяет какую часть из полученной прежде информации мы возьмем в итоговый файл. Смотрите: это матрица 8х8, содержащая коэффициенты, на которые мы делим числа из нашей исходной матрицы. Степень сжатия задаётся характеристиками таблицы квантования, т.е. чем больше коэффициенты в этой таблице, тем меньше будут числа в новой матрице. В зависимости от характеристик таблицы квантования мы можем уменьшить размер изображения до 95% от первоначального размера, но в таком случае, конечно, качество сжатого изображения будет далеко не самым высоким. Именно эта таблица и является основной настройкой jpeg, регулирующей степень сжатия.
Теперь переходим к квантованию, т.е. этапу, на котором будет производиться наибольшее сжатие. Здесь происходит сначала деление чисел из матрицы, которую мы получили на прошлом этапе, на определенные коэффициенты, соответствующие таблице квантования, а затем происходит округление полученных чисел до ближайшего целого (например: делим 560/4=140; -41/3=-13,7- округляем до целого, получаем -14 и т.д.). Но что ещё за таблица квантования и откуда она берется?
- Исходная матрица
- Таблица квантования
- Новая матрица
Если посмотреть на таблицу квантования, то можно увидеть, что самые большие числа расположены в правой нижней части таблицы. Это значит, что большего всего отбрасываются высокочастотные данные, т.е. отвечающие за те детали, которые наши глаза хуже всего воспринимают.
В результате этого процесса получается, что большая часть чисел нашей исходной матрицы стала равной нулю, так как числа, близкие к правому нижнему углу матрицы были слишком малы и после деления и округления обнулились. Остаются только числа, близкие к левому верхнему углу матрицы. На этом этапе и происходит самое существенное сжатие изображения, т.е. практически все данные, к которым наши глаза плохо восприимчивы, отбрасываются.
Далее используется зигзагообразное сканирование матрицы чисел, в результате чего получается числовая строка, в начале которой ненулевые значения, а в конце - нулевые.
Затем вместо того, чтобы перечислять все числа строки по порядку, мы используем алгоритм кодирования длин серий, благодаря которому можно не перечислять все нули, а просто назвать их количество.
Далее уже сжатая последовательность кодируется с помощью алгоритма Хаффмана. Это достаточно сложный процесс, но если говорить кратко, то нули кодируются меньшим количеством бит, а ненулевые элементы последовательности кодируются большим количеством бит.
Процесс сжатия завершён. Мы получили сжатое изображение в виде последовательности чисел.
Как же теперь вывести его на экран? Для этого необходимы все те же самые действия, но уже проделанные в обратном порядке: 1. Декодирование с помощью алгоритма Хаффмана, 2. Декодирование по методу длин серий, 3. Заполнение матрицы 8х8 коэффициентами зигзагообразным методом для каждого из блоков 8х8, 4. Умножение каждого числа матрицы на соответствующий коэффициент согласно таблице квантования, 5. Масштабирование компоненты цветов, 6. Преобразование полученных значений Y, Cb и Cr для каждого пикселя в RGB и 7. Вывод изображения уже в формате JPEG на экран. Готово!
Подведём итоги. Как видите, при работе JPEG используется огромное количество нюансов: особенности строения глаза, математические методы сжатия, разложение в спектр.
Конечно, у JPEG есть достаточно большое количество недостатков: невозможность качественного сжатия текстовых изображений, ухудшение качества при повторном сохранении, но несмотря на все его многочисленные недостатки и почтенный возраст, он по сей день остаётся самым популярным форматом изображений в интернете, хотя сейчас ему на смену начали приходить другие, гораздо более продвинутые форматы, такие как HEIC, AV1, WebP и другие. Причины популярности JPEG в настоящее время, конечно, есть: он очень хорошо изучен за 30 лет существования, является бесплатным, а также всем понятен и привычен.