Protraktor

Как выглядит относительная яркость цветов

Немного про восприятие и игры с цветами на C++

Изначально эта статья была опубликована в моем личном блоге

Изучая некоторые моменты раскрашивания данных на картах (читай метеопрогнозы), а также вопросы поддержки необходимого цветового контраста в рабочей дизайн-системе, я задумался о том, как же выглядит кривая относительной яркости цветов — ведь все мы знаем, что тот же красный выглядит светлее синего, но если усреднить значения RGB «втупую» (то есть превратить в оттенки серого), мы получим эквивалентные значения для #FF0000 и для #0000FF.

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

Но ведь с этим надо как-то уметь жить, чтобы посчитать воспринимаемый контраст между парой цветов?

Увы, методы есть, но все они тоже достаточно условные. Ковыряясь в том, как оценивается и считается цветовой контраст по рекомендациям WCAG, я таки нашел формулы для расчёта относительной яркости цвета, приведенные к диапазону от 0 до 1, где 0 — нулевая яркость черного, а 1 — самый яркий белый.

Загнав формулу в Qt, получил картинку, для максимально кислотных цветов (заданных в HSL, где насыщенность на максимуме, а яркость посередине):

Кривая относительной яркости для HSL lightness = 50%

Формула, кстати, для цветового пространства sRGB, хотя сейчас становятся популярными и другие, то же P3. Но не думаю что разница сильно отличается.

В принципе, для оценки использовать можно (и нужно). Если повышать яркость, график поднимается вверх, если снижать — вниз.

Три кривых для HSL lightness = 80%, 50% и 25% при максимальной насыщенности

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

Зачем надо?

Но где это великое знание может пригодиться кроме понимания работы оценки цветового контраста?

Ну, во-первых, это явно доказывает пользу тёмных («ночных») палитр в системах, где критичны индикации тревог, ведь мы обычно хорошо обращаем внимание на яркие объекты в темноте, в силу высокого контраста. И если красные тревоги еще будут видны на белом фоне в периферии, то привлечь внимание к желтым сообщениям почти нереально, учитывая что они отличаются от белого на жалкие 7%. Обводить их темными контурами — убивает смысл в желтых индикациях, снижать насыщенность и яркость — опять же будет грязь вместо желтого, то есть потеря знакомой семантики, ну а делать большие площади заливки не всегда возможно.

Во-вторых, если мы хотим играть только с тоном, заливая карту равномерными цветами между изолиниями (чтобы другие слои поверх оставались читаемыми, то есть контрастными), нам нужно для каждого оттенка избирательно повышать или снижать яркость и, вероятно, насыщенность. То есть тот же синий выкручивать вверх, чтобы он так же взаимодействовал с картографическими объектами, как и более теплые оттенки желтого и зеленого.

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

Относительная яркость примерно в 70%

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

Но обратите внимание, как тут еще и меняется восприятие технически одинаковой насыщенности — везде она равна 50%. То есть чтобы получить с помощью компьютера (а не глаза) действительно похожие цвета одной яркости всех оттенков, нужно изобрести еще и кривую относительной насыщенности. Но и разница между оттенками чересчур снизится, чтобы кодировать цвета.