Как алгоритм JPEG имитирует биологическое устройство вашей сетчатки

Вслед за материалом о том, почему вашему монитору не под силу отобразить истинный бирюзовый, а 65% цветового спектра остаются для него недосягаемыми, один мой приятель, далекий от мира высоких технологий, задал резонный вопрос: «Если дисплей искажает реальность, то как JPEG распоряжается оставшимися 35% данных?» Вопрос заставил меня углубиться в спецификации. Спустя полчаса я уже не помнил первоначальную цель, захваченный открытием: инженеры, финализировавшие стандарт в 1992 году, фактически совершили реверс-инжиниринг человеческого зрения и интегрировали его механизмы в алгоритм сжатия.

И это не преувеличение. В предыдущей статье я демонстрировал, как экраны эксплуатируют метамерию, подменяя полный спектр тремя стимулами, что мозг воспринимает как чистый цвет. JPEG идет еще дальше. Каждый этап его пайплайна — это выверенный хак, нацеленный на конкретные несовершенства нашей зрительной системы. Пока монитор манипулирует колбочками сетчатки, JPEG обманывает всё остальное: от контрастной чувствительности до механизмов распознавания частот в коре головного мозга.

Я хочу поделиться этим с вами, поскольку это один из самых элегантных примеров инженерной мысли, что я встречал за 12 лет практики. Если раньше мы говорили о том, как мало мы видим, то сейчас разберем, как мало нам достаточно видеть, чтобы мозг поверил в полноту картины. А в конце я проверил теорию на практике.


Глаз — это не видеокамера (очевидный, но важный факт)

Мы привыкли воспринимать глаза как некие биологические сенсоры: свет падает на сетчатку, данные передаются в мозг, и возникает «картинка». Кажется, что это аналог матрицы фотоаппарата. Но это заблуждение.

Ваше зрение — это крайне предвзятый инструмент с массой аппаратных ограничений. Сетчатка снабжена двумя типами фоторецепторов: около 120 миллионов палочек отвечают за восприятие яркости, и лишь 6–7 миллионов колбочек обеспечивают цветовое зрение.

Задумайтесь: соотношение 20 к 1. Фактически ваша сетчатка представляет собой черно-белый сенсор сверхвысокого разрешения, к которому вторым слоем небрежно приклеили низкокачественный цветовой модуль из эпохи ранних веб-камер.

Разработчики JPEG прекрасно это осознавали.


YCbCr: почему JPEG избавляется от RGB в первую очередь

При сохранении изображения в формате JPEG первым делом происходит не сжатие и даже не оптимизация, а смена парадигмы описания цвета.

Исходные данные поступают в RGB. Так их выводит монитор и захватывает камера. Однако JPEG мгновенно транслирует их в систему YCbCr:

  • Y — яркостная составляющая (Luma).

  • Cb — синяя цветоразностная компонента.

  • Cr — красная цветоразностная компонента.

Зачем такие сложности? На первый взгляд, это лишняя вычислительная нагрузка. Ведь RGB универсален. Но в RGB все каналы критически важны: уберите любой из них, и структура изображения рухнет.

В YCbCr ситуация иная. Канал Y несет информацию, которую наш глаз считывает безупречно благодаря 120 миллионам палочек. Мы замечаем малейшие нюансы теней, контуров и текстур. А каналы Cb и Cr обрабатываются всего 6 миллионами колбочек. Цветовое разрешение нашего зрения на порядок грубее яркостного.

Переход к YCbCr — это стратегическое разделение данных на те, что глаз «видит отчетливо», и те, которыми можно безболезненно пожертвовать.

Формула преобразования говорит сама за себя:

Y = 0.299 × R + 0.587 × G + 0.114 × B

Обратите внимание на веса: зеленый составляет более половины значения яркости (0.587), тогда как синий — лишь 0.114. Это математическая модель нашей сетчатки. Пик чувствительности глаза приходится на желто-зеленую область (около 555 нм). Эволюция затачивала зрение приматов под поиск спелых плодов в листве, и стандарт 1992 года филигранно это копирует.

Если вы когда-нибудь переводили фото в ЧБ и замечали, что простое усреднение каналов дает плоскую картинку, а правильные коэффициенты — глубокий контраст, знайте: вы видели работу Y-канала JPEG.


