这是第一篇 Camera ISP 的文章,主要从自己收集到的资料来试着描述下关于 Gamma 的理解,需要注意的是目前为止我了解到的有关于 Gamma 的说法还是众说纷纭,各处都不甚一致,在尽可能收集资料之后进行了自己的理解归纳,可能与别人的又是不一样的一套东西,不过不求达到最精确的理解,能够达到百分之七八十的正确性就可以了,这样已经能够保证百分百可以实际应用到工作中。Gamma 之所以是一个比较复杂的概念,原因是它涉及到了好几个不同的知识领域概念,每一块里面 Gamma 的具体解释与应用不甚相同,我想这个应该是 Gamma 之所以如此令人混淆的原因。

有关于 Gamma 矫正存在的原因大概有下面几种说法:

  1. 由于古早时期的 CRT 显示器的显示亮度和电压并不是呈线性的关系,所以需要一个非线性的电压->亮度的映射公式,呈 2.2(实际上是 1.8~2.5 之间) 倍左右的指数关系。
  2. 人眼对于自然光线的亮度增长不是线性关系,而是和亮度的增长率呈现线性关系,人眼感知亮度->物理亮度映射是约为 2.2(实际上是 1.8~2.5 之间) 左右的指数关系。
  3. 由于人眼对亮度主观感受并不是呈现线性关系,人眼对暗部感受更加明显,在 8bit 的颜色存储系统中我们希望更多的存储暗部信息,亮部信息可以删减一点。

关于 CRT 的 Gamma 是多少这个问题也有点疑惑,有的说是 2.2,有的说是 2.5,唯一我可以确定的就是它确实是一个范围,很多地方目前都是说的 2.2,而一些比较早的文章是 2.5,所以我倾向于认为 2.2 和 2.5 是一个比较普遍的 CRT 显示 Gamma 值,而最新的比较常用的应该是 2.2,需要知道的是 Gamma 的值确实是在发展中变化的,倾向于怀疑早期的 CRT 显示器确实就是 2.5(普遍值而非精确值), 然后后面经过发展之后发现 2.2 更适合我们的需求,后续就对齐到 2.2 了,你看 sRGB 标准的 8bit 图 Gamma 参考值就是 2.2。不过本文只考虑 Gamma 最本质的作用,所以 2.2 和 2.5 无所谓,在说明的时候会选定其中一个。

上面几个看不懂没关系,后面就来一条条的尝试去解释下每一个的作用以及它们之间的联系。话说回来,我看完所有的目前已有的文章,都有一个疑问深深的刻在我的脑海里无法消除,而这些文章我貌似都没有看到有效的解释,这个疑问需要看完上面几点的解释之后才知道是什么,我在这里特别强调下,因为实在是感到疑惑。

人眼的感光特性

人眼对于亮度的变化呈现非线性的感知,就比如说一个亮度最高为 500nit 的发光体,我们看 1nit、250nit、499nit 并不是一个线性的亮度增长关系,更容易理解点的描述就是说,我们看亮度值的变化,从 1nit 到 3nit 可以很容易的区分出来亮度的变化,而从 498nit 到 500nit 的时候我们就很难区分出来亮度是否有变化,这就是所谓人眼感知亮度和物理世界亮度标示值变化不是线性相关的,而是指数相关的。

为什么会出现这种情况,究竟是物理标示值错了还是我们的感知出错了?这个其实是一个人类感知的普遍现象,就比如说对疼痛的感知我们也不是线性的,音阶也不是线性分布的,对同样幅度加薪的兴奋程度也是非线性的,甚至我们的观察也不是线性的,比如说 100 个土豆变成 101 个,我们很难察觉到其中的变化,但是 1 个变成 2 个我们很容易就察觉出变化,如果你怀疑这是因为 100 不好数的原因的话,我们对大小的感知也是如此。人类对世界的感知应该是基于变化量的感知,不变的部分就比较难以感知到了,不知道本质为什么,但是姑且理解确实有这么个特性,而这个特性的描述可以参见「韦伯定律」。

人眼的感光特性可以看作是一个 Luminance 到 Light 的 0.42 次幂曲线,找到一个参考图,来自:https://www.zhihu.com/question/275503679 @OverDrive 的回答:

人眼感光 Gamma 曲线

这个参考图是为了说明为什么有 log 这个玩意儿,不过我觉得也可以很好的说明本节的问题,就拿来用了。这个图片分为上下两个,横轴都是 Luminance,纵轴都是人主管上对于亮度 Lightness 的感知值。上面的那个是 Luminance 均匀变化时候对应人眼观测到的亮度变化,可以看到人眼观测的高亮度区域特别集中,也就是说在 Luminance 比较大的时候,同样的增量对于人主观感觉的亮度提升是没有太大贡献的。而如果我们从纵轴来观察,也就是下面那副图,人眼观测亮度如果是均匀变化的,那么 Luminance 就比较集中于暗部区域,亮部区域比较分散,从信息论的角度来讲,也就是暗部区域包含了更多的信息量,而亮部区域相对来说信息量比较少。

