OpenISP 模块拆解 · 第9讲:伽马校正 (GAC)
模块定位
GAC 在文档标题中写作伽马校正,代码类名为 GC,位于 model/gac.py。它通过 LUT 对像素值做非线性映射,用来把线性或近似线性的图像信号转换成更适合显示和人眼感知的亮度分布。
Gamma 通常位于 CCM 之后、颜色空间转换或后段增强附近。它是 tone pipeline 的关键一环。
背景原理
传感器输出大体与光照强度线性相关,但人眼对暗部变化更敏感,对亮部变化相对不敏感。如果直接显示线性 RGB,暗部会显得过暗,视觉层次不自然。Gamma 校正通过非线性曲线重新分配码值:
text
out = in^(1/gamma)
在 8-bit 显示链路中,Gamma 能把更多码值分配给暗部和中间调,提高主观观感。
openISP 实现
GC 输入参数:
img: RGB 或 YUV 图像。lut: 查找表。mode:rgb或yuv。
在 rgb 模式下,代码对 R/G/B 三个通道分别查同一张 LUT:
text
gc_img[y, x, c] = lut[img[y, x, c]]
gc_img[y, x, :] = gc_img[y, x, :] / 4
这里 LUT 输出再除以 4,说明 LUT 可能以更高精度存储,最后缩放回目标范围。
在 yuv 模式下,代码对 Y 使用 lut[0],对 U/V 使用 lut[1]。这意味着实现允许亮度和色度使用不同曲线。
LUT 的意义
Gamma 是非线性函数,逐像素实时计算幂函数成本较高。ISP 中常用 LUT:
- 速度快,硬件友好。
- 可表达任意 tone curve,不只标准幂函数。
- 可以用分段线性或高精度表控制暗部、亮部和中间调。
真实 ISP 中,Gamma LUT 往往和 tone mapping、contrast curve、display transform 等模块一起设计。
Gamma 与 Tone Mapping
Gamma 主要描述显示和感知相关的非线性编码;tone mapping 更广,常用于把高动态范围压缩到有限显示范围。两者可能都表现为曲线,但目的和设计自由度不同。
在简单 pipeline 中,一张 LUT 可能同时承担 Gamma 和基础 tone curve 的角色。
参数和调试
- 曲线抬暗部:暗部更亮,但噪声也更明显。
- 曲线压亮部:高光更柔和,但可能显得灰。
- 曲线太陡:对比强,但容易断阶。
- LUT 精度不足:可能出现 banding。
调试时建议看灰阶渐变、暗部细节、高光过渡和肤色。Gamma 曲线不应制造明显色偏,所以 RGB 模式下三通道通常使用一致曲线,除非有特殊色彩设计。
实现注意点
当前代码逐像素循环,逻辑直观但性能一般。LUT 索引要求输入值必须在 LUT 范围内,否则会越界。实际工程中通常会在查表前 clip 或保证前级输出范围。
rgb 模式最终除以 4,而 yuv 模式没有统一除以 4,这说明两种 LUT 的数值约定可能不同,使用时要确认配置。
学习重点
- Gamma 是非线性亮度编码,不是颜色矩阵。
- LUT 是 ISP 中实现非线性曲线的常见方式。
- Gamma 通常应在 CCM 后。
- 曲线会影响暗部噪声、亮部层次和 banding。
面试问答
Q1: Gamma 校正为什么需要?
为了匹配人眼和显示系统的非线性感知,把线性传感器信号映射成视觉上更自然的亮度分布。
Q2: Gamma 和 tone mapping 有什么区别?
Gamma 更偏显示编码和感知曲线,tone mapping 更偏动态范围压缩。工程中二者可能被合并到同一 LUT。
Q3: 为什么 ISP 常用 LUT 做 gamma?
查表速度快、硬件实现简单,还能表达任意曲线,比实时幂函数更适合逐像素处理。
Q4: gamma=0.5 会产生什么效果?
具体取决于公式约定。若 out = in^gamma,0.5 会抬亮暗部;若 out = in^(1/gamma),效果相反。讨论时必须先明确公式。
Q5: Gamma 放在 RGB 还是 YUV 做?
都可能。RGB Gamma 简单直接;YUV 中常对 Y 做亮度曲线,减少对色度的影响。具体取决于 pipeline 设计。