Русский
Русский
English
Статистика
Реклама

Цветовые схемы

Перевод Каждый браузер видит цвета видео по-разному

01.06.2021 10:11:39 | Автор: admin

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

В преобразовании значения RGB-триплета в конкретную длину волны света задействовано множество систем. Это преобразование должно быть стандартизовано, чтобы всё ПО, все декодеры видео, видеокарты и мониторы (даже изготовленные разными производителями в разные десятилетия) могли создавать одинаковые результаты по одинаковым входным данным. Для решения этой задачи были разработаны цветовые стандарты. Однако со временем дисплеи и другие технологии развивались. Телевидение стало цифровым, начали применять сжатие, а мы отказались от ЭЛТ в пользу LCD и OLED. Новое оборудование было способно отображать больше цветов при большей яркости, но получаемые им сигналы по-прежнему были адаптированы под возможности старых дисплеев.

Решение этой проблемы заключалось в создании нового цветового пространства. Новый HD-контент можно производить в новом цветовом пространстве, а новые HD-дисплеи могут его отображать. И если старое цветовое пространство будет перед отображением преобразовываться в новое, то старый контент не утратит совместимости.

Это работающее решение, но оно усложняет процесс создания видео. На каждом этапе процесса захвата, записи, редактирования, кодирования, декодирования, композитинга и отображения необходимо учитывать используемое цветовое пространство. И если хотя бы на одном этапе процесса используется старое оборудование, или есть баг ПО, из-за которого не учитывается цветовое пространство, то в результате может получиться некорректное изображение. Видео всё равно можно будет смотреть, однако цвета могут оказаться слишком тёмными или бледными.

Сегодня двумя самыми популярными цветовыми пространствами являются BT.601 (также называемое smpte170m; в статье я буду использовать оба этих названия), которое стало стандартом для SD-контента, и BT.709, которое стало стандартом HD-контента. Существует также BT.2020, которое становится популярнее благодаря HDR- и UHD-контенту. Стоит заметить, что разделение на HD/SD здесь немного ошибочно. Технические ограничения отсутствуют, это просто традиционный подход. HD-контент можно кодировать в BT.601, а SD-контент в BT.709. Если взять видеофайл с разрешением 1080p и уменьшить его до 480p, то цветовое пространство не изменится автоматически. Смена цветового пространства это дополнительный этап, который выполняется как часть процесса.

Что же происходит, если процесс выполняется неправильно? Давайте проведём эксперимент.

Программа ffmpeg настоящее чудо современности. Это очень популярный инструмент для работы с цифровым видео, и вся отрасль многим обязана её разработчикам. В своём эксперименте я воспользуюсь этим инструментом для создания и изменения видеофайлов.

Для начала я создам с помощью ffmpeg простой тестовый файл:

ffmpeg -f rawvideo -s 320x240 -pix_fmt yuv420p -t 1 -i /dev/zero -an -vcodec libx264 -profile:v baseline -crf 18 -preset:v placebo -color_range pc -y 601.mp4

Краткое объяснение этой команды:

ffmpeg исполняемый файл

-f rawvideo сообщает программе ffmpeg, что я передаю ей сырые пиксельные данные, а не видеофайл.

-s 320x240 разрешение выходных данных. Можно использовать любое значение, потому что все входящие значения будут одинаковыми. Но благодаря маленькому размеру мы ускоряем процесс.

-pix_fmt yuv420p указываем формат пикселей входящих данных. Yuv420p это способ описания пикселей. Здесь важно отметить, что значение yuv(0,0,0) представляет собой оттенок зелёного. Я использую этот формат в противовес RGB, поскольку он является самым популярным форматом, используемым в цифровом видео.

-t 1 ограничиваем входящие данные 1 секундой

-i /dev/zero это файл входящих данных. /dev/zero это виртуальный файл, существующий на всех компьютерах mac. Это бесконечно длинный файл, состоящий из одних нулей.

-an обозначает, что выходные данные не должны содержать звука.

-vcodec libx264 используем для сжатия видео потрясающую библиотеку libx264.

-profile:v baseline используем базовый профиль h.264. Он отключает некоторые расширенные возможности h.264, но в этом тесте они нам не понадобятся.

-preset:v placebo сообщаем библиотеке libx264, что она может потратить дополнительные ресурсы процессора на кодирование видео с повышенным качеством. В реальной ситуации эту опцию выбирать не стоит, потому что кодирование занимает КУЧУ времени и обеспечивает минимальное улучшение качества. Нам она подходит, потому что у меня мало входящих данных.