Chroma subsampling — легальный чит-код

После конвертации в YCbCr алгоритм позволяет хранить цветовые каналы с пониженным разрешением. Это называется цветовой субдискретизацией (chroma subsampling), где самым популярным является режим 4:2:0.

Что это означает? Яркость (Y) сохраняется в исходном виде, а для цвета (Cb и Cr) берется один пиксель на блок 2×2 яркостных пикселя.

Мы отбрасываем 75% цветовых данных, и человеческий глаз этого практически не замечает. Почему? Потому что биологическая периферия нашего зрения устроена так же. Пространственное разрешение цветового восприятия в несколько раз ниже яркостного.

JPEG не просто сжимает цвет — он отсекает то, что наш мозг и так не в состоянии обработать.

Проверить это легко: разложите изображение в Photoshop на каналы YCbCr. Цветовые компоненты будут выглядеть как размытые пятна, хотя мозг будет упорно твердить, что фото четкое. Вы просто никогда не видели цвет в отрыве от яркости — ваша визуальная система объединяет их автоматически.


ДКП: эксплуатация нейронов зрительной коры

Цвет урезан, но основное сжатие происходит на этапе дискретного косинусного преобразования (ДКП) и квантования. Казалось бы, причем здесь биология?

Дело в том, что ДКП дробит картинку на блоки 8×8 и раскладывает их на 64 частотных компонента: от плавных градиентов до резких деталей и шумов.

Ниже представлен базис ДКП для такого блока — набор из 64 «элементарных кирпичиков», из которых собирается любое изображение:

Поразительно, но нейроны первичной зрительной коры (зона V1) функционируют по схожему принципу. Еще в 1960-х годах Хьюбел и Визел (лауреаты Нобелевской премии) выяснили, что эти клетки реагируют на полосы определенной ориентации и частоты. Ваша зрительная кора буквально выполняет подобие спектрального анализа входящего сигнала.

Таким образом, ДКП — это математическая аппроксимация того, как мозг декомпозирует визуальный поток.


Квантование — карта ваших когнитивных пробелов

После ДКП мы получаем 64 коэффициента. Чтобы добиться сжатия, их нужно огрубить или обнулить. Для этого используется таблица квантования: каждый коэффициент делится на соответствующее число и округляется.

Вот как выглядит стандартная матрица для яркости:

Принцип прост: левый верхний угол (низкие частоты) содержит малые делители — здесь мы храним данные бережно. Правый нижний угол (высокие частоты) имеет огромные значения — эти детали выбрасываются безжалостно.

Эта матрица — не что иное, как цифровое воплощение графика контрастной чувствительности (CSF) человека.

CSF определяет, насколько хорошо мы видим узоры разной частоты. Пик нашей зоркости находится в диапазоне 3–5 циклов на градус обзора. Слишком плавные или слишком мелкие детали мы распознаем хуже, а за порогом 60 циклов на градус наступает полная визуальная слепота.

Значения в таблице подобраны эмпирически через психовизуальные тесты. Цель — сделать так, чтобы артефакты округления возникали именно там, где глаз не в силах отличить оригинал от аппроксимации.

Понижая параметр Quality в редакторах, вы фактически увеличиваете эти делители, вторгаясь в зону видимых глазом деталей. Quality 100 — это ювелирная точность, Quality 10 — грубая имитация, предполагающая, что зритель почти слеп.


Экспериментальное подтверждение

Теория красива, но можно ли визуализировать то, что JPEG «отрезает» от реальности? И действительно ли это незаметно?

Я воспользовался простым скриптом на Python: берем эталонный PNG, сохраняем его как JPEG с разной степенью сжатия, а затем вычитаем полученный результат из оригинала. То, что останется — и есть выброшенная информация.

from PIL import Image
import numpy as np

original = np.array(Image.open("photo.png")).astype(np.float32)

for q in [95, 80, 50, 20, 5]:
    Image.open("photo.png").save(f"q{q}.jpg", quality=q)
    compressed = np.array(Image.open(f"q{q}.jpg")).astype(np.float32)
    
    diff = original - compressed
    # Визуальная нормализация
    diff_visual = ((diff - diff.min()) / (diff.max() - diff.min()) * 255)
    Image.fromarray(diff_visual.astype(np.uint8)).save(f"diff_q{q}.png")

