Каковы практические различия при работе с цветами в линейном и нелинейном пространстве RGB?
Каково основное свойство линейного пространства RGB и каково основное свойство нелинейного пространства? Что изменится, если говорить о значениях внутри каждого канала в этих 8 (или более) битах?
В OpenGL цвета имеют значения 3+1, и под этим я подразумеваю RGB+ альфа, 8 бит зарезервированы для каждого канала, и это та часть, которую я получаю ясно.
Но когда дело доходит до гамма-коррекции, я не понимаю, каков эффект от работы в нелинейном пространстве RGB.
Поскольку я знаю, как использовать кривую в графическом программном обеспечении для редактирования фотографий, мое объяснение состоит в том, что в линейном пространстве RGB вы берете значения такими, какие они есть, без каких-либо манипуляций и без присоединенной математической функции, вместо этого, когда каждый нелинейный Канал обычно развивается после классического поведения степенной функции.
Даже если я возьму это объяснение за реальное, я все равно не пойму, что такое реальное линейное пространство, потому что после вычисления все нелинейные пространства RGB становятся линейными и, что самое важное, я не получаю ту часть, где -линейное цветовое пространство больше подходит для человеческого глаза, потому что, в конце концов, все пространства RGB являются линейными для того, что я понимаю.
2 ответа
Допустим, вы работаете с цветами 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 или гамма-коррекцию.
Я не "эксперт по распознаванию человеческих цветов", но я встречал нечто подобное при конвертации YUV->RGB. Для каналов R/G/B существуют разные веса, поэтому, если вы измените исходный цвет на x, значения RGB изменят другое количество.
Как я уже сказал, я не эксперт, в любом случае, я думаю, что если вы хотите выполнить какое-либо преобразование с коррекцией цвета, вы должны сделать это в пространстве YUV, а затем преобразовать его в RGB (или выполнить математически эквивалентную операцию с RGB, остерегайтесь потери данных). Кроме того, я не уверен, что YUV является лучшим нативным представлением цветов, но видеокамеры предоставляют этот формат, вот где я столкнулся с проблемой.
Вот волшебная формула YUV->RGB с секретными числами: http://www.fourcc.org/fccyvrgb.php