В предыдущий раз я переводил краткую теорию цвета и описание управления цветом для формата PNG. Если пересказывать своими словами, то цветовые модели делятся на физические (XYZ) и логические (RGB или YUV). В форматах хранения изображений и видео используются логические форматы (потому что они ограничены в диапазоне значений), иногда с добавлением метаданных, описывающих правила конвертации из логической модели в физическую. В то время, как логическая модель обычно хранит значения в диапазоне от 0 до 255 или от 0 до 1, физическая модель оперирует комбинацией трех чисел, каждое из которых представляет взвешенную сумму энергий излучения по всему спектру видимого цвета, взятую с разными весами.

Что касается дисплеев, для них производитель указывает характеристики, описывающие то, как цифровой сигнал из, например, RGB преобразуется в значения XYZ, излучаемые этими самыми дисплеями. Такими характеристиками является точка белого (т.е. какому физическому цвету соответствует RGB-сигнал с компонентами max/max/max), основные цвета (максимумы RGB при остальных минимумах), гамма или передаточная функция, а также охват (gamut), который описывает всё множество физических цветов, которые в принципе может отобразить дисплей.

Гамма, или передаточная функция

Согласно википедии, гамма-коррекция - это процесс нелинейного преобразования значений сигнала с целью лучшей передачи темных полутонов. На пальцах можно объяснить так: пусть есть сигнал в диапазоне от 0 до 255. Яркость точки 2 отличается от яркости точки 1 в 2 раза, а яркость 255 от 254 - в "нисколько" раз, глаз не различит. Поэтому близкие темные полутона такой сигнал передать физически не может. Теперь договоримся, что будем рассчитывать значение яркости как 2^C, где C как раз в диапазоне от 0 до 255. Тогда в примере выше обе пары точек будут соответствовать изменению яркости в 2 раза. Ура, теперь мы одинаково точно передаем и светлые, и темные полутона. Правда, человеческий глаз устроен немного иначе, и классическая формула выглядит как A * C ^ 2.2, где 2.2 - это значение гаммы.

Конечно, вся эта теория уже мхом обросла, и современная версия стандарта H.264 описывает аж целых 10 функций преобразования (Таблица E4 - Transfer characteristics [1]), их которых только 2 соответствуют значению гаммы 2.2 и 2.8. Но в любом случае, передаточная функция - это очень важная часть стандарта, и ошибка ее использования при отображении цвета приведет к очень заметным искажениям.

YUV

В модели YUV за яркость отвечает Y - взвешенная сумма компонентов RGB. U и V соответствуют разностям яркости и синего/красного каналов. За счет того, что человеческий глаз гораздо лучше отличает яркость, нежели оттенок цвета (ну, помните, из школы, бульбочки и колбочки), становится возможным экономить биты при работе в YUV-модели: в пиксельном формате yuv420 блок 2х2 пикселя кодируется четырьмя значениями яркости Y и лишь двумя значениями цветности (по одному для U и V) - экономия! Тот же блок 2х2 в yuv422 соответствует четырем значениям яркости и по двум значениям UV (блоки 2х1 по горизонтали). Больше форматов веселых и разных описаны в вики к плееру VLC, а нам достаточно того, что yuv420p встречается чаще всего в видеопотоке H.264.

Есть еще один момент: во всей этой математике преобразований, особенно реализованной на целочисленной арифметике, есть побочка в виде ошибок округления: например, формула floor(1.1*x) будет давать идентичные значения для 254 и 255, так что для перестраховки используется не полный диапазон значений байта, а, например, от 16 до 235 [2] - глядишь, до границы не дойдет. Страдает количество передаваемых цветов, зато арифметика проще. Это потихоньку уходит в прошлое, потому что, например, стандарт JPEG использует весь диапазон от 0 до 255 и называет этот формат J420.

Метаданные H.264

На этот раз не станем долго листать стандарт, а воспользуемся программой MediaInfo (практика - наше всё).

Video
ID                                       : 1
Color space                              : YUV
Chroma subsampling                       : 4:2:0
Bit depth                                : 8 bits
Scan type                                : Progressive
Color range                              : Limited
Color primaries                          : BT.709
Transfer characteristics                 : BT.709
Matrix coefficients                      : BT.709

По отредактированному выводу программы можно понять следующее:

  • Color space" YUV нам говорит, что в каналах изображения хранятся яркость и UV-компоненты цветности

  • Chroma subsampling: 4:2:0 утверждает, что на каждый пиксель U или V приходится по 4 пикселя Y

  • Bit depth: 8 bits подсказывает что значения каналов хранятся в виде 8-битных чисел

  • Scan type: Progressive соответствует хранению полного кадра - безо всякой черезстрочной развертки из древних телеков

  • Color range: Limited ограничивает задействованные значения диапазоном 16-235.

Вот это всё пока что описывает формат хранения логических цветов, без привязки к физической цветовой модели.

