4、Gamma校正与色调映射:Gamma曲线原理、全局与局部Gamma校正、基于Y域的色调映射
各位好,欢迎来到第四章。这一章我们聊聊Gamma校正和色调映射。这两个概念在图像处理里太常见了,但很多人只是调个参数就完事。我个人习惯是,先搞清楚它为什么存在,再去想怎么用。
Gamma校正最早是为了适配CRT显示器的非线性响应。但后来我们发现,它在视觉感知上也有妙用——人眼对暗部细节的敏感度远高于亮部。说白了,Gamma曲线就是在做一件事:重新分配像素的亮度权重。
4.1 Gamma曲线原理
Gamma曲线的数学形式很简单:
V_out = A * V_in ^ γ
其中A是增益系数,γ是Gamma值。当γ < 1时,曲线向上凸,暗部被拉伸;当γ > 1时,曲线向下凹,亮部被压缩。
我刚开始做低光照增强时,总想着把暗部提亮,结果噪声也跟着放大了。后来才意识到,Gamma校正不是万能药,它只是改变了像素的分布方式。
| γ值 | 效果 | 典型应用场景 |
|---|---|---|
| γ < 1 | 暗部拉伸,亮部压缩 | 低光照图像增强、暗部细节恢复 |
| γ = 1 | 线性映射,无变化 | 标准显示、色彩校准 |
| γ > 1 | 暗部压缩,亮部拉伸 | 过曝图像校正、高动态范围压缩 |
4.2 全局Gamma校正
全局Gamma校正就是对整幅图像使用同一个γ值。实现起来很简单,但问题也很明显:一刀切。
你想想看,一张夜景照片,路灯区域已经很亮了,暗部区域却黑成一片。如果用一个γ < 1的曲线去提亮暗部,路灯区域就会过曝。反过来,如果为了保住亮部细节而用γ > 1,暗部就更看不清了。
全局Gamma的适用场景很有限:
- 图像整体偏暗或整体偏亮
- 亮度分布比较均匀
- 对局部细节要求不高
我在做监控摄像头ISP调试时,遇到过一种情况:白天场景用全局Gamma效果还行,但到了傍晚,画面里既有天空的余晖,又有建筑物的阴影,全局Gamma就完全不够用了。这时候就需要局部Gamma校正。
4.3 局部Gamma校正
局部Gamma校正的核心思想是:不同区域用不同的γ值。暗区用较小的γ来提亮,亮区用较大的γ来压缩,中间区域保持线性。
实现方式通常有两种:
- 基于亮度分量的自适应Gamma:根据每个像素的亮度值动态计算γ
- 基于图像分块的Gamma:将图像分成若干块,每块独立计算最优γ
我比较推荐第一种方式,因为它更平滑,不会出现块效应。具体做法是:
// 自适应Gamma计算伪代码
for each pixel (x, y) in Y channel:
// 计算局部亮度均值
local_mean = box_filter(Y, x, y, radius)
// 根据均值动态调整Gamma
gamma = 1.0 + alpha * (0.5 - local_mean)
// 应用Gamma校正
Y_out(x, y) = pow(Y_in(x, y), gamma)
这里的alpha是调节强度,一般取0.3~0.6。local_mean归一化到[0,1]区间。
4.4 基于Y域的色调映射
色调映射(Tone Mapping)原本是为HDR图像设计的,目的是把高动态范围压缩到低动态范围显示设备上。但在低光照增强中,我们反过来用——把暗部细节映射到可见范围。
在YUV域做色调映射,只处理Y通道,U/V通道保持不变。这样做的好处是:保持色相不变,只调整亮度。
常用的色调映射算子有:
| 算子名称 | 公式 | 特点 |
|---|---|---|
| Reinhard全局算子 | L_out = L_in / (1 + L_in) | 简单高效,适合实时处理 |
| Drago对数算子 | L_out = log(1 + L_in) / log(1 + L_max) | 暗部细节保留好,但亮部压缩过度 |
| 自适应对数映射 | L_out = log(1 + L_in * bias) / log(1 + L_max * bias) | 可调节性强,适合低光照场景 |
我个人在低光照增强项目中用得最多的是自适应对数映射。它有一个bias参数,可以控制暗部拉伸的程度。bias越大,暗部提亮越明显,但噪声也会被放大。
4.5 本章知识体系
下面这张图展示了Gamma校正与色调映射的核心逻辑关系:
4.6 避坑指南
做Gamma校正和色调映射时,有几个坑我踩过,分享给大家:
- 不要直接对RGB做Gamma:三个通道独立做Gamma会导致色相偏移。一定要在YUV域操作。
- 注意Gamma值的范围:γ=0.3以下暗部噪声会爆炸,γ=3.0以上亮部细节全丢。我一般控制在0.4~2.5之间。
- 色调映射后要重新归一化:映射后的Y值可能超出[0,255]范围,需要做一次线性拉伸或裁剪。
- 局部Gamma的半径选择:半径太小会有光晕,半径太大退化为全局Gamma。我习惯取图像短边的1/8~1/4。