-color_range pc один компьютерный байт может иметь значения от 0 до 255. При оцифровке аналогового видео используется интервал 16-235. Он был выбран из-за того, как телевизор интерпретирует очень тёмные и очень яркие сигналы. Так как мы используем цифровой источник, я выбрал значение pc, а не tv.

-crf 18 опция постоянного коэффициента потока (constant rate factor) сообщает libx264, что нужно создать высококачественный видеофайл и использовать любое количество бит, необходимое для обеспечения качества 18. Чем меньше число, тем выше качество. 18 это очень высокое качество.

-y даёт ffmpeg разрешение на перезапись файла, если он существует.

601.mp4 имя получившегося файла.

Эта команда создаёт файл 601.mp4 длительностью 1 секунду, который можно открывать и воспроизводить. После выполнения этой команды мы можем проверить, что ffmpeg не исказил значения пикселей, выполнив следующую команду и изучив выходные данные:

ffmpeg -i 601.mp4 -f rawvideo - | xxd

00000000: 0000 0000 0000 0000 0000 0000 0000 0000
00000010: 0000 0000 0000 0000 0000 0000 0000 0000
00000020: 0000 0000 0000 0000 0000 0000 0000 0000
00000030: 0000 0000 0000 0000 0000 0000 0000 0000
00000040: 0000 0000 0000 0000 0000 0000 0000 0000
00000050: 0000 0000 0000 0000 0000 0000 0000 0000
...
...

Эти данные в шестнадцатеричном виде показывают, что все значения пикселей после декодирования равны нулю.

При рендеринге видео в Safari мы получаем такой скриншот:


Возникает вопрос: что это за цветовое пространство? Я назвал файл 601.mp4, но нигде в команде я не указывал цветового пространства, так как же Safari узнал, какой оттенок зелёного нужно рендерить? Откуда браузер знает, что yuv(0,0,0) должно быть равно rgb(0,135,0)? Очевидно, что существует алгоритм для вычисления этих значений. На самом деле, это простое матричное умножение. (Примечание: в некоторых форматах пикселей, в том числе и в yuv420p, для преобразования требуется этап пре- и постпроцессинга, но в этой демонстрации мы опустим такие тонкости). Для каждого цветового пространства имеется собственная матрица. Так как мы не задавали матрицу цветового пространства при кодировании видео, Safari просто делает предположение. Мы можем перебрать все матрицы, умножить все значения RGB на обратные матрицы и посмотреть, чему же они соответствуют, но давайте попробуем использовать более визуальный подход и посмотреть, удастся ли нам разобраться, что делает Safari.

Для этого я вставлю в файл метаданные, чтобы Safari знал, какое цветовое пространство используется, и не был вынужден угадывать.

ffmpeg -f rawvideo -s 320x240 -pix_fmt yuv420p -t 1 -i /dev/zero -an -vcodec libx264 -profile:v baseline -crf 18 -preset:v placebo -colorspace smpte170m -color_primaries smpte170m -color_trc smpte170m -color_range pc -y 601vui.mp4

Команда ffmpeg осталась практически такой же, но я добавил следующее:

-color_trc smpte170m

-colorspace smpte170m

-color_primaries smpte170m


Это метаданные цветового пространства, в который будет кодироваться файл. Я не буду объяснять различия между этими опциями, потому что для этого понадобится ещё одна статья. Пока мы просто задаём всем им нужное нам цветовое пространство. smpte170m это то же самое, что и BT.601.

Указание цветового пространства не влияет на способ кодирования файла, значения пикселей по-прежнему кодируются как yuv(0,0,0). Чтобы убедиться в этом, мы можем выполнить для нового файла команду ffmpeg -i zero.mp4 -f rawvideo - | xxd. Флаги цветового пространства не игнорируются, однако просто записываются в несколько битов внутри раздела video usability information (VUI) в заголовке видеопотока. Теперь декодер будет искать VUI и использовать его для загрузки нужной матрицы.

А вот результат:


И с VUI, и без него видео рендерятся с одинаковым цветом. Давайте попробуем файл BT.709:

ffmpeg -i 601vui.mp4 -an -vcodec libx264 -profile:v baseline -crf 18 -preset:v placebo -vf "colorspace=range=pc:all=bt709" -y 709.mp4

Новые опции:

-i 601vui.mp4 используем в качестве источника видео прежний файл 601vui.mp4