Декодирование битового формата

Дальше мы попробуем мысленно декодировать данные, хранящиеся в видеопотоке. Этап с декодером мы пропустим, потому что сжатие видео - это про другое. Нашими входными данными будут кадры, для которых на каждые 2х2 пикселя в наличии имеются: 4 байта яркости, 1 байт U-компоненты и 1 байт V-компоненты.

  • Избавляемся от 4:2:0: 1 байт UV-компонент используем для всех четырех пикселей. Теперь у нас на каждый пиксель приходится по 3 байта YUV-компонент.

  • Приводим значения из диапазона 16-235 к диапазону 0-1: так мы избавились от Limited, а компоненты теперь лежат в нормированном диапазоне.

Перевод в RGB

На данном этапе нам необходимо трех-компонентный вектор YUV привести к трех-компонентному вектору RGB. Формулировки знакомы? Первый курс, линейная алгебра, умножение матрицы на вектор. Поле Matrix coefficients подсказывет нам, что надо использовать формулу из Таблицы E-5 - Matrix coefficients [1], указанную ниже:

K[R] = 0.2126; K[B] = 0.0722 

Эти коэффициенты упомянуты в википедии и используются в формуле получения Y-компоненты из RGB:

Y = K[R] * R + (1 - K[R] - K[B]) * G + K[B] * B

Пристально взглянув на эту и остальные формулы, замечаем, что это линейное преобразование, которое описывается матрицей 3х3.

  • Вычисляем обратную матрицу (ага, для каждого пикселя в видео - конечно же нет, просто хардкодим ее в исходниках).

  • умножаем YUV на обратную матрицу M[-1], получаем RGB.

  • Нормируем, как указано в той же статье вики, получаем RGB, где каждая компонента лежит в диапазоне от 0 до 1.

Коррекция тональности

Дальше по классике: чтобы аккуратно передавать светлые и темные полутона, используем передаточную функцию из Таблицы E-4 - Transfer characteristics [1] на каждом из трех каналов:

формула коррекции тональности для BT.709-5
формула коррекции тональности для BT.709-5

Заметьте, мы всё еще не знаем, какие физические цвета обрабатываем: пока мы просто занимались преобразованием логических значений.

Отображение на дисплее

Допустим, мы передаем логическое RGB-значение цвета S на дисплей. Тот что-то мутит с напряжениями, транзисторами и прочей электроникой, в результате чего испускает фотоны, воспроизводящие цвет D. Вот это электронное "что-то" производитель дисплея описывает в характеристиках как преобразование D = Fd(S).

Мы же в свою очередь имеем логическое значение V, декодированное из видеофайла, но (пока что) понятия не имеем, какой физический цвет имел ввиду автор данного видео. Если мы просто отправим на дисплей V вместо S, то увидим "сферического коня в вакууме".

Однако, чтобы наш золотой видеофильский кабель не зря пылился под столом, нам очень хочется, чтобы цвет D на дисплее соответствовал физическому цвету C видеокамеры, на которую снято данное видео. Поэтому нам надо построить функцию преобразования для дисплея: S = Fs(V). Тут нам поможет последнее оставшееся незадействованным поле метаданных Color primaries.

Таблица E-3 – Colour primaries [1]

primary

x

y

green

0.300

0.600

blue

0.150

0.060

red

0.640

0.330

white D65

0.3127

0.3290

В этой таблицы указаны физические цвета, соответствующие точке белого и основным логическим цветам (RGB), использованным при кодировании видео. Хотя скорее можно понимать наоборот: физический цвет white D65 записывается видеокамерой как значение 255,255,255 в RGB.

Так вот, указанные физические координаты описывают функцию преобразования C = Fc(V) из логического цвета V в видеофайле в физический C, который имел ввиду автор видео. Зная, что мы хотим, чтобы авторский физический цвет совпадал с отображенным на дисплее (C == D), мы можем построить функцию преобразования декодированного цвета в сигнал для дисплея: S = Fs(V) = Fd[-1](Fc(V)), где Fd[-1] - это функция обратного преобразования физического света от дисплея ко входному сигналу.

В итоге

Распаковав изображение в формате yuv420p и отнормировав его из Color Space: Limited, мы использовали Matrix coefficients для преобразования YUV в RGB, затем скорректировали полутона с помощью Transfer characteristics и конвертировали цвет в сигнал для имеющегося дисплея, построив функцию преобразования на основе физических характеристик дисплея и Color primaries, описывающих ту же концепцию, но для видеофайла. Теперь наше видео отображается так же, как это задумал его автор.

Ссылки

  1. Rec. ITU-T H.264 (04/2013) - описание стандарта H.264, https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-H.264-201304-S!!PDF-E&type=items.

  2. Rec. ITU-R BT.601-7 - описание стандарта сигнала для телевидения, https://www.itu.int/rec/R-REC-BT.601-7-201103-I/en

Комментарии (0)