颜色空间、伽马值和图像增强

15

颜色空间。众所周知,RGB是三个范围在[0.0,1.0]内的值,它们代表颜色分量红色、绿色和蓝色的强度;这种强度被认为是线性的,对吗?

伽马。据我所知,伽马是一种将RGB颜色分量映射到另一个值的函数。通过谷歌搜索,我看到了线性函数和非线性函数... 线性函数似乎会缩放RGB分量,因此似乎会调整图像的亮度;而非线性函数则似乎会“解压”较暗/较亮的分量。

现在,我要开始实现一个图像查看器,它将显示不同的图像格式作为纹理。我想修改这些图像的伽马值,所以我应该构建一个片段着色器并覆盖纹理四边形。好的,但我如何确定正确的伽马校正?

OpenGL使用线性RGB颜色空间,使用浮点分量。事实上,我可以从这些值(具有特殊的浮点精度)计算出伽马校正的值,这样它们就可以在夹紧了伽马校正值后显示。

首先,我应该确定伽马坡道。我该如何确定它?(分析或使用查找表)

然后,我开始研究OpenGL扩展EXT_framebuffer_sRGB,它似乎与扩展EXT_texture_sRGB非常相关。

EXT_texture_sRGB引入了一种新的纹理格式,用于将像素值线性化为RGB线性空间。(注1)这样,我就知道了sRGB颜色空间并将其用作线性RGB颜色空间。

相反,EXT_framebuffer_sRGB扩展允许我将线性RGB值编码到sRGB帧缓冲区中,而不必担心。

...

等等,所有这些信息是为了什么?如果我可以使用sRGB帧缓冲区和加载sRGB纹理,处理那些纹理而无需进行sRGB转换...为什么还要校正伽马?

也许我还是应该在sRGB缓冲区上进行伽马校正吗?或者最好不要这样做?亮度和对比度:它们应该在伽马校正之前还是之后应用?

这是很多信息,我现在有点困惑了。希望你们中的某些人能更详细地解释一下这些概念!谢谢。

...

还有一个问题。如果设备的伽马值与“标准”的2.2不同,我应该如何“累积”不同的伽马校正?我不知道是否清楚:如果图像RGB值已经针对伽马值为2.2的监视器进行了校正,但监视器具有伽马值为2.8的值,那么我该如何进行伽马校正?


(1)以下是一些摘录以突出我的意思:

sRGB颜色空间基于在昏暗的办公室

   RESOLVED:  No.

  Whether the displayed framebuffer is displayed to a monitor that
    faithfully reproduces the sRGB color space is beyond the scope
    of this extension.  This involves the gamma correction and color
    calibration of the physical display device.

    With this extension, artists can author content in an sRGB color
    space and provide that sRGB content for use as texture imagery
    that can be properly converted to linear RGB and filtered as part
    of texturing in a way that preserves the sRGB distribution of
    precision, but that does NOT mean sRGB pixels are output
    to the framebuffer.  Indeed, this extension provides texture
    formats that convert sRGB to linear RGB as part of filtering.

    With programmable shading, an application could perform a
    linear RGB to sRGB conversion just prior to emitting color
    values from the shader.  Even so, OpenGL blending (other than
    simple modulation) will perform linear math operations on values
    stored in a non-linear space which is technically incorrect for
    sRGB-encoded colors.

    One way to think about these sRGB texture formats is that they
    simply provide color components with a distribution of values
    distributed to favor precision towards 0 rather than evenly
    distributing the precision with conventional non-sRGB formats
    such as GL_RGB8.

我会在一台专门用于HDR图像处理的显示器上进行渲染,而不是普通消费者使用的显示器。目前我没有显示器规格,但我猜测在渲染过程中需要应用伽马校正。 - Luca
3个回答

9

不幸的是,OpenGL本身并没有定义颜色空间。它只定义了传递给OpenGL的RGB值形成线性向量空间。渲染缓冲区的值会按原样发送到显示设备上。OpenGL只是将值通过。

Gamma有两个作用:

  • 感官知觉是非线性的
  • 在旧时代,显示设备具有非线性响应

伽马校正用于补偿这两个问题。

转换只是“线性值V到一些功率伽马”的形式,即y(v)= v^gamma

颜色空间变换涉及从输入值到发送到显示器的完整链路,因此包括伽马校正。这也意味着您不应自行操作伽马坡道。