那么现在如果不考虑数学以及其它的细节,就从直觉上来讲,为了让人眼看到的图像显得亮度均匀,观测到更多细节,我们是不是应该在 Luminance 的暗部存储多花些心思,亮部区域就可以稍微粗犷一点,丢一些数据量对观测结果影响不大。当然这里只是引出一个直觉性的东西,没有任何限定条件,所以显得有点儿戏,关于具体的理论描述后面会涉及到,这里想在心里打一个概念,那就是暗部区域似乎显得更重要一点,接着王后面看。

古老的 CRT 显示器

早前的 CRT 显示器,全称:Cathode-ray tube,也就是阴极射线管,使用电子枪作为显像设备,电子枪使用电压控制电子轰击到荧幕上面进行显像,这个电压和最终的显示亮度不是线性增长的关系,而是呈现前面说的指数形式,也就是随着电压的增加,亮度增加是越来越快的,曲线图如下所示:

CRT 显示 Gamma

三条不同的曲线代表不同对比度设置下的电压/亮度映射关系,也就是说对比度对曲线是有影响的,但是这个不是我们关注的点,我们只需要知道电压和亮度有一个幂指数关系就可以了。还有个问题就是,这个纵轴的亮度值应该是通过仪器测量而不是目测的结果,标示值也是 Luminance 而不是 Lightness,曲线的典型值是幂为 2.2/2.5 的指数。

到这里就要拎出来那个传说中的:惊人的巧合。说的就是人眼的亮度感知曲线和 CRT 显示器的 Luminance 曲线刚好是类似倒数的关系,前者是 0.42,后者是 2.2/2.5,那么假设说我们有一个图片,它没有经过任何的 Gamma 处理,也就是线性存储,那用电子枪打出来的显示亮度在二者叠加之后刚好呈现出原始图片 Luminance -> 人眼观测亮度为大约 1:1 的线性关系。这块有点绕,我用下面的几个步骤去描述:

  1. 原始图像存储的是像素值,我们姑且理解为 Luminance 的线性值,输入到 CRT 里面就最终表现为与之线性对应的电子枪的电压值。
  2. 这个像素值经过 CRT 解码,使用电子枪打到荧幕上面会自动添加一个幂为 2.2/2.5 的指数效果,使得荧幕显示的 Luminance 值显示曲线看起来是被压凹下去了。
  3. 由于人眼的 Gamma 响应,显示器上面的显示值到我们主观感知的亮度值又被施加了一个幂为 0.42 的指数效果,最终使得像素值 -> 人眼观测亮度的变化率呈现 1:1 的关系。

看起来不错,但是。。。这跟我们人眼观测到的现实世界不一样呀,毕竟我们直接观测现实世界的时候实际上还是一个现实世界 Luminance 值 -> 人眼观测亮度为 0.42 次幂的曲线关系,这个巧合是看起来有点巧合,但是这样并不符合我们观测现实世界的感觉。这里我们只需要了解到老式的 CRT 显示器有这么一个规律,到最后面的时候把所有的信息串到一起就能够得到一个关于 Gamma 的大致脉络。

紧张的灰阶预算

目前为止,比较主流的色彩存储还是 8bit 的宽度,之前 oppo 做过一个全通道 10bit 的色彩通道,在 Find X3 Pro 上面实现过,整个的图像采集、编码、存储、解码、显示全部都是 10bit 的域操作的,因为 10bit 带来的是 256 的 4 倍宽容度,也意味着更加精确的颜色保真度。那么这个位宽影响的到底是什么么,下面这幅图融合了 Gamma 编码部分,但是我觉得非常能够说明一个问题,那就是位宽不够带来的是什么?

Gamma encode

可以看到第一个是原始的亮度图,我们可以理解为它就是一个自然界的色卡,可以无限细分,代表着从最暗到最亮的变化,这里不用管这个最亮的绝对值是多少,最暗的绝对值是多少,姑且认为是一个极大的值和一个极小的值。第二个就是如果要把这段亮度编码成为一个 5bit 的数据可以存储的量化亮度值,假设说我们按照线性编码那么暗部就会有比较多的值被映射到同一个亮度值编码区域,为什么看起来会是这个样子的?可以参考上面人眼的感光特性部分,我们主观感觉使得我们看到的线性编码之后的暗部区域显得比较稀疏,亮部区域显得比较密集。