Я ожидал увидеть хаотичный цифровой шум, но результат оказался куда интереснее.


Призраки в деталях

При Quality 95 карта различий практически черная. Только при экстремальном выкручивании контраста проявляются едва уловимые следы.

При Quality 80 (золотой стандарт веба) на карте проступают контуры, резкие границы и высокочастотные текстуры: волосы, ворс ткани. Это именно те данные, которые ДКП отправляет в высокочастотный спектр, и которые квантование режет первыми.

При Quality 50 разница напоминает детальную гравюру. Здесь собраны все тонкие линии и микротекстуры.

На Quality 5 ситуация становится драматичной.

Здесь в «мусоре» отчетливо видна вся композиция снимка. Алгоритм стал настолько агрессивным, что начал уничтожать и средние частоты — те самые, к которым наш глаз максимально чувствителен. Именно поэтому такие изображения кажутся нам «разваленными»: кодек перешел черту дозволенного биологией.

По сути, грань между качественным фото и визуальной кашей — это и есть граница вашей контрастной чувствительности. Для большинства сцен она пролегает в диапазоне Quality 20–40.


Магия числа 80

Мне стало любопытно: почему именно 80 считается эталоном для веба?

Я проанализировал зависимость размера файла и индекса структурного сходства (SSIM) от параметра качества.

from PIL import Image
import numpy as np
from skimage.metrics import structural_similarity as ssim
import io

original = np.array(Image.open("photo.png"))
results = []

for q in range(5, 101):
    buf = io.BytesIO()
    Image.open("photo.png").save(buf, format="JPEG", quality=q)
    size_kb = buf.tell() / 1024
    
    buf.seek(0)
    compressed = np.array(Image.open(buf))
    score = ssim(original, compressed, channel_axis=2)
    results.append((q, size_kb, score))

Вес файла растет экспоненциально при приближении к отметке 100. Разница в объеме между Quality 80 и 95 может быть двукратной, при этом SSIM изменится лишь в третьем знаке после запятой.

Quality 80 — это точка перегиба, обусловленная нашей биологией. До этого порога квантование затрагивает лишь то, что мы почти не видим. После — начинает «резать по живому».


Эпилог: инженерия восприятия

Мы привыкли использовать тег <img>, не задумываясь о том, какая колоссальная научная база стоит за привычными форматами. JPEG — это не просто математика, это десятилетия исследований в области психофизики, изучение порогов восприятия и изящная попытка адаптировать данные под конкретный биологический пайплайн.

JPEG — это алгоритм сжатия восприятия, а не просто байтов.

Современные WebP и AVIF продолжают эту традицию. Например, в AVIF внедрен синтез зернистости (film grain synthesis): кодек не хранит шум, а генерирует его на лету, зная, что мозг не помнит точного паттерна зерна, лишь его характер. Снова хак над биологией.

Самое ироничное, что таблицы квантования в спецификации JPEG носят лишь рекомендательный характер. Продвинутые энкодеры вроде MozJPEG тратят ресурсы на подбор индивидуальных матриц для каждого кадра, создавая детальную карту того, что не увидите именно вы в этой конкретной сцене.

А новый стандарт JPEG XL идет еще дальше, интегрируя перцептивную метрику Butteraugli непосредственно в ядро. Кодек моделирует работу вашего зрительного тракта, включая адаптацию к яркости и маскировку контуров, буквально спрашивая систему: «Заметишь ли ты подмену?»

Спустя тридцать лет мы всё так же балансируем на грани между видимым и невидимым, просто инструменты стали острее, а модели — точнее.

Помните об этом, глядя на характерные блоки 8×8 на пережатом фото. Это не просто ошибка — это сетка, по которой алгоритм препарировал реальность, адаптируя её под ваши глаза. Обычно он делает это настолько мастерски, что швы остаются невидимыми.


Примечание для специалистов: я сознаю, что при ярком свете палочки инактивируются, а острота зрения определяется нейронным пулингом. Но метафора разделения на яркостный и цветовой сенсоры слишком точно отражает архитектуру JPEG, чтобы ей пренебрегать.

 

Источник

Читайте также