- PVSM.RU - https://www.pvsm.ru -

Об относительной яркости, или насколько живучим бывает легаси

Я уверен, что многим программистам знакома формула:

Y=0.299 R + 0.587 G + 0.114 B

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

Вычисляет она относительную яркость цвета (relative luminance или в некоторых контекстах luma; не путать с lightness и brightness) и широко применяется для преобразования цветного RGB-изображения в Grayscale и связанных с этим задач.

Формула растиражирована и процитирована в тысячах статей, форумных обсуждений и ответов на StackOverflow… Но дело в том, что единственно-правильное её место — на свалке истории. Использовать её нельзя. Однако же используют.

Но почему нельзя? И откуда же взялись именно такие коэффициенты?

Мини-экскурс в историю

Есть такая международная организация, которая разрабатывает рекомендации (де-факто стандарты) для сферы телерадиокоммуникаций — ITU [1].

Интересующие нас параметры прописаны в рекомендациях ITU-R BT.601 [2], принятых в 1982 году (по ссылке обновленная редакция). Уже на этом моменте можно слегка удивиться — где мы и где 82-ой год? Но это только начало.

Циферки перекочевали туда из рекомендаций ITU-R BT.470 [3] от 1970 года (по ссылке также обновленная редакция).

А они, в свою очередь — наследие цветовой модели YIQ [4], которая была разработана для североамериканской системы телевещания NTSC [5] в 1953 году! К нынешним компьютерам и гаджетам она имеет отношение чуть более, чем никакое.

Никому не напоминает байку про связь космических кораблей с шириной древнеримской лошадиной задницы?

Современные колориметрические параметеры начали выкристаллизовываться в 1970 году с модернизацией систем PAL/SECAM. Примерно в это же время американцы придумали свою спецификацию SMPTE-C [6] на аналогичные люминофоры, но NTSC перешла на них только в 1987 году. Я не знаю наверняка, но подозреваю, что именно этой задержкой объясняется сам факт рождения пресловутых Rec.601 — ведь по большому счету, они морально устарели уже к моменту своего появления.

Потом в 1990 году случились новые рекомендации ITU-R BT.709 [7], а в 1996 на их основе придумали стандарт sRGB, который захватил мир и царствует (в потребительском секторе) по сей день. Альтернативы ему существуют, но все они востребованы в узкоспецифичных областях. И прошло уже, ни много ни мало, 20 лет — не пора бы уже избавиться от атавизмов окончательно?

Так в чем же конкретно проблема?

Кто-то может подумать, что те коэффициенты отражают некие фундаментальные свойства человеческого зрения и потому не имеют срока давности. Это не совсем верно — помимо всего прочего, коэффициенты привязаны к технологии воспроизведения цвета.

Любое RGB-пространство (а YIQ это преобразование над моделью RGB) определяется тремя базовыми параметрами:

1. Хроматическими координатами трех основных цветов (они называются primaries);
2. Хроматическими координатами белой точки (white point или reference white);
3. Гамма-коррекцией.

Хроматические координаты принято задавать в системе CIE xyY [8]. Регистр букв в данном случае важен: cтрочные xy соответствуют координатам на хроматической диаграмме (всем известная «подкова»), а заглавный Y — это яркость из вектора CIE XYZ.

Теперь посмотрим на компоненту Y у всех первичных цветов NTSC (я пометил их розовым):

Об относительной яркости, или насколько живучим бывает легаси - 2
* Оригинал таблицы со многими другими пространствами на сайте Брюса Линдблума [9].

Знакомая цифирь, правда? Вот и ответ на вопрос «откуда взялось?»

А проблема в том, что используемое сегодня пространство sRGB существенно отличается от системы 60-летней давности. И дело даже не в том, что из них лучше или хуже — они просто разные:

Об относительной яркости, или насколько живучим бывает легаси - 3

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

Вы можете спросить: а зачем древнему NTSC охват (практически совпадает с охватом Adobe RGB 1998!) настолько больше, чем у современного sRGB? Я не знаю. Совершенно очевидно, что кинескопы того времени покрыть его не могли. Быть может, хотели сделать задел на будущее?

Как правильно?

Относительные яркости первичных цветов в пространстве sRGB приведены в таблице выше (помечены зеленым) — их и нужно использовать. На практике обычно делают округление до 4-х знаков:

Y=0.2126 R + 0.7152 G + 0.0722 B

Внимательный читатель заметит, что коэффициент при R округлен не по правилам (в меньшую сторону), но это не ошибка. Дело в том, что сумма всех трех чисел должна равняться единице, и «правильное» округление внесло бы погрешность. Педанты могут взять все шесть знаков после запятой и не беспокоиться.

Этой формулы хватит для 99% обычных случаев. Её использует во всех своих спецификациях W3C (например, матричные фильтры в SVG [10]). Если вам нужна бОльшая точность, придется вычислять L* [11], но это отдельная большая тема. Неплохой ответ на StackOverflow [12], который дает отправные точки для дальнейшего чтения.

Почему меня это волнует?

