2、经典夜景增强算法回顾
说到夜景增强,很多同学第一反应就是“拉亮度”。其实没那么简单。夜景图像的问题不只是暗,还有对比度低、细节被噪声淹没、颜色偏色。我早期做移动端相机算法时,踩过不少坑,今天就把几个经典方法掰开揉碎讲清楚。
2.1 直方图均衡化(HE)
直方图均衡化,说白了就是把像素值的分布“拉平”。让暗的地方变亮,亮的地方稍微压一压,整张图的对比度就上来了。
它的数学原理很简单:对灰度图像,计算累积分布函数(CDF),然后做映射。
// 伪代码:直方图均衡化核心
for each pixel (x, y):
gray = image[x][y]
new_gray = CDF[gray] * 255 / total_pixels
效果呢?对于整体偏暗但光照均匀的夜景,确实能提亮。但我遇到过一个问题——如果画面里有大面积天空或黑色区域,HE 会把噪声也放大,结果就是“噪点满天飞”。
2.2 自适应直方图均衡化(AHE / CLAHE)
为了解决 HE 的局部失效问题,AHE 出现了。它的思路是:把图像分成小块,每块单独做直方图均衡化。
但 AHE 有个致命伤——噪声放大更严重。尤其是平坦区域(比如夜空),本来噪声就不小,AHE 一搞,块与块之间的边界还会出现“块效应”。
于是 CLAHE(对比度限制自适应直方图均衡化)来了。它加了一个“裁剪”操作:
- 设定一个对比度上限(clip limit)
- 超过上限的像素被裁剪,重新分布到整个直方图
- 最后用双线性插值消除块边界
| 方法 | 优点 | 缺点 |
|---|---|---|
| HE | 简单、速度快 | 局部失效、噪声放大 |
| AHE | 局部对比度提升好 | 块效应、噪声严重 |
| CLAHE | 抑制噪声、无块效应 | 参数调优麻烦 |
2.3 Retinex 理论及其变体
Retinex 理论认为,人眼感知的亮度 = 光照分量 × 反射分量。说白了,图像可以分解成“光照”和“物体本色”。
公式很简单:
I(x, y) = L(x, y) * R(x, y)
其中:
I = 观测图像
L = 光照分量(低频)
R = 反射分量(高频,包含细节)
我们想要增强夜景,就是去掉不均匀的光照,保留或增强反射细节。
2.3.1 单尺度 Retinex(SSR)
SSR 用高斯滤波估计光照分量,然后做对数域减法:
log(R) = log(I) - log(G * I)
其中 G 是高斯核,* 表示卷积
效果嘛,确实能去雾、提亮。但有个问题——高斯核的尺度 σ 很难选。σ 太小,细节保留好但颜色失真;σ 太大,颜色自然但细节模糊。
2.3.2 多尺度 Retinex(MSR)
MSR 就是多个 SSR 加权求和。用多个不同尺度的高斯核,取长补短。
R_msr = Σ w_i * [log(I) - log(G_i * I)]
其中 w_i 是权重,通常各取 1/3
我当年在项目里试过 MSR,效果确实比 SSR 好。但计算量也上来了——三个尺度的卷积,在移动端跑 1080p 图像,帧率直接掉到个位数。
2.3.3 MSRCR(带色彩恢复的 MSR)
MSR 有个毛病:颜色饱和度下降。MSRCR 加了一个色彩恢复因子 C,把颜色拉回来。
R_msrcr = C(x, y) * R_msr(x, y)
C(x, y) = β * log(α * I_i(x, y) / Σ I_j(x, y))
嗯,这里要注意:α 和 β 的取值很敏感。我调过一组参数,α=125,β=46,效果还行。但不同场景下,参数得重新调。
2.4 知识结构图
下面这张图把经典夜景增强算法的演进关系画清楚了:
2.5 避坑指南
我这些年做夜景算法,踩过几个大坑,分享给你:
- 别在 RGB 三个通道上直接做 HE——颜色会完全乱掉。我建议只在 YUV 的 Y 通道上做,或者转 HSV 后在 V 通道上做。
- CLAHE 的 tile size 别设太小——8×8 的 tile 在 1080p 图像上会产生上千个块,计算量爆炸。我一般用 64×64。
- Retinex 的高斯核半径——SSR 的 σ 取 80~120 比较稳。太小了细节全是噪声,太大了跟没做一样。
- 移动端实时性——MSR 三个尺度卷积,在 GPU 上可以用分离滤波加速。我曾在 Adreno 640 上把 720p 的 MSR 优化到 30fps。
好了,经典方法就回顾到这里。这些方法虽然老,但思想很扎实。后面讲硬件加速时,你会发现很多优化思路都是从这些基础方法里长出来的。