Блок 1: не то, чем кажется

Начнем немного издалека: так уж сложилось, что наш глаз видит яркости окружающего мира в нелинейной кривой. Там, где в реальном мире яркость одного объекта может быть больше другого в 2 раза, для нашего глаза разница в яркости может показаться иной. Для наглядной проверки достаточно взглянуть на обычный линейный градиент:

Для подтверждения, вот так выглядит вейвформа изображения:

Абсолютно линейный градиент от 0 до 100 нит.

В каком месте вы лучше видите изменение яркости? В левой или правой части? Для упрощения задачи давайте разобьем градиент на несколько сегментов с одним оттенком у каждого сегмента

И взглянем на его левую:

И правую части:

Думаю большинство ответило, что в левой части изменение оттенков более заметно, чем справа. Это подводит нас к мысли, что абсолютно линейной изменение яркости наш глаз видит нелинейно: изменение яркости в темных оттенках заметно лучше, чем в светлых. Если мы попробуем выразить эту зависимость изменения яркости для глаза (ось Y) от действительных значений (ось X), то на выходе получим примерно вот такую кривую:

Поздравляю, мы только что простым экспериментов вывили примерную яркостную кривую, в которой наше зрение воспринимает окружающий мир. Если мы теперь целенаправленно поищем в интернете, в какой наш глаз видит кривой, то найдем похожие изображения, которые подтвердят наш эксперимент:

Эта кривая близка к функции гаммы 2.2:

y = x^(1/«gamma value»)

Да да, а вы что думали, матан в жизни не пригодится?

Поэтому там, где программа показывает 0.218, нам кажется, что там четкие 0.5.

При отображении линейной зависимости для нашего глаза в тенях на 1 стоп экспозиции будет приходиться буквально несколько единиц изменения значений rgb. Тогда как в светлых участках на 1 стоп приходится 128 единиц rgb -> нерациональное использование памяти

Выводы на данный момент:

  • Наш глаз видит мир нелинейно

  • В тенях мы видим изменение яркости лучше, чем в светлых участках

Блок 2: И овцы целы, и волки сыты

Имея вышеперечисленные знания можно предположить: а ведь не самой лучшей идеей будет записывать цифровые изображения в линейную кривую. Ведь для того, чтобы сохранить достаточно деталей в темных участках, нам придется применять бОльшую битность, тратить большое памяти, так еще и ко всему этому записывать бесполезные для нашего глаза детали в ярких участках. И тут на помощь приходит кодирование в некую яркостную кривую. Почему в «некую»? А потому что хрен их там разберет, какая камера в какую конкретно пишет, и вообще все это настолько часто менялось, что там уже не чистая гамма кривая, а некая кусочно-заданная функция. Но для упрощения понимания, давайте будем называть ее «гамма 2.2». И вот как раз эта гама кривая позволяет эффективно использовать возможности камеры и с меньшей битностью записывать изображения, сохраняя бОльшее количество деталей в темных участках, и меньшее в светлых. Эта коррекция называется OETF

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

y = x^«gamma value»

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

Сделаем выводы:

  • записывая изображение с применением гамма-кривой, мы можем эффективно сохранять детали для нашего глаза

  • для корректного отображения таких файлов в устройстве просмотра применяется обратная гамма-функция

Блок 3: про работу в линейном пространстве

Ладно, с энкодингом и декодингом разобрались, но зачем нам надо работать в линейной кривой?

И не опять, а снова, будем все познавать на примерах. Допустим, на входе имеем солид со значениями по всем 3-м каналам в 0.2.

Если мы работаем в нелинейной кривой, то при увеличении значения в 2 раза мы получим… значение примерно 0,93.

Согласитесь, не совсем то, что нам нужно было.

Теперь же умножим на два исходник, работая в линейной кривой:

Теперь мы получили желаемое значение в 0,4.

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

Обобщаем

  1. Камера видит мир в линейной зависимости, наш глаз - в нелинейной

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

  3. При простом просмотре на мониторе применяем обратную гамма-коррекцию, возвращающую яркостную зависимость к линейному виду. При обработке переводим изображение в линейное пространство, корректируем, после возвращаем исходную гамму-кривую.

Причина применения гамма-коррекции:

  • Эффективное хранение данных

Причина работы в линейном пространстве:

  • Корректная обработка с использованием математических операций

Источники:

https://www.gdcvault.com/play/1012351/Uncharted-2-HDR

https://chrisbrejon.com/cg-cinematography/introduction/

https://www.youtube.com/watch?v=zBawwb6L9bo

https://www.youtube.com/watch?v=WJzmcJQFlao

https://www.youtube.com/watch?v=vRG6o-UII8k&t=547s

https://color.viewsonic.com/explore/content/Accurate-Gamma_4.html

https://wiki2.org/ru/Гамма-коррекция

https://habr.com/ru/post/353632/

https://en.wikipedia.org/wiki/Gamma_correction#Explanation

https://www.cambridgeincolour.com/ru/tutorials-ru/gamma-correction.htm

Комментарии (4)


  1. iShrimp
    24.08.2023 15:00
    +3

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

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

    Проблемы с гаммой наиболее заметны на тонких светлых линиях на тёмном фоне.


  1. Tolkik
    24.08.2023 15:00
    +1

    Брехня. Даже не стал читать статью, проверил в фотошопе первую картинку - это никакой не линейный градиент. Картинка 1000 пикселей ширины, значит каждые 10 пикселей значение "черноты" должно меняться на 1%. По факту слева первый пиксел 100% черноты десятый пиксел уже 93% черноты, хотя должен быть 99%. А справа наоборот: крайний правый пиксел 0% черноты, а через 100 пикселей 6%, хотя должно быть 10%. Картинка явно засвечена.
    Вот настоящий линейный градиент и никаких резких переходов не видно, если что-то видно, то это уже дефекты цветопередачи вашего монитора.


    1. Tarakanator
      24.08.2023 15:00
      +2

      Так монитор же выводит с учётом гамма кривой. Так что это у вас не линейный градиент. А ТС как я понял сделал картинку так, чтобы с учётом гаммы монитора сделать линейный градиент.


    1. viruseg
      24.08.2023 15:00
      +1

      Откуда вообще берутся такие юзеры на хабре. И кто-то плюсует комментарий.

      Под рукой в данный момент открытая unity. Создал простейший шейдер, который берет uv координаты и выводит x. Т.е. на выходе линейное изменение от 0 до 1. Но вообще такое даже проверять глупо. Всё равно, что подвергать сомнению высказывание "земля круглая".

      Результат
      Результат
      Шейдер
      Шейдер
      Скрин того, что лежит в uv. Чтобы не было сомнений.
      Скрин того, что лежит в uv. Чтобы не было сомнений.

      А вот с гамма-коррекцией: