如何理解Gamma校正?

网上很多人讲了Gamma校正的东西,好多都是说由于CRT显示器的电压与亮度不是线性关系的特性导致的,但总感觉怪怪的。

韩世麟同学写出了我的困惑。

凡是说Gamma 2.2来自于老式CRT显示器物理特性的解释,都是误解。 这个误解一般会这么讲解Gamma的来龙去脉:当年老式的CRT显示器内置Gamma 2.35左右,解码的时候会把输入信号压暗,所以我们呢,为了保证总Gamma接近1,就要预先在编码的时候把输入文件的信号提亮,而且这样一来呢,刚好顺应了人眼对暗部感兴趣的特点,把暗部的信息多多记录了下来,充分利用了文件的空间,真是美妙的巧合啊。讲起来顺畅,听起来也很美妙,我也曾经这么给别人讲,但是我发现我没法说服我自己,人类就被一个老式硬件的物理特性决定了后世的工业标准?这逻辑不对。

所以我觉得需要再梳理下Gamma校正的前因后果,以便更好地理解。

图像保存与图像显示用的Gamma校正#

人眼对亮度的感觉#

首先从人眼对亮度的感受出发。

下图是摄影箱常用的黑白灰卡。假定人眼感受到的白色是100%灰阶,黑色是0%的灰阶,那灰卡上的中灰是介于黑色和白色之间50%处的灰阶。

用反射率测量仪器测量各卡的反射率,假定白卡的反射率为100%。那么黑卡的反射率为4%,中灰卡的反射率是18%。并不是我们通常认为的的50%。

再看一个韩世麟同学提到了《粉刷匠的故事》,用以更好地理解灰阶。

假如你是一位粉刷匠,你拥有充足的白油漆和充足的黑油漆,那么现在要求你完成一个任务:

把黑白油漆混合成各种不同灰度的油漆,并且把它们排成一排,直到形成黑到白的均匀过渡。

在理想的情况下,会发生什么样的情况?你会不断地调出灰色的油漆,并且把它和已有的油漆相比较,如果它是一个新灰色,你就会把它插入队列,如果是重复的灰色(肉眼难以分辨,达到了你灰阶分辨能力的极限),你就会把它丢掉。

最终,功夫不负有心人,你面前的灰阶将会形成从黑到白的均匀过渡,此时的灰油漆种类将会是几百个甚至更多,那么最中间那一个,就是中灰的油漆:

之前我们已经测量过中灰的反射率为20%左右。从上面这个图可以得到结论:从纯黑到中灰,与中灰到纯白,这两部分人眼能分辨出来的灰阶数目是一样的。

对这些灰阶进行物理反射率的测量,可以得到自然界中亮度(可以用反射率近似)与人眼感受到的灰阶的对应关系。

从上面的曲线对应关系可以得到结论:

  1. 人眼对自然界亮度的感受不是线性的,对应关系近似幂次函数ViewL0.4545View \approx L ^ {0.4545}
  2. 人眼感受 0 ~ 0.5的灰阶与0.5 ~ 1.0的灰阶,分别对应了反射率0 ~ 0.2, 0.2 ~ 1.0,也就是说人眼对暗的亮度变化感受比较明显

人眼对亮度的感受非线性可以由韦伯定律解释。

韦伯定律是由德国著名的生理学家与心理学家E.H. 韦伯发现,韦伯-费希纳定律是表明心理量和物理量之间关系的定律,即感觉的差别阈限随原来刺激量的变化而变化,而且表现为一定的规律性,用公式来表示,就是△Φ/Φ=C,其中Φ为原刺激量,△Φ为此时的差别阈限,C为常数,又称为韦柏率。

举例,距离在一个黑暗的房间里,假定点第1支蜡烛时人眼感受到的亮度为1,那么想要让人眼感受的亮度增加到2,就需要再点燃2支蜡烛,而不是1支。同样地,人眼想要感受亮度为3,就需要再加4支蜡烛。人的感受变化不是随着刺激线性变化的。生活中有很多这样的例子,人对疼痛的感受,味觉等等。

人眼对暗的亮度变化感受比较明显,在图像大小受限的条件下,可以花更多的空间存储颜色较暗的部分,能起到提高图片质量的效果

到这,先记住两个结论:

  1. 人眼对自然界亮度的感受不是线性的,对应关系大约是幂次函数pow(0.45)
  2. 人眼对暗的亮度变化感受比较明显,也就是暗部细节对视觉更重要。

接下来从讲讲CRT显示器的故事。

CRT显示器#

CRT显示器大概长这样,也被称为“大背头”。

CRT的由来,来自百度百科。

1897,诺贝尔奖获得者、著名物理学家和发明家KarlFerdinand Braun(卡尔·布劳恩)创造了第一个CRT (Cathode Ray Tube,阴极射线管)。其工作原理是:电子枪发射高速电子,经过垂直和水平的偏转线圈控制高速电子的偏转角度,最后高速电子击打屏幕上的磷光物质使其发光,通过电压来调节电子束的功率,就会在屏幕上形成明暗不同的光点形成各种图案和文字。但是,此时的CRT大部分还是用来验证粒子、电子等现象的设备,似乎同显示毫无关系。

直到1925年,约翰·洛吉·贝尔德(John Logie Baird)在伦敦的一次实验中使用CRT器材“扫描”出木偶的图象成为一个转折点,其被称为电视诞生的标志。

CRT显示器的工作原理:

CRT显示器用电子束来进行控制和表现三原色原理电子枪工作原理是由灯丝加热阴极,阴极发射电子,然后在加速极电场的作用下,经聚焦极聚成很细的电子束,在阳极高压作用下,获得巨大的能量,以极高的速度去轰击荧光粉层。这些电子束轰击的目标就是荧光屏上的三基色。为此,电子枪发射的电子束不是一束,而是三束,它们分别受电脑显卡R、 G、 B三个基色视频信号电压的控制,去轰击各自的荧光粉单元。 电子束的电流是受显示信号控制的,信号电压高, 电子枪发射的电子束流也越大,荧光体发光亮度也越高

这种显示器有个特性,显示器显示出来的亮度与电压并不是线性关系。

电压与亮度的关系为L=VgammaL = V^{gamma},L是输出亮度,V是电压,gamma一般约为2.2。我们称它CRT gamma

在这里我们定义Gamma:输出与输入的幂次关系。

Gamma校正主动对输入信号做一个幂次函数变换

可以这么理解这个公式,想让显示器显示20%的物理亮度,如果显示器按照给20%的电压,那么最终显示器显示的亮度为2.9%,显示器你这不搞笑吗?你得给我解决这个问题。

显示器厂商可以这样做,反正显示器电压和亮度不成线性关系在技术上也没法整,那不如就在现成条件下,对输入信号做一下处理。比如想要在显示器上显示20%的亮度,那显示器直接用20%的电压整肯定不行,那显示20%的亮度多大的电压呢?查这个曲线嘛,需要50%。那显示器提供50%的电压,就可以显示出50%的亮度了。

也就是说显示器厂商可以对输入信号做一个CRT gamma的逆校正,就可以让显示器得到正确的亮度。即

L1.0/CRTgammaL1.0/2.2=L0.4545L^{1.0/CRT_{gamma}} \approx L ^{1.0/2.2} = L ^{0.4545}

那成,显示器可以按照这个方法解决物理亮度显示不正确的问题

摄像设备保存物理真实亮度到图片里,电脑读取图片,并让显示器显示。虽然显示的亮度大致上没问题,但艺术家很挑剔,感觉你这图片咋感觉那么别扭呢,丢失了好多细节,于是找技术看看怎么回事。

还记得之前说的人眼对亮度感受的结论吗?

人眼对暗的亮度变化感受比较明显,也就是暗部细节对视觉更重要。

图像工程师经过分析后,知道原因了:图片保存亮度就一个字节,只能有255个数值,按照真实亮度从0%到100%均匀地分配。但是中灰只对应20%的亮度,也就是说人眼更敏感的暗部只占了很小一部分的存储空间

可以这样处理,按照人眼的亮度感受曲线,把真实亮度编码之后,再存储到贴图上,理论上图片的质量比之前更好了。这个编码我们叫它Encoding Gamma。 处理公式可以近似成:

Image=L0.4545Image = L^{0.4545}

但有一个问题,这时候图片里存储的已经不是物理的亮度了,而上面说的显示器的假定的输入是物理亮度,这不就出问题了嘛。这还涉及到显示器供应商,那得开个会啊。

于是微软、惠普、互联网企业和供应商开了个会,显示器别整那别扭的CRT gamma逆校正了。那样高质量图片(encoding gamma = 0.4545),正好能在显示器的CRT gamma(gamma = 2.2)作用下显示出自然界中的样子,多棒的巧合(encoding gamma * CRT gamma = 1)!

于是他们提出了sRGB标准,作为互联网的通用色彩标准。

当然sRGB标准比上面说的要复杂,它提出了几个概念:

  1. Viewing gamma:是整个从图片拍摄到最终显示器上显示的gamma,是camera gamma 与 display gamma的乘积。
  2. Camera gamma:摄像或感光元件对物理亮度的转换gamma,也就是上面提到的encoding gamma,图片里保存的是转换后的值。
  3. CRT gamma:物理显示器的gamma。
  4. LUT gamma:帧缓冲的颜色映射表的gamma,不是shader里用的LUT,猜测是显示器自己实现的色彩校正的东西。
  5. Display gamma:CRT gamma * LUT gamma,提交帧缓存的显示gamma。

所以我们需要关注的是

viewgamma=encodinggammadisplaygammaview gamma = encoding gamma * display gamma

下图是真实场景产生图像到显示器显示图像的完整流程,如果想要显示器显示的图像与看到的真实场景完全一样,保证viewinggamma为1就可以了。