-vf "colorspace=all=BT.709" сообщаем ffmpeg, что нужно использовать видеофильтр цветового пространства для изменения значений пикселей. Это похоже на умножение матрицы для преобразования из yuv в rgb, но матрица имеет другие коэффициенты. all это сокращение для одновременного задания color_primaries, colorspace и color_trc.

Здесь мы берём видео 601vui.mp4 и используем фильтр цветового пространства для преобразования в BT.709. Фильтр цветового пространства может считать из vui файла 601vui.mp4 цветовое пространство входящих данных, поэтому нам достаточно только указать цветовое пространство, которое мы хотим получить на выходе.

Выполнив для этого файла команду ffmpeg -i 709.mp4 -f rawvideo - | xxd, мы получаем после преобразования цветового пространства значения пикселей yuv(93,67,68). Однако при рендеринге файла он должен выглядеть так же. Стоит заметить, что окончательные результаты могут и не быть идентичными, потому что мы продолжаем использовать 24 бита для кодирования каждого пикселя, а BT.709 имеет чуть больший диапазон цветов. Следовательно, некоторые цвета в BT.709 не сопоставляются точно с BT.601, и наоборот.

Посмотрев на результат, можно чётко заметить, что что-то не так. Новый файл рендерится со значениями rgb, равными 0,157,0 гораздо ярче, чем входящий файл.

image

Давайте внимательно изучим свойства файла при помощи приложения ffprobe:

ffprobe 601vui.mp4:
Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuvj420p(pc, smpte170m), 320x240, 9 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)

И

ffprobe 709.mp4:
Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuvj420p(pc), 320x240, 5 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)

Основная часть информации здесь для нас неважна, но мы заметим, что 601vui.mp4 имеет формат пикселей yuvj420p(pc, smpte170m). Так мы понимаем, что файл имеет правильный VUI. Но 709.mp4 содержит только yuvj420p(pc). Похоже, метаданные цветового пространства не были включены в выходной файл. Даже несмотря на то, что фильтр цветового пространства смог прочитать исходное цветовое пространство, и мы явным образом указали новое пространство, программа ffmpeg не записала правильный vui в конечный файл.

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

Обойти её можно, добавив метаданные цветового пространства вручную:

ffmpeg -i 601vui.mp4 -an -vcodec libx264 -profile:v baseline -crf 18 -preset:v placebo -vf "colorspace=range=pc:all=bt709" -colorspace bt709 -color_primaries bt709 -color_trc bt709 -color_range pc -y 709vui.mp4

В результате значение цвета в 709vui.mp4 будет равно rgb(0,132,0). Яркость зелёного канала чуть меньше, чем в 601vui.mp4, но поскольку преобразование цветового пространства происходит с потерями, а результат меня устраивает, то назовём это успехом.

Из этого мы можем прийти к заключению, что когда в файле не указано цветовое пространство, Safari считает, что это BT.601. И со стороны Safari это очень хорошее допущение. Но как сказано выше, BT.601 это стандарт SD-видео, а BT.709 стандарт для HD. Давайте проверим HD-видео с VUI и без него, и посмотрим, как их рендерит Safari. Я использовал те же команды ffmpeg, только изменил разрешение на 1920x1080.

image

И в SD, и в HD цвет рендерится одинаково. Делая предположение о цветовом пространстве, Safari не учитывает разрешение. Apple уже давно работает в пространстве медиа и издательского дела, поэтому я ожидал, что продукт этой компании обеспечит достойные результаты. Но если даже в Safari всё устроено настолько хитро, то интересно, как обстоит ситуация в других браузерах.

Chrome:


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

По опыту я знаю, что когда в настройках отключено аппаратное ускорение, Chrome выполняет рендеринг иначе:


Изучив этот результат, мы увидим, что значения в 601 рендерятся очень похожими на предыдущие, но файлы 709 рендерятся так, как будто в них нет VUI. Из этого мы можем заключить, что Chrome при отключенном аппаратном ускорении просто игнорирует VUI и рендерит все файлы, как будто они имеют формат 601. Это означает, что все файлы 709 будут воспроизводиться некорректно.

И наконец, давайте изучим Firefox:


Здесь нужно разобрать многое. Так как 709.mp4 и 709vui.mp4 выглядят одинаково, можно прийти к заключению, что при отсутствии VUI браузер Firefox предполагает формат BT.709. Правильный рендеринг 601vui.mp4 означает, что для контента BT.601 раздел VUI учитывается. Однако когда файл BT.601 без VUI рендерится как 709, то становится очень тёмным. Очевидно, что невозможно отрендерить картинку правильно без всей необходимой информации, однако выбранный Firefox способ искажает цвет сильнее, чем выбранные браузерами Safari и Chrome.

