Почему при переводе в серый зелёный канал весит больше синего

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

Почему при переводе в серый зелёный канал весит больше синего

Дальше вы проверите формулу на конкретном HEX-коде, поймёте разницу между яркостью, HSL Lightness и готовым серым пикселем, а ещё перестанете выбирать «правильные» коэффициенты по одному скриншоту из интернета.

В статье вы узнаете:

Простое среднее делает три разных цвета одинаково серыми

Возьмём три чистых цвета sRGB: красный rgb(255, 0, 0), зелёный rgb(0, 255, 0) и синий rgb(0, 0, 255). Среднее каналов у каждого равно 85:

(255 + 0 + 0) / 3 = 85

Такой алгоритм превратит все три цвета в один серый rgb(85, 85, 85), или #555555. Арифметика безупречна, результат для изображения слабый. Чистый зелёный на обычном экране даёт намного больший яркостный сигнал, чем чистый синий, но среднее этого не замечает.

Похожая ловушка есть в HSL. Чистые красный, зелёный и синий получают Lightness 50%, потому что HSL берёт середину между максимальным и минимальным RGB-каналами. Это удобная геометрия цветового цилиндра, а не измерение воспринимаемой яркости. В конвертере HEX, RGB и HSL можно по очереди ввести #FF0000, #00FF00 и #0000FF: значение L останется 50%, хотя цвета не выглядят одинаково светлыми.

Преобразование в серый сжимает три числа в одно. При таком сжатии часть информации неизбежно исчезает. Алгоритм может сохранить среднее кодов, телевизионный сигнал яркости, относительную яркость для контраста или художественное разделение объектов. Это разные цели, поэтому универсальной вечной кнопки «убрать цвет правильно» не существует.

Коэффициенты задаёт не радуга вообще, а конкретное пространство sRGB

Фотометрия измеряет свет с учётом того, как стандартный человеческий наблюдатель реагирует на разные длины волн при заданных условиях. Международная комиссия по освещению CIE фиксирует функции спектральной световой эффективности. Они нужны потому, что одинаковая физическая мощность на разных участках спектра создаёт разный световой отклик [CIE, ISO/CIE 23539:2023].

sRGB, в свою очередь, задаёт координаты красного, зелёного и синего основных цветов и белую точку D65. Линейные значения этих трёх каналов переводят в CIE XYZ матрицей. Вторая строка матрицы вычисляет компонент Y:

Y = 0,2126R + 0,7152G + 0,0722B

Коэффициенты складываются в единицу. Поэтому белый с тремя полными линейными каналами получает Y = 1, а чёрный с тремя нулями Y = 0. У чистого зелёного Y = 0,7152, у чистого красного 0,2126, у чистого синего 0,0722. Для одинаковых линейных уровней вес зелёного почти в 9,9 раза больше веса синего.

Объяснение «глаз просто в десять раз чувствительнее к зелёному» слишком грубое. Коэффициент относится не к любому зелёному фотону и не к числу колбочек в сетчатке. Он получается из характеристик выбранных основных цветов sRGB, белой точки и стандартизованной фотометрической функции. В другом RGB-пространстве основные цвета имеют другие координаты, поэтому изменится и строка Y.

Даже само слово «яркость» требует аккуратности. Luminance, по-русски яркость в фотометрическом смысле, является измеряемой величиной. Brightness описывает зрительное ощущение и зависит ещё от цвета, окружения, размера поля и адаптации. CIE отдельно предупреждает: два источника разных цветов с одинаковой измеренной яркостью не обязательно кажутся одинаково яркими. Значит, Y является хорошей стандартизованной опорой, но не читает мысли каждого глаза в каждой комнате.

Перед взвешиванием нужно снять гамма-кодирование sRGB

Байт 128 в sRGB не означает половину световой мощности канала 255. Кодированная шкала нелинейна: больше уровней достаётся тёмной области, где небольшие различия особенно заметны. Это помогает хранить изображение, но мешает физическому сложению. Та же ловушка возникает при цифровом смешении; разницу между сложением света и смешением красок полезно разобрать отдельно.

Сначала байт канала переводят в диапазон от 0 до 1:

c = channel / 255

Затем получают линейный световой компонент C:

C = c / 12,92, если c ≤ 0,04045

C = ((c + 0,055) / 1,055)^2,4, если c > 0,04045

После этой операции линейные R, G и B можно подставлять в формулу Y. Именно такой порядок использует определение относительной яркости sRGB в WCAG 2.2 [W3C, относительная яркость].

Проверим стартовый цвет конвертера #336699. Его каналы равны 51, 102 и 153, то есть после деления на 255 получаются 0,2, 0,4 и 0,6. Линейные значения примерно равны:

  • R = 0,03310
  • G = 0,13287
  • B = 0,31855

Теперь считаем вклады:

0,2126 × 0,03310 = 0,00704

0,7152 × 0,13287 = 0,09503

0,0722 × 0,31855 = 0,02300

Сумма даёт относительную яркость около 0,12506. Синий исходный байт был самым большим, но зелёный вклад всё равно оказался примерно в четыре раза крупнее синего. Коэффициенты и нелинейность шкалы работают одновременно.

