sRGB и спектрумовский пиксель арт

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





Кратко о цветовых пространствах


Цвет пикселя можно закодировать в числовое значение множеством разных способов, и для удобства понимания используется термин цветового пространства. Это — такой способ кодировки цвета, при котором его яркость, насыщенность и окрашенность зависят от его цветовых компонент («координат»).
Чаще всего мы встречаемся в описаниях работы с цветом с простым 24 бит линейным RGB, где конкретный пиксель описывается тремя байтами:
1. R — красная составляющая, один байт, значения от 0 до 255
2. G — зеленая составляющая, один байт, значения от 0 до 255
3. B — синяя составляющая, один байт, значения от 0 до 255
Соответственно, 0 — это минимум, а 255 — максимум. То есть, фактически, если принять R, G и B компоненты за координаты в трехмерном пространстве, то каждому возможному цвету соответствует такой вот набор «координат».

sRGB


Всё это просто и всем более-менее прекрасно известно, а вот дальше будет чуть любопытнее, что лично для меня было немалым открытием. Дело в том, что, при всей математической простоте линейного RGB пространства, оно не очень хорошо подходит для биологических аспектов нашего зрения. В линейном RGB значения яркости цветовых каналов распределяются линейно:
0 — 0% яркости
128 — 50% яркости
255 — 100% яркости

Беда в том, что наш глаз воспринимает свет нелинейно и воспринимает разное количество градаций яркости в зависимости от освещенности. Как вы сами понимаете, использовать линейную яркость для наших нелинейных глаз — это неэффективно, поэтому в широкое обращение на правах стандарта было внесено более сложное цветовое пространство — sRGB. Его различие в том, что оно линейно только в более темной части спектра, а светлая часть скорректирована нелинейной функцией. Кому интересно более конкретно — пару ссылок в конце заметки. Что это даёт нам? Это, по факту, делает нашу жизнь, как разработчиков, намного более увлекательной. Ведь теперь:
0 — 0% яркости
187 — 50% яркости
255 — 100% яркости

Видите, да? Мы ведь все привыкли к простой логике: хочешь получить серый цвет, где 50% освещенности, просто возьми максимум (255) и подели на два. Получишь 128, которое и будет соответствовать половинной яркости. Так вот, проблема в том, что на современном оборудовании это далеко от истины. Современное оборудование ожидает, что данные о пикселях будут поступать нелинейные, а дальнейшую коррекцию оборудование сделает само. Самый наглядный пример и доказательство, что на современном оборудовании 50% от 255 реально не соответствует половине яркости (картинка с офигенной статьи, ссылка в конце заметки):



Еще раз — это касается более-менее всех мониторов, всех видеокарт, всех современных ОС. Кроме того, более-менее любая картинка, которую вы скачаете с интернетиков, уже тоже имеет скорректированную гамму с учетом ожиданий оборудования.

Как это касается нас?


Нас этот эффект касается напрямую, так как мы работаем с пиксельной графикой и должны учитывать эти эффекты при простых операциях, дабы сохранять точность результата.
Первое, что нам нужно понять: практически никто и нигде не учитывает, что 256/2 != 50% яркости. То есть, интерполяция в браузерах, CSS эффектах, редакторах графики не учитывает нелинейность. Не потому, что дураки, а потому, что очень накладно.
Возьмем для иллюстрации работу Paracels-а Whirlpool, отображенную в палитре Pulsar:


Обратите внимание на нос, здесь используется спорный, но нередкий приём — переход от темно-зеленому к светло-зеленому через шахматную текстуру.

Попробуем увеличить картинку средствами браузера на 50% (Firefox 57.0.3, Chrome 63.0.3239.84, IE11):


Переход стал заметнее? Стал, да. А всё потому, что при ресэмплинге картинки браузер применил функции, рассчитанные на линейное пространство, к пикселям в нелинейном пространстве.

Доказать? Легко. Берем фотошоп, открываем картинку в фотошопе, конвертируем цветовой профиль в Custom RGB с гаммой 1.0, ресайзим, конвертируем обратно в sRGB. Получаем:


Наглядная разница, да? Еще раз, как в современнном мире надо работать с графикой:
1. Переводим цифровые значения из нелинейных в линейные.
2. Применяем математические преобразования.
3. Перед показом переводим из линейных значений в нелинейные под наше оборудование.

А теперь давайте осознаем всю величину проблемы:
1. Эмуляторы и «растягивание» картинки на весь экран с применением интерполяции (не nearest neighbour).
2. Показ графики на сайтах, изготовление 2x и уменьшенных копий скриншотов
3. Проекторы, показ работ на пати?

Бонус для PHP-кодеров

В библиотеке GD2 (я уж молчу про image magick) есть готовые средства, просто о них мало кто знает.

Было:

$dstImage = imagecreatetruecolor($dstWidth, $dstHeight);
imagecopyresampled($dstImage, $srcImage, 0, 0, 0, 0, $dstWidth, $dstHeight, $srcWidth, $srcHeight);


Стало:

imagegammacorrect($srcImage, 2.2, 1.0);

$dstImage = imagecreatetruecolor($dstWidth, $dstHeight);
imagecopyresampled($dstImage, $srcImage, 0, 0, 0, 0, $dstWidth, $dstHeight, $srcWidth, $srcHeight);

imagegammacorrect($dstImage, 1.0, 2.2);


Ссылки
Цветовое пространство на Wiki
sRGB на Wiki
Подробная статья по проблеме (англ)
sRGB в геймдев (англ)
Еще одна подробная статья (англ)

10 комментариев

avatar
Я думаю, что тут как бы слегка неточное вышло у тебя описание, потому что по сути главное, что мало кто (почти никто) не учитывает при работе со спектрумовским экраном — это гамма экрана. sRGB — это просто стандартизированный способ коррекции гаммы. Я примерно так же угробил немало времени пару лет назад, пытаясь понять как правильно смешивать цвета в спектрумовских текстурах.

Но, в любом случае, очень хорошо, что этот разговор начался.
avatar
sRGB — всё же цветовое пространство прежде всего, но учитывать нам надо гамму, это верно. И вопрос не только в работе со спектрумовским экраном, мало кто вообще учитывает цветовое пространство перед применением преобразований. Те же браузеры для нас сейчас не менее актуальны, чем эмуляторы. Canvas, видео на ютубе — каждый случай надо отдельно смотреть и, по возможности, править и компенсировать.
avatar
В телефоне первый пример с яркостями выглядит как A=B, С — светлее. На ноуте — наоборот: B темнее A, A почти равно C.

Второй пример в телефоне — первые 2 скриншота whirlpool сильно клэшат, третий — с плавным переходом. На ноутбуке с клэшингом только whirlpool 2. 1 и 3 — с плавным переходом.
avatar
Все логично. Первый пример 256 пикселей шириной, но телефонные браузеры оперируют не пикселями, а поинтами, то есть происходит масштабирование с ресемплингом, где опять же гамма не учитывается. То есть, на телефоне принципиально можно отобразить правильно, но не в адаптивном дизайне.
avatar
И еще, клэшинг — только часть проблемы. Меняются цвета, из-за артефактов, например, желтый дитеринг на голубом фоне приобретает темную окантовку.
avatar
avatar
Подтверждаю: на телефоне А почти равно В (В слегка темнее), на компе В очень сильно темнее А, а А=С в точности. Как теперь с этим жить?.. =)

Прошу меня простить за занудство. Английское слово «accurate» переводится на русский, как «точный», а русское слово «аккуратный» может означать лишь человека, который подключает свою пентеву, не размахивая высоковольтным кабелем в опасной близости от микросхем, например. =))
  • tsl
  • +2
avatar
Телефон физически показывает не 256 пикселей картинку, а слегка перемасштабирует её по-своему усмотрению, это нормально. Можно и точно 256 заставить отображать, но тогда мобильный сайт будет кривым, скорее всего.

Про «аккуратность» — да, есть такая болезнь, мне надо больше читать литературы, а не треша всякого :)
avatar
не знаю как в других браузерах, но в 12-й Opera Mobile картинка отображается 1 в 1, яркость у A и C визуально одинакова, B темнее. Если масштабировать страницу, то да, A приближается к B.

кстати! какой из не-хромовых браузеров под андроид имеет смысл поставить? Сейчас пользуюсь хромом для некоторых сайтов и оперой для остальных (особенно для всяких deviantart и прочих, как раз за счет того, что масштабирование идет пиксел в пиксел), но с https у оперы все очень печально, а более поздние версии иначе как огрызками не назвать :(
avatar
Лекарство для браузеров. Пока что поддерживается очень криво, но вот такой вариант уже сейчас отключит фильтрацию при ресэмплинге в хроме и файрфоксе. Соответственно, и проблема гаммы отпадёт.
.myimg {
   image-rendering: -moz-crisp-edges;
   image-rendering: pixelated;
}
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.