Как мы видим, ситуация с рендерингом цветов видео по-прежнему представляет собой Дикий Запад. К сожалению, мы рассмотрели только часть проблемы. Мы ещё не изучали Windows. Давайте сделаем это.

Microsoft Edge:


Похоже, что Edge (по крайней мере, на моём компьютере) просто игнорирует VUI и рендерит всё как 601.

Chrome (со включенным аппаратным ускорением):


Ситуация не очень отличается от Mac. При наличии VUI он обрабатывается правильно, но если его нет, для SD-контента предполагается формат BT.601, а для HD-контента BT.709. Это единственный браузер, в котором я такое видел, но в этом есть определённая логика. Так как рендеринг выполняется иначе, чем на Mac, то подозреваю, что дело в ОС или, что более вероятно, в чём-то на уровне драйверов видеокарты, и этот выбор сделан не командой разработчиков Chrome.

Firefox ведёт себя так же, как и на Mac.


Что касается Linux, iOS, Android, Roku, Fire TV, смарт-телевизоров, игровых консолей и т.д., то я оставлю это в качестве упражнения для читателя.

Чему же мы научились? Самое главное: всегда указывайте в своих видео метаданные цветового пространства. Если вы пользуетесь ffmpeg и не задаёте флаги цвета, то вы работаете неправильно. Во-вторых, хотя ffmpeg и является потрясающей программой, её популярность, простота использования и неудачно выбранные стандартные параметры сослужили плохую службу. Никогда не стоит допускать, что ПО достаточно умно, чтобы разобраться в этом самостоятельно. Руководителям проектов Ffmpeg, Google, Mozilla, Microsoft (и, вероятно, Nvidia и AMD) нужно собраться и вместе выбрать единый способ. Я понимаю, что здесь нет хорошего решения, но плохое и предсказуемое лучше, чем плохое и случайное. Лично я рекомендую всегда предполагать формат BT.601, если раздел VUI отсутствует. Это создаёт наименьшую степень искажений. Можно выбрать для согласования этого стандарта FOMS, или даже AOM, поскольку эти организации имеют довольно неплохое представительство.

И напоследок: если у вас есть видео без информации о цвете и вам нужно его преобразовать или отрендерить, то удачи!



На правах рекламы


VDSina предлагает недорогие серверы с посуточной оплатой. Интернет-канал для каждого сервера 500 Мегабит, защита от DDoS-атак включена в тариф, возможность установить Windows, Linux или вообще ОС со своего образа, а ещё очень удобная панель управления серверами собственной разработки. Давно пора попробовать ;)

Присоединяйтесь к нашему чату в Telegram.

Подробнее..

Крутые трюки с переменными CSS

02.04.2021 10:12:46 | Автор: admin


Переменные в CSS (или custom properties, кому как удобнее) изначально задумывались для хранения повторяющихся свойств вроде цветовой палитры или шрифтов в одном месте. В препроцессорах работа с переменными куда более гибкая, но магия SASS/SCSS применима не всегда и не везде, и в реальном мире часто обходятся без них, что нередко ведёт к раздуванию и размазыванию кодовой базы по разным файлам и форматам. В этой статье мы рассмотрим несколько интересных хаков, которые позволяют построить на механизме custom properties вещи, кажущиеся невозможными без препроцессоров или вмешательства JS.

Избегаем повторного определения цветов


