2、经典夜景增强算法回顾

说到夜景增强,很多同学第一反应就是“拉亮度”。其实没那么简单。夜景图像的问题不只是暗,还有对比度低、细节被噪声淹没、颜色偏色。我早期做移动端相机算法时,踩过不少坑,今天就把几个经典方法掰开揉碎讲清楚。

2.1 直方图均衡化(HE)

直方图均衡化,说白了就是把像素值的分布“拉平”。让暗的地方变亮,亮的地方稍微压一压,整张图的对比度就上来了。

它的数学原理很简单:对灰度图像,计算累积分布函数(CDF),然后做映射。

// 伪代码:直方图均衡化核心
for each pixel (x, y):
    gray = image[x][y]
    new_gray = CDF[gray] * 255 / total_pixels

效果呢?对于整体偏暗但光照均匀的夜景,确实能提亮。但我遇到过一个问题——如果画面里有大面积天空或黑色区域,HE 会把噪声也放大,结果就是“噪点满天飞”。

注意:HE 对局部过暗或过亮的区域处理很差。比如夜景里有一盏路灯,灯周围会过曝成一片白,暗部细节反而丢失。

2.2 自适应直方图均衡化(AHE / CLAHE)

为了解决 HE 的局部失效问题,AHE 出现了。它的思路是:把图像分成小块,每块单独做直方图均衡化。

但 AHE 有个致命伤——噪声放大更严重。尤其是平坦区域(比如夜空),本来噪声就不小,AHE 一搞,块与块之间的边界还会出现“块效应”。

于是 CLAHE(对比度限制自适应直方图均衡化)来了。它加了一个“裁剪”操作:

  • 设定一个对比度上限(clip limit)
  • 超过上限的像素被裁剪,重新分布到整个直方图
  • 最后用双线性插值消除块边界
方法 优点 缺点
HE 简单、速度快 局部失效、噪声放大
AHE 局部对比度提升好 块效应、噪声严重
CLAHE 抑制噪声、无块效应 参数调优麻烦
我的经验:CLAHE 的 clip limit 一般设在 2~4 之间。我曾经在骁龙 835 平台上调这个参数,调了一周才找到平衡点——既要夜景细节,又不能把 ISO 800 以上的噪点全炸出来。

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 是高斯核,* 表示卷积

效果嘛,确实能去雾、提亮。但有个问题——高斯核的尺度 σ 很难选。σ 太小,细节保留好但颜色失真;σ 太大,颜色自然但细节模糊。

关键点:SSR 对彩色图像处理时,通常只在亮度通道(如 Y 通道)上做,否则颜色会严重偏灰。

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 知识结构图

下面这张图把经典夜景增强算法的演进关系画清楚了:

经典夜景增强算法演进关系 经典夜景增强算法演进关系 原始夜景图像 直方图均衡化 (HE) 自适应 HE (AHE) CLAHE Retinex 理论 单尺度 SSR 多尺度 MSR MSRCR (色彩恢复) HE/AHE/CLAHE 侧重对比度 Retinex 侧重光照分离

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。
小技巧:如果你在移动端做夜景增强,可以试试“CLAHE + 轻度 Retinex”的组合。先用 CLAHE 提局部对比度,再用单尺度 Retinex 做光照校正。计算量可控,效果比单独用任何一个都好。

好了,经典方法就回顾到这里。这些方法虽然老,但思想很扎实。后面讲硬件加速时,你会发现很多优化思路都是从这些基础方法里长出来的。