it-swarm.com.ru

Каковы практические различия при работе с цветами в линейном и нелинейном пространстве RGB?

Каково основное свойство линейного пространства RGB и каково основное свойство нелинейного пространства? Что изменится при обсуждении значений внутри каждого канала в этих 8 (или более) битах?

В OpenGL цвета имеют значения 3 + 1, и под этим я подразумеваю RGB + альфа, 8 бит зарезервированы для каждого канала, и это та часть, которую я получаю ясно.

Но когда дело доходит до гамма-коррекции, я не понимаю, каков эффект от работы в нелинейном пространстве RGB.

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

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

63
Ken

Допустим, вы работаете с цветами RGB: каждый цвет представлен тремя интенсивностями или яркостью. Вы должны выбрать между "линейным RGB" и "sRGB". Сейчас мы упростим ситуацию, игнорируя три разные интенсивности, и предположим, что у вас есть только одна интенсивность: то есть вы имеете дело только с оттенками серого.

В линейном цветовом пространстве соотношение между числами, которые вы храните, и интенсивностями, которые они представляют, является линейным. Практически это означает, что если вы удваиваете число, вы удваиваете интенсивность (легкость серого). Если вы хотите добавить две интенсивности вместе (поскольку вы вычисляете интенсивность на основе вкладов двух источников света или потому, что вы добавляете прозрачный объект поверх непрозрачного объекта), вы можете сделать это, просто добавив два числа вместе. Если вы используете любой вид 2D-смешивания или 3D-затенения, или почти любую обработку изображений, тогда вы хотите, чтобы ваши интенсивности были в линейном цветовом пространстве, так что вы можете просто складывать, вычитать, умножать и разделите числа, чтобы иметь одинаковое влияние на интенсивности. Большинство алгоритмов обработки цвета и рендеринга дают правильные результаты только с линейным RGB, если вы не добавите дополнительные веса ко всему.

Это звучит очень просто, но есть проблема. Чувствительность человеческого глаза к свету лучше при низкой интенсивности, чем при высокой интенсивности. То есть, если вы составите список всех интенсивностей, которые вы можете выделить, темных будет больше, чем светлых. Другими словами, вы можете различить темные оттенки серого лучше, чем светлые оттенки серого. В частности, если вы используете 8 бит для представления своей интенсивности и делаете это в линейном цветовом пространстве, у вас будет слишком много светлых оттенков и недостаточно темных оттенков. Вы получаете полосы в темных областях, в то время как в светлых областях вы тратите биты на различные оттенки почти белого, которые пользователь не может различить.

Чтобы избежать этой проблемы и наилучшим образом использовать эти 8 бит, мы обычно используем sRGB . Стандарт sRGB говорит вам о кривой, которую нужно использовать, чтобы сделать ваши цвета нелинейными. В нижней части кривая более мелкая, поэтому у вас может быть больше темных серых оттенков, а в верхней части круче, чтобы у вас было меньше светлых оттенков серого. Если вы удвоите число, вы увеличите интенсивность более чем вдвое. Это означает, что если вы добавите цвета sRGB вместе, вы получите более светлый результат. В наши дни большинство мониторов интерпретируют свои входные цвета как sRGB. Итак, когда вы размещаете цвет на экране или сохраняете его в текстуре 8 бит на канал, сохраняйте его как sRGB, чтобы вы наилучшим образом использовали эти 8 бит.

Вы заметите, что у нас теперь есть проблема: мы хотим, чтобы наши цвета обрабатывались в линейном пространстве, но сохранялись в sRGB. Это означает, что в конечном итоге вы выполняете преобразование sRGB в линейную при чтении и преобразование в линейную в sRGB при записи. Как мы уже говорили, для линейных 8-битных интенсивностей не хватает темноты, это может вызвать проблемы, поэтому есть еще одно практическое правило: не используйте 8-битные линейные цвета если вы можете избегай это. Становится общепринятым следовать правилу, согласно которому 8-битные цвета всегда являются sRGB, поэтому вы выполняете преобразование sRGB в линейное в то же время, что и увеличение интенсивности с 8 до 16 бит или с целого числа до числа с плавающей запятой; аналогично, когда вы закончили обработку с плавающей запятой, вы сужаетесь до 8 бит одновременно с конвертацией в sRGB. Если вы следуете этим правилам, вам никогда не придется беспокоиться о гамма-коррекции.

