sRGB и спектрумовский пиксель арт
Хочу обратить ваше внимание на небольшой нюанс, актуальный при работе со спектрумовской графикой, и который лично мне ранее был неизвестен. Возможно, людям с академическим образованием рассматриваемый факт давно известен и по сути своей банален, но самоучкам вроде меня наверняка будет интересно.
Сразу предупрежу, я не владею этой темой на академическом уровне, и детали могут быть неточными, но самое важное передать суть явления и его влияние на нашу жизнь.
Цвет пикселя можно закодировать в числовое значение множеством разных способов, и для удобства понимания используется термин цветового пространства. Это — такой способ кодировки цвета, при котором его яркость, насыщенность и окрашенность зависят от его цветовых компонент («координат»).
Чаще всего мы встречаемся в описаниях работы с цветом с простым 24 бит линейным RGB, где конкретный пиксель описывается тремя байтами:
1. R — красная составляющая, один байт, значения от 0 до 255
2. G — зеленая составляющая, один байт, значения от 0 до 255
3. B — синяя составляющая, один байт, значения от 0 до 255
Соответственно, 0 — это минимум, а 255 — максимум. То есть, фактически, если принять R, G и B компоненты за координаты в трехмерном пространстве, то каждому возможному цвету соответствует такой вот набор «координат».
Всё это просто и всем более-менее прекрасно известно, а вот дальше будет чуть любопытнее, что лично для меня было немалым открытием. Дело в том, что, при всей математической простоте линейного 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. Проекторы, показ работ на пати?
Было:
Стало:
sRGB на Wiki
Подробная статья по проблеме (англ)
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);
Ссылки
Цветовое пространство на WikisRGB на Wiki
Подробная статья по проблеме (англ)
sRGB в геймдев (англ)
Еще одна подробная статья (англ)
10 комментариев
Но, в любом случае, очень хорошо, что этот разговор начался.
Второй пример в телефоне — первые 2 скриншота whirlpool сильно клэшат, третий — с плавным переходом. На ноутбуке с клэшингом только whirlpool 2. 1 и 3 — с плавным переходом.
Прошу меня простить за занудство. Английское слово «accurate» переводится на русский, как «точный», а русское слово «аккуратный» может означать лишь человека, который подключает свою пентеву, не размахивая высоковольтным кабелем в опасной близости от микросхем, например. =))
Про «аккуратность» — да, есть такая болезнь, мне надо больше читать литературы, а не треша всякого :)
кстати! какой из не-хромовых браузеров под андроид имеет смысл поставить? Сейчас пользуюсь хромом для некоторых сайтов и оперой для остальных (особенно для всяких deviantart и прочих, как раз за счет того, что масштабирование идет пиксел в пиксел), но с https у оперы все очень печально, а более поздние версии иначе как огрызками не назвать :(