Определять темы в чистом CSS не самое приятное занятие: обычно переключение на тёмную палитру требует смены сразу многих цветов для многих элементов: фоны, текст, ссылки, кнопки и так далее. Исходные предпочтения пользователя получаются медиа-запросом prefers-color-scheme, внутри которого нужно расставить новые цвета для всех селекторов, что приводит к раздуванию:

  :root {    --background: #fff;    --text-color: #000;    --link-color: #0089c7;    --primary-color: #165fb9;    /* ... */  }    @media (prefers-color-scheme: dark) {    :root {      --background: #1b1b1b;      --text-color: #eaeaea;      --link-color: #b76c10;      --primary-color: #8916b9;      /* и в том же духе на десятки строк в разных скоупах */    }  }


В CSS нет другого механизма для изменения переменных, но повторения всё же можно избежать с помощью дополнительных значений:

  --background: var(--light, #fff) var(--dark, #1b1b1b);


Если использовать переменную --light со значением initial, а в --dark передать валидное, но неприменимое значение, то --background получит цвет #fff. Для этой ситуации у CSS такое значение есть, и это пробел. Таким образом, для белой темы строка распарсится так:

  --background: #fff  ;


А для тёмной так:

  --background:  #1b1b1b);


Обратите внимание на пробелы, они не ломают синтаксис (что сбросило бы определение всей строки). Теперь осталось только вынести переключатель состояния в отдельные переменные:

  :root {    /* --ON и --OFF заменяют двоичную переменную */    --ON: initial;    --OFF: ;  }    /* выбираем светлую тему по умолчанию */  .theme-default,  .theme-light {    --light: var(--ON);    --dark: var(--OFF);  }    .theme-dark {    --light: var(--OFF);    --dark: var(--ON);  }    /* медиа-запрос теперь нужен только для переключения */  @media (prefers-color-scheme: dark) {    .theme-default {      --light: var(--OFF);      --dark: var(--ON);    }  }


Теперь цветовые схемы можно определять в одном месте, выглядеть это будет так:

  :root {    --background: var(--light, #fff) var(--dark, #1b1b1b);    --text-color: var(--light, #000) var(--dark, #eaeaea);    --link-color: var(--light, #0089c7) var(--dark, #b76c10);    --primary-color: var(--light, #165fb9) var(--dark, #8916b9);    /* ... */  }


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

Используем switch-case в языке без логики


Как мы помним, в CSS не существует явных условных операторов для управления состоянием кроме медиа-запросов. Но структура этого языка иногда подбрасывает возможности там, где их никто не планировал. Встречайте: switch-case на анимации!

Для свойства animation можно создавать любое количество ключевых кадров (@keyframes), которые можно использовать как постоянное хранилище состояния, если держать анимацию остановленной. Для каждого кадра нужно знать точную задержку, чтобы остановленная анимация показывала нужный момент, а не фиксировалась на первом кадре. Вот наглядный пример:



Разберём принцип работы:

  • Анимация остановлена через animation-play-state: paused.
  • Отрицательная задержка в animation-delay заставляет анимацию останавливаться на конкретном кадре (или между двумя определёнными кадрами, так работает градиент на первом ползунке). Значения на ползунке от -100s до 0s.
  • В animation-duration можно указать любое удобное число, но нужно помнить, что при проигрывании последнего кадра анимация выключается, поэтому максимальная длительность не должна совпадать по времени с последним определённым кадром (case). Поэтому в примере выше разброс ползунка 100 секунд при длительности 100.001s.


Бинарная логика на функции calc()


В первом трюке мы уже использовали переменные --ON --OFF вместо двоичной переменной. В custom properties можно хранить числовые значения, и с помощью вычислений разных параметров через calc() и clamp() можно получать 0 или 1 в самых разных сценариях (подробнее в этой статье). Довольно неудобно даже инвертировать значение явным присваиванием, как в примере выше, а пытаться строить на этом какую-то логику и вовсе кошмар. Хорошо, что основные логические операции можно выполнять прямо в объявлении переменных!

not
Тут всё просто, 1 0 = 1, 1 1 = 0

  --not: calc(1 - var(--j))


and
Простое умножение:
0 * 0 = 0
1 * 0 = 0
0 * 1 = 0
1 * 1 = 0

  --and: calc(var(--k)*var(--i))


nand
1 and = инвертированный and

  --nand: calc(1 - var(--k)*var(--i))


or
Если хотя бы один из операндов равен единице, or возвращает единицу:
k or i = (not k) nand (not i)

  --or: calc(1 - (1 - var(--k))*(1 - var(--i)))


nor
Аналогично с nand, nor = 1 or:

  --nor: calc((1 - var(--k))*(1 - var(--i)))


xor
Возвращает единицу, если ровно один из операндов равен единице:

  --xor: calc((var(--k) - var(--i))*(var(--k) - var(--i)))


Заключение


Имея на руках бинарную логику и условные операторы, в CSS можно реализовать кучу вещей, которые раньше казалось возможным делать только через грубое вмешательство со стороны JS. Но есть один нюанс чем глубже в дебри, тем менее читаемым становится код и тем сложнее его писать и поддерживать. Поэтому список подобных трюков можно продолжать ещё очень долго, но большинство приёмов из него почти наверняка не пригодятся в реальном мире. Зато основные концепции вроде switch и or помогут обойтись красивым CSS-only решением там, где раньше это казалось невозможным.



Облачные серверы от Маклауда подходят для размещения веб-проектов любой сложности! Вы можете создать собственную конфигурацию сервера в течение минуты и менять эти параметры в любой момент через удобную панель управления.
Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru