传统去雾算法优化:从对比度增强论文到工程复现
编写时间:2023-02
围绕传统去雾算法的一次学习和复现,核心参考是 《Optimized Contrast Enhancement for Real-time Image and Video Dehazing》(Kim et al., JVCIR 2013, 项目页)这条路线。它不是端到端学习,而是从大气散射模型出发,估计大气光 A 和透射率 t(x),再反推出无雾图 J(x)。
雾退化模型
雾图可以写成:
其中 x 是像素坐标,c 是 RGB 通道,I 是观察到的有雾图,J 是待恢复的无雾图,A 是大气光,t(x) 是透射率。物理上常写作:
r 是大气散射系数,d(x) 是场景深度。透射率越小,说明雾越浓、距离越远、原始信息保留越少。
如果 A 和 t 已知,可以反推:
这一步本质是线性映射:
因此 J 和 I 的关系不是随意增强,而是一条斜率为 1/t 的直线。t 越小,斜率越大,对比度越强;但同样也越容易把像素拉到 [0,255] 之外。
大气光 A 的估计
传统暗通道思路常会先找灰度较高、颜色接近白色的像素,再取其中亮度最高的点作为大气光。但真实图像里有白车、白墙、天空、过曝灯牌等干扰;它们很亮,却不一定代表雾层大气光。
论文里使用四叉树递归:雾最厚的区域通常亮度高、区域内部变化小,所以每个区域可以用“亮但均匀”来打分:
mu(R) 越高,区域越可能接近大气光;sigma(R) 越小,区域越均匀,越不像包含丰富纹理的近景物体。递归过程是:
这个策略不是单纯追求最亮,而是避免把有纹理的白色物体误当成 A。它也为后面的优化埋下一个点:每层递归都需要区域均值和标准差,后续可以用积分图把这类统计加速。
透射率 t 的搜索
t 不能直接从单张图精确解出。论文把它改写成局部搜索问题:对一个局部块 B,尝试一组候选 t_B,把它代入反推公式,得到候选无雾块 J_B,再用代价函数评价。
这个代价由两部分组成:
| 代价项 | 目标 | 直觉 |
|---|---|---|
E_loss | 避免像素反推后越界 | t 太小会把暗部拉到 0 以下,把亮部拉到 255 以上 |
E_contrast | 增强局部对比度 | t 越小,1/t 越大,局部方差越大 |
总目标可以写成:
这里的 lambda 是平衡项:lambda 越大,算法越保守,更怕失真;lambda 越小,算法越激进,更愿意拉对比度。
为什么会越界
反推式的输入输出关系是一条直线。对单个通道:
当 J_c=0 时,输入阈值为:
当 J_c=255 时,输入阈值为:
所以只有 I_c \in [\alpha_c,\beta_c] 时,反推出的 J_c 才落在 [0,255]。如果 I_c < alpha_c,输出会低于 0;如果 I_c > beta_c,输出会高于 255。
去雾反推的线性映射与截断区间
越界损失可以写成:
这项只惩罚出界部分:落在 [0,255] 内的像素不增加损失。
对比度项
对比度增强可以用局部方差理解。论文讨论过 MSE、Michelson、Weber 等形式,工程上选择更容易处理的 MSE/方差形式:
因为 J 是 I 的线性变换:
所以 t_B 越小,局部方差越大,E_contrast 越小。也就是说,对比度项天然倾向于选择更小的 t,而损失项会阻止 t 小到造成大量截断。
| 选择 | 对比度 | 越界风险 | 视觉结果 |
|---|---|---|---|
t 较大 | 弱 | 小 | 去雾不足、残雾 |
t 较小 | 强 | 大 | 过曝、色块、黑白截断 |
E_contrast + lambda E_loss | 平衡 | 可控 | 尽量增强但不大量失真 |
候选 t_B 还可以先给一个下界,避免明显必然越界的取值。由 J_c \in [0,255] 可得到类似约束:
实际实现时只需要把不合理的候选 t_B 排除,再在 [t_{min},1] 内寻找代价最小值。
块级 t 的问题
块级搜索的优点是快。假设一个块内的 t 相同,就不用为每个像素单独求解;但这个假设也很脆弱。t(x)=e^{-rd(x)} 里真正变化的是深度 d(x),一个固定块里可能同时有近处栏杆、远处建筑和天空,单个 t_B 无法同时照顾它们。
为了减轻固定网格带来的块效应,可以让窗口围绕每个像素滑动:对像素 x,在所有覆盖它的候选块中选择代价更合适的块,而不是只用固定分块的结果。直觉上相当于让每个像素从周围局部块里取一个更稳的 t。
这里也能看出后续工程优化的方向:如果对每个像素都枚举覆盖它的块,计算量会上去;区域统计、局部方差和代价搜索都需要更高效的实现。
Guided Filter 细化透射图
块级 t 只是一张粗透射图,还需要细化到边缘。Guided Filter 的假设是,在局部窗口 omega_k 内,输出透射率可以表示成引导图 I 的线性组合:
s_k 是向量,phi_k 是偏移量,在同一个窗口内保持不变。通过最小二乘求:
这样做的意义是:用原图边缘引导透射图边缘。平坦区域继续平滑,物体边界处尽量保留深度突变。
视频去雾的时间一致性
单帧去雾直接逐帧应用到视频上,会遇到时间一致性问题:每帧单独估计 A 和 t,输出就可能闪。为了降低复杂度,可以把 RGB 转到 YUV,先用 Y 通道估计透射率,U/V 通道不参与核心估计。
一个简化假设是:相邻帧同一场景点的原始辐射值在短时间内近似不变:
如果大气光 A_Y 在短片段内也近似稳定,则有:
其中:
这个关系只在“同一场景点”上成立。真实视频里有运动、遮挡、相机抖动和压缩噪声,所以不能硬约束每个像素。更稳的做法是用相邻帧差异构造一个权重:
差异越小,权重越大,说明这个位置更可能可以使用时间一致性;差异越大,权重越小,减少错误约束。块级权重可写成:
于是可以把时间一致性写成额外代价:
总目标变成:
这条视频扩展思路的价值在于提醒:视频去雾不能只看单帧对比度。大气光稳定性、透射率连续性、运动区域的错误约束,都需要被单独考虑。
可落地的算法链路
这条路线的优点是可解释、可控、能做实时优化;缺点是对 A、t 的估计都很敏感。优化基本围绕两个点:一是别把 A 当作简单的全局最亮点,二是别让递归区域统计和局部代价搜索重复计算。