就从第一和第二个图就可以看出来一旦灰阶数量不够,依然按照线性编码,会出现什么后果?那就是人眼敏感的暗部区域信息被大量丢弃了,最终会导致显示的时候无法准确还原色彩,因为线性编码之后整个的信源就丢失了很多,从信息论角度来讲就是我们感兴趣的信息被丢了很多,保留的是对我们用处不大的信息,综合起来整个的存储效率就是比较低的。还有个问题觉得有必要提一下,假设第一幅图代表自然界亮度的一部分,比如说从 0lx 到 100000Lx,而第二个图就是存储 0lx 到 100000lx,只不过中间的部分信息被压缩了。

但是假设说我们有足够的位宽可以容纳 0~100000 的每一个数值,那就表示我们可以做到精度为 1lx 的无损存储,当然因为自然界是连续的,并非我们量化之后的间断型数据,这里就是一个举例说明,假设我们只需要精确到 1lx 就能够满足需求,那可不就是无损存储嘛。这个时候就不需要考虑什么人眼感光特性,直接暴力存储,力大砖飞,有多少存多少就可以了,不用担心信息量减少的问题。

跟 Gamma 有什么关系

前面铺垫了那么久,那么就那些概念跟 Gamma 有什么关系呢?我觉得先明确一个本质的问题,那就是:Gamma 本质上就可以看做一个幂指数映射关系。然后我们再去考虑下面几个不同领域的定义:

  1. Image Gamma
  2. Display Gamma
  3. System Gamma

这里有一个简单的 gamma pipeline 示例:

Gamma encode pipeline

Image Gamma
这个 Gamma 就是上图里面的第一个,相当于 Gamma encode 的步骤,也就是我们上面说的灰色预算一节描述的内容,它的 Gamma 曲线是一个上凸的矫正,也就是最终编码出来的亮度更多的集中存储了暗部区域的信息。这个 Gamma 会随着图像像素数据一起保存到我们常见的 Jpeg、Png 等图像文件里面,这一步的 Gamma 目的主要有下面几个:

  1. 为了适应 CRT 显示器时代遗留下来的显示端的 Gamma 矫正,是的最终显示出来的图像更加接近于真实世界看到的图像。
  2. 由于图像存储带宽限制,我们需要把有效信息更多的集中在我们感兴趣的区域,也就是暗部,以此来在带宽有限的条件下获得更加有效的信息。

Display Gamma
显示的时候对上面 Gamma 的补偿,同时恢复我们 Encode gamma 之前的原始数据,CRT 显示器就是一个硬件的 Display gamma 例子,这一步保证了我们最终显示出来的画面更加适合我们主观感觉,一个比较高的 Display gamma 会使得最终显示亮度变得比较暗,但是也有更高的对比度。

System Gamma
就是经过上面两个 Gamma correction 之后的最终 Gamma 系数,理论上来说我们需要把这个做成 1,因为它是理论上比较接近现实世界的显示亮度。但是从实际操作的时候我们一般是把 system gamma 做成一个大于 1 的矫正曲线,这样可以获得更好的动态范围,达到较高对比度的目的。但是更加深入一点去探讨,实际我们应用的时候就更加复杂点儿,我们会根据图像的亮暗区域分别应用不同的 Gamma 校正系数以达到更好的显示效果,当然这个都是比较主观的感受。

这里我就想再次回到为什么说 CRT 显示器的 Gamma 和人眼的感光特性是一个惊人的巧合。因为考虑我们从一开始就拥有一个 Gamma 为 1 的显示器,也就是给什么电压就显示线性比例的亮度,如果我们把图像存储位宽不足看做是一个因,我们依然还是需要对数据源进行编码,尽可能按照人眼的感光曲线特性去编码原始亮度数据,这个就是 Image gamma(encode gamma),在显示的时候依然还是得施加一个反向矫正以达到最终系统 Gamma 为 1。重点在哪里?重点就是 CRT 显示器自动充当了 Display Gamma(decode gamma) 的角色,不需要我们特意去为此设计一个元件,而这个就是我理解的巧合,这导致了我们先注意到 CRT 的特性,把它当做了因,而后面的一系列是果。

不同标准

实际应用当中,我们有好几种不同的色彩标准,不同的色彩标准里面定义的 Image gamma、Display gamma、System gamma(End to end gamma) 也是有些差异的。色彩标准的例子有下面的几种:

  1. Rec. 709:一种电视标准
  2. Rec. 1361:709 的扩展
  3. SMPTE 240M:第一代影像电视标准
  4. sRGB:PC 端的标准

sRGB 假定 Display gamma 是 2.5,它设计 Encode gamma 是 0.45 也就是 1/2.2,这样会使得最终的 System gamma 是 1.125 左右,也就是略高于 1,原因也在上面提到过,为了更加适应我们的主观感受。

End