当然在viewgamma并不要求一定为1,当在昏暗的电影院时,viewgamma为1.5我们人看起来更爽、更舒服,而在明亮的办公室这种环境中更适合1.125。

sRGB标准帮我们处理了gamma,解决了图像生成和显示的问题:

  1. 在图像生成的时候做了encoding gamma的编码。
  2. CRT显示的时候执行display gamma。

虽然给工业界提供了很大的便利,但同时也会引发其他的问题。

sRGB标准引起的问题及如何解决#

着色计算#

自然界20%的亮度,存储在贴图里的值经过encoding gamma后约保存为50%。

现在有个效果需求,对这个亮度效果翻倍。正确的亮度是 20% * 2 = 40%。

而现在的渲染流程会发生什么?shading里读取贴图得到的值为 50%,在shader里乘以2,得到100%,经过显示器显示它是100%的亮度!错了!

现在整理下渲染流程,并定义几个空间便于理解。

  1. 物理亮度值所在的空间,是物理空间,我们称它为线性空间
  2. 物理亮度经过encoding gamma后,结果所在的空间成为encoding gamma空间
  3. 贴图经过shading,输出到framebuffer,结果所在的空间为仍然为encoding gamma空间
  4. Framebuffer经过显示器的display gamma后,结果所在的空间为viewing gamma空间(viewing gamma = encoding gamma * display gamma)。

image.png

发现问题了吗?我们期待的结果是在线性空间中将亮度乘以2。但是在渲染流程里面,是在encoding gamma空间中乘以2,结果必然出问题。

所以应该在shading计算前将贴图转换到线性空间,再将最后的framebuffer转换到encoding gamma空间,结果就对了。

image.png

在图形API中,可以通过创建贴图的时候用sRGB后缀的格式将贴图转换到线性空间,GPU会自动转换;当然也可以在shader里执行pow(tex, 1.0/encoding gamma) 完成转换。

颜色混合#

借用博客——我理解的伽马校里的例子:

在Quad上画了个边缘模糊的圆,然后使用了混合模式来会屏幕进行混合。我们在场景中画三个这样不同颜色的圆,三种颜色分别是(0.78, 0, 1),(1, 0.78, 0),(0, 1, 0.78):

在不同颜色的交接处出现了不正常的渐变。例如,从绿色(0, 1, 0.78)到红色(0.78, 0, 1)的渐变中,竟然出现了蓝色。

正确的显示结果应该是:

原因跟上面着色的例子是一样的,混合时的颜色值不在线性空间,正确的做法应该先把颜色转换到线性空间,进行混合,然后再转换到encoding gamma空间。

Youtobe上有一个视频讲解混合出现的问题, Color is broken,可以看看。

我们使用一个稍微扩展的数学公示来解释:假定xx为亮度1,yy为亮度2,保存到图里分别是

xencodinggammax^{encoding gamma}yencodinggammay^{encoding gamma}

xxyy按照50%进行混合,正确的结果为 (x+y)2\frac{(x + y)} 2

而目前图形API采用的混合方式,是直接对贴图的颜色值进行了混合,得到的结果是(xencodinggamma+yencodinggamma)2\frac{(x^{encodinggamma} + y^{encodinggamma})} 2

正确的结果是线性空间的,图形API混合的结果在encoding gamma空间。为了方便比较,我们将线性 空间的结果也转换到encoding gamma空间:(x+y2)encodinggamma({\frac{x + y} {2}})^{encodinggamma}

琴生不等式可得,

(xencodinggamma+yencodinggamma)2(x+y2)encodinggamma\frac{(x^{encodinggamma} + y^{encodinggamma})} 2 \le ({\frac{x + y} {2}})^{encodinggamma}

也就是说在encoding gamma空间混合的结果比真实的结果要暗的。

不过各种软件和厂商的混合算法,都是拿直接拿贴图或framebuffer做的混合,而贴图或framebuffer在什么空间,GPU是不知道的,所以想要得到正确的结果,需要图形程序员自行做空间的转换。

总结#

由于人眼对自然界亮度的非线性感受,并且对暗部比较敏感,再加上CRT显示器电压与显示亮度非线性关系,最终工业界提出了sRGB通用颜色空间,作为互联网的通用标准,用以解决图像质量和显示器显示的一系列问题。

而之后长时间在选中光照和混合长时间都不在线性空间中计算,造成失真。解决这个问题需要在shader计算前把贴图转换到线性空间,在线性空间中光照、混合!最后再转换到encoding gamma空间,才能得到正确的渲染结果。

参考#

  1. 色彩校正中的 gamma 值是什么? - 知乎
  2. 【图形学】我理解的伽马校正(Gamma Correction)_candycat-CSDN博客_伽马校正
  3. Color is broken
  4. en.wikipedia.org/wiki/Gamma_…
  5. en.wikipedia.org/wiki/Cathod…
posted @   silence394  阅读(0)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示