Проблема TV/PC диапазонов видео

Работа с видео
Из песочницы
postimage

Привет, хабр!

Хочу поведать о своём недавно проведённом исследовании, в котором я изучил проблему несоответствия TV/PC диапазонов при сжатии/воспроизведении видео. Проблема эта довольно мелочная, но в то же время достаточно массовая, из-за неё я частенько раньше винил кодеки сжатия в изменении цветов.


Пример

Представьте, вы вернулись только что с отпуска, были в местечке N и сделали на мероприятии M суперские фотографии на свой, несомненно, отличный фотоаппарат F. Сбрасываете вы фотографии на компьютер, и понимаете — руки чешутся сделать видео-коллаж и показать всю эту красоту друзьям. Сказано — сделано, устанавливаете любимый видео-редактор, подправляете контрастность, гамму, всё делаете так, чтобы было максимально красиво. И вот проект уже готов: перед вами свежеиспечённый видеофайл. Запускаете для проверки и… Что-то не так, как будто уменьшили контраст и насыщенность. А что если другим кодеком?? Попробовали другой кодек — то же самое. Ладно, главное чтобы на DVD было всё нормально. Записываете клип на DVD — и цвета, действительно, стали на свои места… Как же так?

Предыстория

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

Но, чёрно-белая картинка не очень удовлетворяла людей, всё-таки зрение у нас цветное, поэтому совсем скоро появилось цветное телевидение. В цветных телевизорах так же использовался кинескоп, но для передачи цвета в нём использовались, так же как и в современных мониторах, красно-зелёно-синие пиксели. Тут стал вопрос, как передавать сигнал, что бы при этом сохранялась обратная совместимость с чёрно-белыми телевизорами? Можно, конечно, было бы вещать на соседней частоте помимо яркости, ещё и 3 цветовых канала, но, во-первых, диапазон частот, выделенный на канал, был довольно узкий, во-вторых, это сложнее, т. к. получаем в итоге 4 цветовых канала. Так математики и инженеры придумали передавать в дополнение к яркостному каналу два цветовых «хроматических» — синего и красного, получаемых как разность яркости из соответствующего цветового канала, получая из RGB цветовое пространство (Y, Cb=B-Y, Cr=R-Y). Весь цветовой диапазон можно вычислить исходя из этих трёх каналов. Более того, зрение человека менее чувствительно к цветам, поэтому это позволило сократить разрешение цветовых каналов в два раза, практически не потеряв в качестве картинки. То есть разрешение яркостного канала в PAL-кадре 720x576, а цвета для него передавались в разрешении 360x576 (так называемая 4:2:2 цветовая субдискретизация). Но как именно преобразовывать Яркость (Y) и цветовые хроматические каналы (Cb, Cr) в RGB и наоборот?

Так, в 1982 году, был заложен стандарт преобразования YCbCr<=>RGB, называется он CCIR 601 (с 1992 года — BT.601). Основываясь на результатах многочисленных экспериментов по восприятию цвета человеком, яркость определяется как сумма красного, зеленого и синего с коэффициентами 77/256, 150/256 и 29/256, соответственно.



В результате преобразования из RGB=>YCbCr получается сокращённый диапазон яркости (16-235) и цветности (16-240). Как указано в стандарте, значения 0 и 255 могут использоваться для синхронизации, а значения 1-15 и 236-254 считаются некорректными, и будут отображаться как чёрный и белый. Позже, эти сужения диапазонов перенеслись и на цифровое видео, в результате чего узкий диапазон стал стандартом в видео. Хотя для видео высокой чёткости разработали уже другой стандарт преобразования цветов — BT.709, отличающийся от BT.601 лишь коэффициентами.

Как же правильно должно сжиматься и воспроизводиться видео? Видеофайл, если он сжат в одной из яркостно-хроматических схем (а это подавляющее большинство кодеков, лишь в несжатом видео используется RGB), должен быть закодирован в узкий TV-диапазон яркости (16-235). Т. к. монитор использует всё-таки RGB-вывод, то декодер должен преобразовать YCbCr в RGB с полным диапазоном 0-255. Это почти идеальная схема, но почему почти? А вот почему:
  1. Искусственное сжатие диапазонов — из 256 оттенков серого мы получаем 220 (из 8-ми бит, по сути, получаем чуть больше 7-ми бит), причём отсутствуют объективные причины, зачем нужно сжимать диапазон, кроме как ради совместимости. Мы сознательно ухудшаем качество картинки.
  2. Каждый пиксель видео проходит от файла до точки на мониторе через кучу разных фильтров (декодер, программа плеер, рендеринг, видеодрайвер). По пути в этой длинной цепи может быть сделано много преобразований, в результате чего теряется качество из-за постоянного сужения диапазона.
  3. Из-за того, что некоторые фильтры игнорируют сужение диапазонов, а некоторые видео закодированы ошибочно в полном диапазоне, вместо узкого, другие фильтры пытаются это исправлять, в итоге получается ещё большая запутанность.


Эксперимент

Я решил провести эксперимент, протестировать, как ведут себя различные плееры/драйверы при воспроизведении стандартного узкого диапазона (16-235) и для видео, закодированного в полном (0-255) диапазоне. Для этого я взял PNG картинку с серым градиентом от 0-255, через AviSynth отдавал её наиболее популярному и современному енкодеру x264. Использовал я три скрипта avs, первый читал картинку и отдавал её «как есть», в RGB формате (как несжатое видео):

rgb.avs
ImageReader("palette.png", end=24)


Во втором и третьем файле выполнял конвертирование в YV12 цветовое пространство в полный диапазон по двум стандартам BT.601 и BT.709:

pc601.avs
ImageReader("palette.png", end=24)
ConvertToYV12(matrix="PC.601")


pc709.avs
ImageReader("palette.png", end=24)
ConvertToYV12(matrix="PC.709")


Далее я осуществлял сжатие несколькими вариантами. Дело в том, что в x264 есть два параметра, которые могут влиять на результат: --input-range [TV, PC] и --range [TV, PC]. В старых версиях x264 за это отвечал параметр --fullrange.

colortest.cmd
x264.exe --preset veryslow --crf 1 --output rgb.mp4 rgb.avs
x264.exe --preset veryslow --crf 1 --input-range TV --range TV --output rgb-tv-tv.mp4 rgb.avs
x264.exe --preset veryslow --crf 1 --input-range TV --range PC --output rgb-tv-pc.mp4 rgb.avs
x264.exe --preset veryslow --crf 1 --input-range PC --range TV --output rgb-pc-tv.mp4 rgb.avs
x264.exe --preset veryslow --crf 1 --input-range PC --range PC --output rgb-pc-pc.mp4 rgb.avs

x264.exe --preset veryslow --crf 1 --output pc601.mp4 pc601.avs
x264.exe --preset veryslow --crf 1 --input-range TV --range TV --output pc601-tv-tv.mp4 pc601.avs
x264.exe --preset veryslow --crf 1 --input-range TV --range PC --output pc601-tv-pc.mp4 pc601.avs
x264.exe --preset veryslow --crf 1 --input-range PC --range TV --output pc601-pc-tv.mp4 pc601.avs
x264.exe --preset veryslow --crf 1 --input-range PC --range PC --output pc601-pc-pc.mp4 pc601.avs

x264.exe --preset veryslow --crf 1 --output pc709.mp4 pc601.avs
x264.exe --preset veryslow --crf 1 --input-range TV --range TV --output pc709-tv-tv.mp4 pc709.avs
x264.exe --preset veryslow --crf 1 --input-range TV --range PC --output pc709-tv-pc.mp4 pc709.avs
x264.exe --preset veryslow --crf 1 --input-range PC --range TV --output pc709-pc-tv.mp4 pc709.avs
x264.exe --preset veryslow --crf 1 --input-range PC --range PC --output pc709-pc-pc.mp4 pc709.avs