Когда вы читаете изображение sRGB и хотите получить линейную интенсивность, примените эту формулу к каждой интенсивности:

float s = read_channel();
float linear;
if (s <= 0.04045) linear = s / 12.92;
else linear = pow((s + 0.055) / 1.055, 2.4);

Если вы хотите написать изображение как sRGB, примените следующую формулу к каждой линейной интенсивности:

float linear = do_processing();
float s;
if (linear <= 0.0031308) s = linear * 12.92;
else s = 1.055 * pow(linear, 1.0/2.4) - 0.055; ( Edited: The previous version is -0.55 )

В обоих случаях значение с плавающей точкой находится в диапазоне от 0 до 1, поэтому, если вы читаете 8-битные целые числа, сначала нужно разделить на 255, а если вы пишете 8-битные целые, вы хотите умножить на 255. наконец, так же, как вы это обычно делаете. Это все, что вам нужно знать для работы с sRGB.

До сих пор я имел дело только с одной интенсивностью, но есть более умные вещи, связанные с цветами. Человеческий глаз может различать разные яркости лучше, чем разные оттенки (более технически он имеет лучшее разрешение по яркости, чем цветность), поэтому вы можете еще лучше использовать свои 24 бита, сохраняя яркость отдельно от оттенка. Это то, что пытаются делать представления YUV, YCrCb и т.д. Канал Y является общей яркостью цвета и использует больше битов (или имеет большее пространственное разрешение), чем два других канала. Таким образом, вам не нужно (всегда) применять кривую, как вы делаете с интенсивностями RGB. YUV - это линейное цветовое пространство, поэтому если вы удваиваете число в канале Y, вы удваиваете яркость цвета, но вы не можете добавлять или умножать цвета YUV вместе, как вы можете с цветами RGB, поэтому он не используется для обработка изображений, только для хранения и передачи.

Я думаю, что это отвечает на ваш вопрос, поэтому я закончу небольшой исторической запиской. До появления sRGB в старые ЭЛТ встроена нелинейность. Если вы удвоите напряжение для пикселя, вы увеличите интенсивность более чем в два раза. Насколько больше было различно для каждого монитора, и этот параметр назывался гаммой . Это поведение было полезным, потому что это означало, что вы могли получить больше темного света, чем света, но это также означало, что вы не могли сказать, насколько яркими будут ваши цвета на ЭЛТ пользователя, если вы не откалибровали его сначала. Гамма-коррекция означает преобразование цветов, с которых вы начинаете (возможно, линейный), и преобразование их для гаммы ЭЛТ пользователя. OpenGL пришел из этой эпохи, поэтому его поведение sRGB иногда немного сбивает с толку. Но производители графических процессоров теперь склонны работать с соглашением, которое я описал выше: когда вы сохраняете 8-битную интенсивность в текстуре или кадровом буфере, это sRGB, а когда вы обрабатываете цвета, это линейно. Например, OpenGL ES 3.0, каждый кадровый буфер и текстура имеют "флаг sRGB", который можно включить, чтобы включить автоматическое преобразование при чтении и записи. Вам вообще не нужно явно выполнять преобразование sRGB или гамма-коррекцию.

174
Dan Hulme

Я не "эксперт по распознаванию человеческих цветов", но я встречал нечто подобное при конвертации YUV-> RGB. Для каналов R/G/B существуют разные веса, поэтому, если вы измените исходный цвет на x, значения RGB изменят другое количество.

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

Вот волшебная формула YUV-> RGB с секретными числами: http://www.fourcc.org/fccyvrgb.php

1
ern0