到这里就差不多了,但是 Gamma 延伸出来还有更多的广义概念,比如说 GTM、LTM 等等,这些也是 ISP 中比较常见的概念,这些就放在后面的文章里面去介绍了。写到这里发现还有一个缺漏,那就是 Gamma 怎么应用到实际的编码实现或者硬件实现当中来呢?仔细想想这个还是不打算写了,基本原理知道,网络上也有很多代码实现参考示例,但是具体到某一家 ISP 厂商可能也有更加多样化的实现,所以还是重原理部分(着实是代码示例写起来也挺繁琐的)。不过想要直观感受一些 Gamma 的实例,可以参考这里:https://www.cambridgeincolour.com/tutorials/gamma-correction.htm 这个给了好几个可以对比的 Gamma 矫正示例,我觉得比编代码看的直观多了。

Camera ISP - Gamma 矫正相关推荐

  1. ISP中gamma矫正模块的FPGA设计和仿真

    pangpang最近耗费很久的时间写了一个ISP中的gamma矫正模块,写下本文记录一下. 目录 1.gamma矫正介绍 2.本文gamma矫正设计要求 3.设计过程 4.仿真验证 5.总结 1.ga ...

  2. ISP算法:gamma矫正

    Gamma矫正:在视频系统,线性光Intensity通过Gamma校正转换为非线性的视频信号,通常在摄像过程内完成. 矫正原因:

  3. Camera ISP技术

    Camera ISP技术 ISP图像信号处理 • 1,ISP图像信号处理介绍 • 2,ISP的目的是什么? • 3, ISP的处理流程以及算法 o 3.1镜头的几何变形 o 3.2 镜头渐晕 o 3. ...

  4. 【高通SDM660平台 Android 10.0】(14) --- Camera ISP

    [高通SDM660平台 Android 10.0]--- Camera ISP 一.Camera ISP 与 DSP 区别 1.1 名词解释 1.2 功能解释 1.3 手机摄像头ISP是独立好还是内置 ...

  5. 从FPGA到camera ISP漫谈

    从FPGA到camera ISP漫谈 参考文献链接 https://mp.weixin.qq.com/s/RBzN1Ii3_AH3omljmAByNA https://mp.weixin.qq.com ...

  6. gamma矫正/gamma映射

    what is the gamma? CCD.CMOS成像方式是通过像点中的"硅"感受光线的强弱而获得画面.而硅感光是物理成像,它真实地反应光线强度的变化,来多少就输出多少,因此它 ...

  7. ISP——Gamma Correction

    现象 上图是百度上找的一张图,是电子发烧友网站的网友的,如果发现侵权了请告知.觉得这张图能很好的看出不同gamma曲线带给人的直观感受的变化.从上往下看左侧黑色块黑得越来越严重,对比度也在逐渐加深.但 ...

  8. IP SOC与Camera ISP

    IP SOC与Camera ISP FH8858V200: 新一代8M高性能网络摄像机 SoC FH8858V200是新一代面向8M专业型网络摄像机应用的高性能H.265/H.264/JPEG SoC ...

  9. 图像处理——gamma矫正

    gamma矫正的目的 (Gamma Correction,伽玛校正):在电视和图形监视器中,显像管发生的电子束及其生成的图像亮度并不是随显像管的输入电压线性变化,电子流与输入电压相比是按照指数曲线变化 ...

最新文章

  1. 仟叶学校:武汉老师最燃演讲“人生很贵,请别浪费”
  2. Replation requires the actual server name ... Replication.Utilies
  3. CSV文件读取和处理
  4. '0','\0',NULL,EOF的区别
  5. Mathcad Prime 下载 安装 与 和谐教程
  6. 新增方法java_Java8_map新增方法
  7. Python内置数据类型之Tuple
  8. 【loj6191】「美团 CodeM 复赛」配对游戏 概率期望dp
  9. SpringBoot Scheduled Cron表达式范例记录
  10. 基于echarts实现非常规可视化图表
  11. Normalize异常报错
  12. Python爬取拉钩招聘网,告诉你:这类程序员最赚钱!
  13. web服务器与APP服务器
  14. flowchart.js使用总结
  15. Win10自带的SSH服务 scp功能传输文件(linux)
  16. 2.天猫商品数据爬虫(已模拟登录)
  17. Mockito 之 verify 使用方法
  18. 分享电影《了不起的老爸》
  19. 正态分布 概率密度函数PDF
  20. 深度学习——手写数字识别底层实现

热门文章

  1. 华为全栈人工智能-AI发展新趋势
  2. E20-021 考试培训
  3. 前端入门学习笔记六十五
  4. 【计算机系统】 信息在计算机中的表示和内存地址与空间的简单理解
  5. UE 宏定义GENERATED_BODY
  6. 华为手机出货量大跌,零售门店关闭潮或将出现
  7. 怎么打开.net服务器文件,asp.net 打开服务器文件
  8. 数据结构 排序 思考题1
  9. 谁说学生模型就得小?稀疏化DAN模型推理可提速600倍
  10. register寄存器变量