很长一段时间内,Typical Gamma值为2.2。然而,这会导致低值的一些不良量化,因此Adobe引入了一个新的颜色空间,称为sRGB,其中低值具有线性部分,高值具有指数约为2.3的幂函数。大多数显示设备现在使用sRGB。此外,大多数图像文件现在都是sRGB。

如果您有一个sRGB图像,并将其原样显示在配置了线性伽马坡道的sRGB显示设备上(即视频驱动程序伽马=1),则只需使用sRGB纹理和帧缓冲区,不需要进行其他操作。

由于评论而编辑

总结一下:

如果您的系统不支持sRGB帧缓冲区:


1
@Luca:通常情况下,你应该坚持使用线性颜色转换;不仅在OpenGL中,而是在所有情况下。如果你知道驱动程序和显示设备应用的确切转换,你可以考虑这些因素。此外,一些显示驱动程序(专业图形卡的驱动程序)提供特殊的像素格式配置,其中应用程序的值直接传递到设备而不需要中间校正。只有在这种情况下,你才能安全地应用非线性转换,但当然必须自己进行所有校正。 - datenwolf
1
@Luca:在输出端忘掉伽马校正吧。相反,在将图像数据传递给OpenGL之前,应用一个反向伽马校正来线性化图像数据。如果您的图像具有2.6的伽马值,则应用1/2.6的伽马值,然后将其传递给OpenGL。sRGB不幸地有点麻烦,简单的伽马校正行不通。这就是EXT_texture_sRGB发挥作用的地方。 - datenwolf
@Luca:哦,还请阅读@Bethor提供的链接文章,它真的是一颗宝石! - datenwolf
1
@datenwolf,您能否验证我是否理解正确?在使用sRGB帧缓冲区时,值存储在sRGB空间本身中,而所有涉及的操作都将自动转换为在线性空间中执行。这比手动使用常规线性颜色缓冲区然后在后处理中进行伽马校正(使用着色器)产生更高的质量。因为sRGB帧缓冲区线性操作将在硬件中以完整精度执行,而手动方法会在每次传递时将值截断为8个线性位。 - Steven Lu
1
@StevenLu: 不,没有质量下降的问题,因为在着色器中,所有值都可以作为浮点值进行存储和计算,而不仅仅是8位。但是使用sRGB纹理和帧缓冲可能会导致更好的性能,因为驱动程序中内置了优化。但你是对的,颜色保留在sRGB空间中,并且只有在采样和混合时才会线性化,并在片段传输到屏幕上时去线性化。 - datenwolf
显示剩余4条评论

6
等等,这些信息有什么用?如果我可以使用sRGB帧缓冲区和加载sRGB纹理,处理这些纹理而不需要进行sRGB转换...为什么要校正伽马值?
一般来说,你不需要这样做。sRGB纹理和帧缓冲的目的是让你无需手动进行伽马校正。从sRGB纹理读取的内容会被转换为线性颜色空间,写入sRGB帧缓冲区的线性RGB值会被转换为sRGB值。这一切都是自动完成的,更重要的是,它在性能方面是免费的。
唯一需要进行伽马校正的情况是,如果显示器的伽马值与2.2伽马的sRGB估计值不匹配。但这种情况很少发生。
你的纹理不必在sRGB颜色空间中。然而,大多数图像创建应用程序都会保存图像在sRGB中,并使用sRGB中的颜色,所以很可能你的大部分纹理已经是sRGB格式,无论你是否想要。sRGB纹理功能只是让你真正获得正确的颜色值,而不是你到目前为止获取的颜色值。
亮度和对比度:是在伽马校正之前还是之后应用?
我不知道你所说的亮度和对比度是什么意思。这应该由显示器设置,而不是你的应用程序。但几乎所有你想在图像数据上进行的数学操作都应该在线性颜色空间中完成。因此,如果你得到的是sRGB颜色空间中的图像,你需要将其线性化才能对其进行任何数学处理。sRGB纹理功能可以免费实现这一点,而无需进行复杂的着色器数学运算。

我怎么知道图像加载函数是否实际给我 sRGB 值或已经校正的 RGB 值?(是的,这个语句太泛泛了;例如,设置 jpeglib 和 openexr 作为图像加载器)。 - Luca
@Luca OpenEXR是用于浮点图像的。这些图像应该在线性颜色空间中,并且sRGB对于浮点图像来说是不允许的。JPEG格式具有颜色配置文件,告诉数据所在的颜色空间,但sRGB在其中非常普遍。同样,PNG可以在其中嵌入颜色配置文件信息。 - Nicol Bolas
1
正确的做法是,OpenEXR 应该是线性的。不幸的是,有些程序会输出非线性的 OpenEXR。所以如果有疑问,一定要再次确认。 - datenwolf

