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

88

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

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

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

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

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

Кен
источник
С практической точки зрения, вы можете указать в качестве цветового пространства в SVG либо линейный, либо стандартный RGB, и я понятия не имею, каков будет эффект, кроме того факта, что это кажется важным :-)
Майкл Маллани,
@MichaelMullany, эта линейная вещь больше похожа на подсказку для пользователя, чем на реальную отличительную черту.
Кен
Что-то, что мне помогло: я думаю, это учитель математики сказал мне: «Думай, когда слышишь линейный». Не уверен, что это поможет, но для меня это было «О да!» момент
Джо Планте

Ответы:

229

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

В линейном цветовом пространстве отношение между сохраняемыми вами числами и интенсивностями, которые они представляют, является линейным. На практике это означает, что если вы удвоите число, вы удвоите интенсивность (яркость серого). Если вы хотите сложить две интенсивности вместе (потому что вы вычисляете интенсивность на основе вкладов двух источников света или потому что вы добавляете прозрачный объект поверх непрозрачного объекта), вы можете сделать это, просто добавив два числа вместе. Если вы выполняете какое-либо двухмерное смешивание или трехмерное затенение, или почти любую обработку изображений, тогда вам нужны ваши интенсивности в линейном цветовом пространстве., поэтому вы можете просто складывать, вычитать, умножать и делить числа, чтобы иметь такой же эффект на интенсивности. Большинство алгоритмов обработки цвета и рендеринга дают правильные результаты только с линейным 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 )

В обоих случаях значение s с плавающей запятой находится в диапазоне от 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 или гамма-коррекцию.

Дэн Халм
источник
6
замечательный ответ, спасибо, всегда удивительно видеть объяснения, я бы попросил только книгу или ресурс, которые, по вашему мнению, будут достаточно хороши для этой темы, цветовых пространств и какие формулы используются для преобразования sRGB <-> linear или какая функция может аппроксимировать это поведение.
Кен
Боюсь, я не знаю хороших книг или ресурсов. Страница Википедии является всеобъемлющей и включает в себя все, что касается белой точки и того, насколько мала гамма (что я не упоминал, поскольку большинству людей не нужно об этом знать), но это делает ее немного непонятной. .
Дэн Халм
1

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

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

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

ern0
источник
1
Будьте осторожны с преобразованиями RGB <-> YUV и обратно. Я не уверен, что это во всех случаях, но цветовое пространство YUV иногда преобразуется в диапазон от 16 до 235 вместо диапазона от 0 до 255 в 24-битном RGB. Таким образом, вы можете терять данные каждый раз, когда выполняете преобразование цветового пространства. Большинство людей склонны придерживаться одного и того же цветового пространства, если можете.
Джо Плант