Как уже было сказано выше, за многие годы формула растиражирована на несметном количестве сайтов, и они сидят в топе всех поисковиков (например [13]). Источники посерьезнее часто приводят обе формулы, но не делают между ними должного различия, преподнося их как равноправные альтернативы. Характерный пример на StackОverflow: Formula to determine brightness of RGB color [14] — ответы довольно подробные, но человеку не в теме сложно сделать осознанный выбор.

Справедливости ради, серьезные проекты такими ошибками почти не страдают — авторы не брезгуют сверяться со стандартами, да и фидбек аудитории работает (хотя и там не обходится без исключений). А вот рядовой программист, которому нужно побыстрее решить задачу, вбивает в поисковик что-то типа «rgb to grayscale», и тот подсовывает ему сами знаете что. Формулу продолжают находить и копипастить до сих пор! Феноменальная живучесть.

На розыск этих примеров я потратил около 20 минут:

Обратите внимание, что наряду со старенькими проектами в списке немало упоминаний самых свежих и модных технологий, то есть код писался/копипастился совсем недавно.

А поводом для написания этой заметки стал слайд из доклада Василики Климовой [31] с HolyJS-2016 — с той же самой доисторической формулой. Понятно, что формула не повлияла на основной смысл выступления, но наглядно продемонстрировала ваши шансы ненароком её нагуглить в 2016 году.

Подытоживая: если увидите в чьем-то действующем коде последовательность 299/587/114 — кидайте автору ссылку на эту заметку.

Автор: dom1n1k

Источник [32]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/obrabotka-izobrazhenij/145615

Ссылки в тексте:

[1] ITU: https://en.wikipedia.org/wiki/International_Telecommunication_Union

[2] ITU-R BT.601: https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.601-7-201103-I!!PDF-E.pdf

[3] ITU-R BT.470: https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.470-6-199811-S!!PDF-E.pdf

[4] YIQ: https://en.wikipedia.org/wiki/YIQ

[5] NTSC: https://en.wikipedia.org/wiki/NTSC#Colorimetry

[6] SMPTE-C: https://en.wikipedia.org/wiki/NTSC#SMPTE_C

[7] ITU-R BT.709: http://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.709-6-201506-I!!PDF-E.pdf

[8] CIE xyY: https://en.wikipedia.org/wiki/CIE_1931_color_space#CIE_xy_chromaticity_diagram_and_the_CIE_xyY_color_space

[9] сайте Брюса Линдблума: http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html

[10] матричные фильтры в SVG: https://www.w3.org/TR/SVG/single-page.html#filters-feColorMatrixElement

[11] L*: https://en.wikipedia.org/wiki/Lab_color_space#CIELAB

[12] Неплохой ответ на StackOverflow: http://stackoverflow.com/a/689547

[13] например: http://www.gamedev.ru/code/forum/?id=79884

[14] Formula to determine brightness of RGB color: http://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color

[15] документация Microsoft: https://msdn.microsoft.com/en-us/library/bb332387.aspx#code-snippet-7

[16] документация Matlab: http://www.mathworks.com/help/matlab/ref/rgb2gray.html#moreabout

[17] стандартная библиотека Go: https://github.com/golang/go/blob/master/src/image/color/color.go#L249

[18] модные колор-пикеры для React-а: https://github.com/casesandberg/react-color/blob/master/modules/tinycolor2/index.js#L76

[19] Chart.js: https://github.com/chartjs/Chart.js/blob/master/dist/Chart.js#L450

[20] библиотека CImg для обработки изображений: https://github.com/dtschump/CImg/blob/master/examples/radon_transform2d.cpp#L190

[21] какие-то: https://github.com/Itseez/opencv/blob/master/modules/imgcodecs/src/utils.cpp#L46

[22] модули: https://github.com/Itseez/opencv/blob/master/modules/imgproc/perf/perf_floodfill.cpp#L58

[23] коллекция демок на WebGL: https://github.com/fluuuid/labs/blob/master/common/shaders/fragment-shaders/halftone2-fs.glsl#L34

[24] что-то: https://github.com/GNOME/gimp/blob/master/plug-ins/common/sample-colorize.c#L46

[25] внутри: https://github.com/GNOME/gimp/blob/master/plug-ins/imagemap/imap_preview.c#L168

[26] утилитка: https://github.com/inkscape/inkscape/blob/master/inkscape-launchpad/share/extensions/color_grayscale.py#L9

[27] Swift: https://github.com/T-Rex/lab-swift-programming-ios-image-processor/blob/master/Filterer/GrayscaleFilter.swift#L17

[28] Go: https://github.com/justinruggles/image2/blob/master/color2/gray.go#L63

[29] Three.js: https://github.com/Jeremboo/brunch-threejs-es6/blob/master/app/assets/vendors/wagner/fragment-shaders/grayscale-fs.glsl#L7

[30] JS: https://github.com/ramitos/luminosity/blob/master/src/luminosity.js#L25

[31] слайд из доклада Василики Климовой: http://vasilika.ru/webgl-texture/?full#39

[32] Источник: https://habrahabr.ru/post/304210/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best