在线性与非线性RGB空间中处理颜色时有哪些实际差异?

Ken*_*Ken 69 graphics rgb colors color-coding color-space

线性RGB空间的基本属性是什么?非线性RGB空间的基本属性是什么?在谈论这8个(或更多)位中每个通道内的值时,会发生什么变化?

在OpenGL中,颜色是3 + 1值,我的意思是RGB + alpha,每个通道保留8位,这是我清楚得到的部分.

但是当涉及到伽马校正时,我无法得到在非线性RGB空间中工作的效果.

由于我知道如何在图形软件中使用曲线进行照片编辑,我的解释是,在线性RGB空间中,您可以按原样获取值,不需要操作和附加数学函数,而是在每个非线性时通道通常随着经典的幂函数行为而演变.

即使我把这个解释作为真实的解释,我仍然没有得到真正的线性空间,因为在计算之后所有的非线性RGB空间变得线性,最重要的是我没有得到非零的部分 - 线性色彩空间更适合人眼,因为最终所有RGB空间都是线性的,我所理解的.

Dan*_*lme 192

假设您正在使用RGB颜色:每种颜色都用三种强度或亮度表示.您必须在"线性RGB"和"sRGB"之间进行选择.现在,我们通过忽略三种不同的强度来简化事物,并假设你只有一种强度:也就是说,你只处理灰色阴影.

在线性颜色空间中,您存储的数字与它们所代表的强度之间的关系是线性的.实际上,这意味着如果你将数字加倍,你的强度加倍(灰色的亮度).如果要一起添加两个强度(因为您根据两个光源的贡献计算强度,或者因为您在不透明对象上添加透明对象),您可以通过添加两个数字在一起.如果你做任何形式的2D混合或3D阴影,或者几乎任何图像处理,那么你希望你在一个线性颜色空间的亮度,所以你可以加,减,乘,除的号码有同样的效果关于强度.大多数颜色处理和渲染算法只能使用线性RGB提供正确的结果,除非您为所有内容添加额外的权重.

这听起来很容易,但是有一个问题.人眼对光的敏感度在低强度时比在高强度下更精细.也就是说,如果你列出了你可以区分的所有强度,那么黑暗的那些比浅色的更强.换句话说,你可以用浅色调的灰色来分辨深色调的灰色.特别是,如果你使用8位来表示你的强度,并且你在一个线性色彩空间中这样做,你最终会得到太多的浅色调,而且暗色调不够.你会在你的黑暗区域进行划分,而在你的光线区域,你会在不同的近白色阴影上浪费一些用户无法区分的位.

为了避免这个问题,并充分利用这8位,我们倾向于使用sRGB.sRGB标准告诉您使用曲线,使您的颜色非线性.曲线底部较浅,因此您可以拥有更多深灰色,顶部更陡峭,因此您拥有更少的浅灰色.如果您将数字加倍,则强度增加一倍以上.这意味着如果将sRGB颜色添加到一起,最终会得到比它应该更轻的结果.目前,大多数显示器将其输入颜色解释为sRGB.因此,当您在屏幕上添加颜色或将其存储在每通道8位纹理中时,将其存储为sRGB,以便充分利用这8位.

您会注意到我们现在遇到了一个问题:我们希望我们的颜色在线性空间中处理,但存储在sRGB中.这意味着您最终会在读取时进行sRGB到线性的转换,并在写入时进行线性到sRGB的转换.正如我们已经说过线性8位强度没有足够的暗,这会引起问题,所以还有一个更实用的规则:如果你能避免它,就不要使用8位线性颜色.这已经成为常规的遵循规则,8位颜色总是sRGB的,所以你在同一时间,扩大你的强度从8位到16位,或从整数浮点做你的sRGB到线性转换; 类似地,当您完成浮点处理时,在转换为sRGB的同时缩小到8位.如果您遵循这些规则,您永远不必担心伽马校正.

当您正在读取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);
Run Code Online (Sandbox Code Playgroud)

另一方面,当您想要将图像写为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 )
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,浮点s的值范围从0到1,因此如果您正在读取8位整数,则首先要除以255,如果要写入8位整数,则需要乘以255最后,你通常会这样做.这就是使用sRGB所需要知道的全部内容.

到目前为止,我只处理过一种强度,但颜色更明确.人眼可以比不同的色调更好地分辨出不同的亮度(从技术上讲,它具有比色度更好的亮度分辨率),因此通过将亮度与色调分开存储,可以更好地利用24位.这就是YUV,YCrCb等代表所做的事情.Y通道是颜色的整体亮度,并且比其他两个通道使用更多位(或具有更多空间分辨率).这样,您就不会(总是)像使用RGB强度那样应用曲线.YUV是一种线性的色彩空间,因此,如果您双击在Y通道的数量,您双击颜色的亮度,但你不能相加或相乘YUV色彩在一起,就像你可以与RGB色彩,所以它不是用于图像处理,仅用于存储和传输.

我认为这回答了你的问题,所以我将以快速的历史记录结束.在sRGB之前,旧的CRT曾经内置了非线性.如果将像素的电压加倍,则强度会增加一倍以上.每个监视器有多少不同,这个参数称为gamma.这种行为很有用,因为它意味着你可以获得比灯光更暗的颜色,但这也意味着你无法分辨你的颜色在用户的CRT上有多亮,除非你先校准它.Gamma校正意味着转换您开始的颜色(可能是线性的)并将它们转换为用户CRT的伽玛.OpenGL来自这个时代,这就是为什么它的sRGB行为有时会让人感到困惑.但GPU供应商现在倾向于使用我上面描述的惯例:当你在纹理或帧缓冲器中存储8位强度时,它是sRGB,当你处理颜色时,它是线性的.例如,OpenGL ES 3.0,每个帧缓冲区和纹理都有一个"sRGB标志",您可以打开它以在读取和写入时启用自动转换.您根本不需要明确地进行sRGB转换或伽马校正.

  • 惊人的答案,谢谢你,看到解释的东西总是令人惊叹,我只会要求你认为书或资源对于这个主题,色彩空间以及用于转换的公式是什么? < - >线性或可以近似此行为的函数是什么. (4认同)