- PVSM.RU - https://www.pvsm.ru -
Я уверен, что многим программистам знакома формула:
А уж тот, кто плотно работал с графикой, знает эти цифры буквально наизусть — как в былые времена эникейщики запоминали серийники 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 (я пометил их розовым):
* Оригинал таблицы со многими другими пространствами на сайте Брюса Линдблума [9].
Знакомая цифирь, правда? Вот и ответ на вопрос «откуда взялось?»
А проблема в том, что используемое сегодня пространство sRGB существенно отличается от системы 60-летней давности. И дело даже не в том, что из них лучше или хуже — они просто разные:
Треугольник шире и смещён в сторону. Другая белая точка. К слову сказать, иллюминант C уже очень давно признан deprecated в пользу иллюминантов серии D вообще и наиболее популярного D65 в частности. Тело цветового охвата другое — соответственно, результаты вычислений яркости окажутся неадекватны реальности.
Вы можете спросить: а зачем древнему NTSC охват (практически совпадает с охватом Adobe RGB 1998!) настолько больше, чем у современного sRGB? Я не знаю. Совершенно очевидно, что кинескопы того времени покрыть его не могли. Быть может, хотели сделать задел на будущее?
Относительные яркости первичных цветов в пространстве sRGB приведены в таблице выше (помечены зеленым) — их и нужно использовать. На практике обычно делают округление до 4-х знаков:
Внимательный читатель заметит, что коэффициент при 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
Нажмите здесь для печати.