1

RGB

RGB:三个标准化数值在 [0.0, 1.0] 范围内,它们代表颜色的红、绿、蓝三个成分的强度;这种强度是线性的,对吗?

不对。RGB 值没有特定空间/编码定义时是没有意义的数字。它们可能是线性、伽马编码、对数编码或使用复合传输曲线,例如 Rec709 和 sRGB 规范。

此外,它们相对于其主色和白点在颜色空间中定义,因此例如,在 sRGB 中,#00FF00 是一种不同于在 DCI-P3 中的颜色。

要定义 RGB 像素值的显示方式,您不仅需要 RGB 三元组,还需要知道它所针对的颜色空间,该颜色空间需要包括主坐标、白点和传输曲线。

sRGB 是 Web 和常规计算机默认的“标准” RGB 颜色空间。它与 HDTV 的标准颜色空间 Rec709 有关联。

GAMMA aka TRANSFER CURVE

Gamma。据我理解,伽马是一种将 RGB 颜色分量映射到另一个值的函数。

图像伽马利用人类感知的非线性特性,充分利用每个通道8位的有限数据大小。人眼对较暗颜色的变化更为敏感,因此在伽马编码的图像中使用更多的位来定义较暗的颜色。

在数字化之前,伽马也被用于NTSC广播系统中,以抑制信号中的明显噪声,类似于图像伽马防止每个通道8位图像出现“条纹”伪影。

首先,我要确定伽马坡道。我该如何确定?(通过分析或使用查找表)

伽马曲线。sRGB伽马曲线很容易访问。这里是Wikipedia链接,可以从sRGB转换到线性。您还可以使用“简化”方法,该方法仅使用2.2指数曲线:

linearVideo = sRGBvideo^2.2 ,简化反向操作,返回sRGB:

sRGBvideo = linearVideo^0.4545

使用简化版本会引入一些小的伽马误差,建议在关键操作或图像将被多次“往返”处理的情况下使用“正确”的曲线。
还有一个问题。如果设备伽马值与“标准”2.2不同,如何“累积”不同的伽马校正?我不知道是否清楚:如果图像RGB值已经校正为具有2.2伽马值的监视器,但监视器具有2.8值的伽马值,如何进行校正?
2.8???那是什么显示器?PAL?这很不寻常-虽然PAL规范说了这个,但2.8不是“实用的”。显示器通常在2.3到2.5左右,具体取决于它们的设置。当您调整黑色电平和对比度(白色电平)时,实质上是调整感知伽马以匹配观看环境(房间照明)。
只是提醒一下,虽然sRGB“信号”的编码伽马为1/2.2,但显示器通常会添加约1.1的指数。
对于Rec709,编码信号的有效伽马约为1/1.9,但参考观看环境中的显示器约为2.4。
在这两种情况下都有意识到系统伽马增益。
如果您想为2.8显示器编码图像并且不想要任何系统伽马增益,则指数为1/2.8。
常用的“最高”伽马是数字电影(以及Rec2020),为2.6。对于那些考虑PAL和2.8的人,我鼓励您阅读Poynton关于这个主题的文章:
高度推荐阅读
{{link1:Charles Poynton的Gamma FAQ}} 是一篇易读的文章,完全描述了这些问题,以及它们在图像流程中为什么很重要。同样在该链接中阅读他的颜色FAQ。
关于线性与sRGB的几句话
在线性工作区处理图像通常是理想的,因为它不仅简化了数学,而且模拟了现实世界中的光。世界上的光以线性方式运作(添加)。但是,如果使用线性工作区,则需要足够的比特深度,8位不够。
人类的感知是非线性的。图像伽马编码利用这种非线性性质,以充分利用8位图像容器。当您转换为线性时,您需要更多的位数。每个通道12位被认为是最低要求,但16位浮点是线性工作空间的“推荐最佳实践”。
如果在线性渲染环境中使用纹理,则这些纹理需要转换为线性空间(通常需要更深的位深度)。虽然增加的位数增加了数据带宽,但简化的数学通常可以加快计算速度。
sRGB是一种显示参考空间,旨在用于显示目的,并以紧凑的“显示就绪”状态存储图像。黑色为0,白色为255,传输曲线接近1/2.2。
sRGB基于Rec709(HDTV),使用相同的原色和白点。但传输曲线和数据编码不同。Rec709旨在在黑暗的客厅中在高伽马监视器上显示,并将黑色编码为16,白色编码为235。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接