В результате получил 15 файлов. После проверки гистограммы с помощью AviSynth получил, что без указания параметров --range и --input-range, видео сжимается так, как подаётся, иначе происходит конверсия диапазонов средствами x264. То есть гладкую гистограмму обеспечивают только файлы pc601.mp4 и pc709.mp4, но т. к. эти стандарты отличаются лишь коэффициентами для хроматических каналов, для нашей серой шкалы разницы между ними не будет, тестировать я буду только два файла — rgb.mp4 и pc601.mp4 (узкий и полный диапазон соответственно).

Протестировал я воспроизведение этих файлов на 4-рёх компьютерах, везде стоит Windows 7 и кодеки ffdswow и K-Lite Codec Pack. Результат занёс в табличку:

table

Пояснение к таблице:
normal — правильное отображение
in — неправильное отображение, диапазон 16-235 не маштабирован до 0-255
out — неправильное отображение, диапазон 0-255 маштабирован по ошибке, в результате образовалась обрезка диапазона.

Вот скриншоты результатов в плеере MPC-HC:


Стоит отметить, что настройки диапазонов есть практически в каждом фильтре. В моём случае эта настройка есть и в ffdshow video decoder, в рендерах Lav, Haali, в настройках драйвера видеокарты, и также можно заставить преобразовывать диапазон в плеере (есть специальный шейдер). Однако почему-то переключение диапазона в ffdshow video decoder не влияла на результат. Настройка в драйвере не везде влияет на результат, там, где он влиял, я занёс в таблицу (строка настройки видеокарты). Кроме того, при DXVA и CUDA аппаратном ускорении, правильным считается только диапазон 16-235, не говоря уже о телевизорах.

Также заодно протестировал два видео-редактора:
  1. VirtualDub — диапазон не трогает, если нет конверсии в из YV12/YUV2 в RGB и наоборот, иначе делает преобразование диапазонов, фильтры работают в RGB, при отображении в программе делает преобразование TV->PC.
  2. AviDemux — диапазон не меняет (попросту не работает с RGB источниками), фильтры работают с YUV2, при отображении в программе конверсия TV->PC.


Итоги

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

Правильный (хотя и не совсем) диапазон для сжатия видео — TV (16-235), иначе в большинстве случаев отображаться видео будет некорректно (с обрезкой диапазона чёрного и белого). И хотя с точки зрения цифрового видео, логичнее было бы хранить и отображать полный диапазон без всяких преобразований, на нынешнем этапе сложился такой стандарт, не соблюдая который, получим в большинстве устройств неправильное отображение.

Как с этим бороться? Сразу приходит на ум — в метатэгах указывать используемый диапазон, этот метод даже уже реализован (существует флаг fullrange), но, к сожалению, очень часто этот флаг игнорируется. Поэтому:
  1. разработчикам — необходимо заботиться об реализации влияния такого флага, а также сделать правильное отображение видео в своих плеерах/кодеках, если необходимо преобразование диапазона;
  2. пользователям, если возникла проблема с цветами, попробовать настроить параметры (в кодеке/плеере/драйвере), или попробовать другой плеер;
  3. обработчикам видео — знать о существовании такой проблемы, и правильно сжимать (кодировать в диапазоне 16-235 или хотя бы указывать флаг fullrange).


скачать архив с экспериментом

Ссылки

Wikipedia YCbCr
Wikipedia Rec. 601
Wikipedia Rec. 709
Wikipedia 4:2:2
Wikipedia Цветовая субдискретизация
Теги:видеоBT.601BT.709fullrangeyuvrgbtv
Хабы: Работа с видео
+47
12,5k 60
Комментарии 21

Похожие публикации

iOS-разработчик
от 110 000 до 150 000 ₽InstreamaticТулаМожно удаленно
Middle React Native / Flutter developer, full-time, дистанционно
от 1 500 до 2 000 $Триумф-МаркетингМожно удаленно
Java разработчик
от 200 000 ₽НТВМосква
Разработчик С/С++
до 180 000 ₽Stream LabsМоскваМожно удаленно
iOS-разработчик (удаленная работа, полная занятость)
от 150 000 до 230 000 ₽42TapsМожно удаленно

Лучшие публикации за сутки