Отдельные умножения удобно проверить через калькулятор процентов: 21,26% от линейного R, 71,52% от линейного G и 7,22% от линейного B. Вводить туда исходные 51, 102 и 153 для расчёта WCAG нельзя, сначала нужна линейзация.

Относительная яркость ещё не является кодом серого пикселя

После расчёта для #336699 у нас получилось Y = 0,12506. Если просто умножить это число на 255, выйдет около 32. Но нейтральный rgb(32, 32, 32) имеет намного меньшую линейную яркость, потому что его каналы снова будут интерпретированы как гамма-кодированные.

Чтобы получить нейтральный серый с той же относительной яркостью, Y нужно кодировать обратно в sRGB:

c = 12,92Y, если Y ≤ 0,0031308

c = 1,055 × Y^(1 / 2,4) - 0,055, если Y > 0,0031308

Для Y = 0,12506 результат равен примерно 0,3887. Умножаем на 255 и получаем 99, то есть серый около #636363.

Здесь легко увидеть три самостоятельных результата для одного цвета:

HSL Lightness: 40%. Она описывает положение #336699 в модели HSL.

Относительная яркость WCAG: примерно 0,125. Она нужна для расчёта контраста.

Нейтральный sRGB-серый той же относительной яркости: примерно #636363. Он получается после обратного кодирования.

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

CSS-фильтр даёт ещё один серый, и это предусмотрено стандартом

Функция CSS filter: grayscale(100%) использует матрицу с коэффициентами 0,2126, 0,7152 и 0,0722 прямо в пространстве sRGB. Для #336699 она получает:

0,2126 × 51 + 0,7152 × 102 + 0,0722 × 153 ≈ 94,84

После округления все три канала становятся примерно 95, то есть #5F5F5F. Это близко к #636363, но метод другой. На насыщенных основных цветах расхождение гораздо заметнее.

Для чистого красного CSS-матрица даёт около 54, для зелёного 182, для синего 18. Если же искать нейтральные sRGB-серые с той же относительной яркостью после полной линейной процедуры, получатся примерно 127, 220 и 76.

Один вариант не разоблачает другой как ошибку. CSS-фильтр определяет конкретный визуальный эффект над кодированными пикселями. WCAG определяет относительную яркость для контрастных отношений. Назначение формулы важнее того, насколько знакомо выглядят её коэффициенты.

Это утверждение относится именно к CSS-функции grayscale(). У SVG filter primitives цветовое пространство задаётся отдельно через color-interpolation-filters, поэтому переносить результат без проверки на любой SVG-фильтр нельзя.

Формула 0,299, 0,587 и 0,114 пришла из другой системы

В учебниках и старом коде часто встречается:

Y′ = 0,299R′ + 0,587G′ + 0,114B′

Штрихи здесь важны. Они обозначают нелинейно предобработанные сигналы. Коэффициенты относятся к рекомендации ITU-R BT.601 для телевидения стандартной чёткости и её основным цветам. Это luma, кодированный сигнал яркости, а не линейная физическая luminance.

Для HDTV в BT.709 используются коэффициенты 0,2126, 0,7152 и 0,0722, но там они тоже встречаются в формуле сигнала Y′ над предобработанными компонентами. WCAG применяет ту же тройку к уже линейным sRGB-компонентам и называет результат relative luminance. CSS grayscale использует тройку в матрице над sRGB. Три одинаковых числа не делают три процедуры одинаковыми.

Поэтому при встрече формулы сначала спросите:

  1. В каком цветовом пространстве находятся значения?
  2. Каналы линейные или гамма-кодированные?
  3. Нужен контраст, видеосигнал, готовый серый пиксель или художественный чёрно-белый вариант?
  4. Как результат кодируется и округляется обратно?

Без этих ответов спор между 0,299 и 0,2126 похож на спор между километрами и милями без упоминания единиц. Обе записи могут быть верны в своём контракте.

Художественный перевод в чёрно-белое не обязан сохранять Y

Фотографу часто важнее разделить объекты, чем сохранить стандартизованную яркость каждого пикселя. Красная куртка и зелёная стена могут иметь близкий тон после автоматического преобразования и слиться. Тогда редактор поднимает вклад красного канала или опускает зелёный, как если бы перед объективом чёрно-белой плёнки появился цветной фильтр.

Это не ошибка против физики, а осознанная тональная интерпретация. Она может сделать кожу светлее, небо темнее, листву контрастнее. Цена решения тоже понятна: итоговый серый уже не обещает сохранить исходную относительную яркость цветов.

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

Формулу выбирают по задаче, а не по размеру коэффициента

Для контраста текста в обычном веб-интерфейсе используйте определение WCAG для sRGB: нормализация, линейзация, взвешенная сумма, затем контрастное отношение. HSL Lightness и простое среднее RGB для этой задачи не подходят.

Для CSS-эффекта ожидайте результат, который задаёт спецификация grayscale(). Не подменяйте им аудит доступности: серый вид картинки и контраст текста являются разными проверками.

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

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

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

Зелёный коэффициент самый большой не потому, что математика любит зелёный. Так стандарт sRGB связывает свои основные цвета с фотометрическим Y. Сначала назовите пространство и задачу, затем снимите гамма-кодирование, если этого требует формула. И только потом умножайте, иначе аккуратные четыре знака после запятой просто очень точно посчитают не то.

Есть что добавить?

Напишите своё мнение, комментарий или предложение.