066、宽动态范围(HDR)多帧融合:曝光序列合成、鬼影抑制与运动补偿
一、从一次车载夜视翻车说起
去年夏天,某Tier1的ADAS项目,客户反馈夜间会车时,对面大灯一亮,整个画面直接“炸”成一片白,然后暗区死黑,HDR完全没起作用。我拿到log一看,三帧曝光序列的融合权重分配出了问题——长帧的暗区细节被短帧的高亮区域“污染”了,鬼影和运动模糊混在一起,像打翻的调色盘。更糟的是,车在移动,路灯杆在画面里拖出了半米长的“尾巴”。
这种问题,在HDR多帧融合里太典型了。你以为是算法不行,其实根子在曝光序列的时序设计上。今天这篇笔记,就专门聊聊HDR多帧融合里最磨人的三个坑:曝光序列怎么排、鬼影怎么压、运动补偿怎么做。
二、曝光序列合成:不是简单“拍三张叠一起”
很多人以为HDR就是拍三张不同曝光的图,然后加权平均。这是典型的“学生作业”思维。实际工程里,曝光序列的帧间隔、曝光时间比、帧对齐精度,任何一个参数跑偏,都会让融合结果像“鬼片”。
2.1 曝光时间比:别迷信“1:4:16”
我见过不少方案直接套用1:4:16的曝光比,觉得这样动态范围最宽。但真实场景里,这个比例往往导致中间帧和长帧之间出现严重的运动模糊差异。比如拍一个快速移动的汽车,短帧(1x)能看清轮廓,中帧(4x)已经开始拖影,长帧(16x)直接糊成一团。融合时,短帧的清晰边缘和中帧的模糊边缘一叠加,鬼影就出来了。
我的做法:根据场景动态范围动态调整曝光比。比如在车载场景,车速超过60km/h时,把曝光比压缩到1:2:4,牺牲一点动态范围,换取运动一致性。安防监控这种静态场景,才敢放开到1:8:16。别死板,要“看菜下饭”。
2.2 帧间隔:被忽视的“隐形杀手”
曝光序列里,帧与帧之间的时间间隔(Frame Interval)比曝光时间本身更关键。很多SoC的ISP pipeline里,三帧HDR是连续抓拍的,帧间隔固定为1帧周期(比如33ms@30fps)。但问题来了——如果长帧曝光时间本身就有30ms,加上帧间隔33ms,第二帧和第三帧之间实际时间差接近66ms。这个时间差里,物体已经移动了很大距离,鬼影必然出现。
这里踩过坑:某次调试,发现鬼影总是出现在画面右侧,排查半天,发现是帧间隔设置导致长帧和短帧的采集时间点错位了半个帧周期。后来改成“短帧-中帧-长帧”的时序,让长帧紧挨着中帧,减少时间差。别小看这几十毫秒,在高速场景里就是生与死的区别。
2.3 合成权重:别用“一刀切”的线性权重
教科书里教的是“根据像素亮度分配权重”,但实际场景里,暗区噪声和亮区过曝的权重分配要更精细。我常用的策略是“三区权重法”:
- 暗区(<10%动态范围):完全信任长帧,短帧和中帧的权重设为0。因为短帧和中帧在暗区只有噪声,没有信号。
- 中间区(10%~70%):用中帧为主,长帧和短帧做边缘补偿。这里要小心,中帧的噪声水平决定了融合质量。
- 亮区(>70%):完全信任短帧,长帧和中帧的权重设为0。因为长帧和中帧在亮区已经饱和,只有短帧保留了细节。
别这样写:有些方案用高斯函数做平滑过渡,结果暗区和亮区的边界出现“光晕”。我更喜欢用分段线性函数,过渡区控制在5%的亮度范围内,边界清晰,不会出现“鬼影光环”。
三、鬼影抑制:从“检测”到“消除”的实战
鬼影是HDR多帧融合的“癌症”,一旦出现,基本无药可救。所以核心思路是“预防为主,治疗为辅”。
3.1 运动检测:别用全局运动向量
很多ISP用全局运动向量(Global Motion Vector)来做帧间对齐,但局部运动(比如行人挥手、树叶晃动)会被忽略。结果就是:背景对齐了,前景出现重影。
我的方案:分块运动检测。把画面分成16x16的块,每个块独立计算运动向量。然后做“运动置信度”打分——运动幅度大、纹理丰富的块,置信度高;平坦区域(比如天空、墙面)运动检测不可靠,直接跳过。这样能精准定位运动区域,避免误判。
3.2 鬼影消除:不是“抹掉”而是“替换”
检测到运动区域后,很多算法直接“抹掉”运动像素,用相邻帧插值。这是最蠢的做法——插值出来的像素是“假”的,边缘会模糊。
正确姿势:对运动区域,只使用单帧数据。比如检测到某块区域有运动,就只取短帧(因为短帧曝光时间短,运动模糊最小)的像素,放弃长帧和中帧。虽然动态范围会下降,但至少没有鬼影。这叫“两害相权取其轻”。
这里踩过坑:某次在安防场景,树叶被风吹动,运动检测把整棵树都标记为“运动区域”,结果融合后树变成了“剪纸”,边缘锐利但毫无层次。后来加了“运动区域膨胀”的逆操作——只对运动区域的核心部分做单帧替换,边缘部分保留多帧融合,过渡自然多了。
3.3 鬼影残留的“最后防线”
即使做了运动检测和替换,鬼影还是可能残留。这时候要用“后处理滤波器”。我常用的是一维中值滤波,沿着运动方向做3x3窗口的中值替换。注意:别用均值滤波,均值会模糊边缘,中值能保留细节同时消除鬼影。
别这样写:有些方案用双边滤波,参数调不好,要么鬼影没去掉,要么细节全没了。中值滤波简单粗暴,但有效。
四、运动补偿:从“对齐”到“预测”
运动补偿是HDR融合的“高阶玩法”,目的是让多帧图像在空间上对齐,消除运动带来的错位。
4.1 全局运动补偿:仿射变换就够了
对于相机自身运动(比如手持抖动、车载颠簸),用仿射变换(Affine Transform)就能搞定。计算两帧之间的仿射矩阵,然后对长帧和中帧做warp。注意:warp后的图像会有边缘裁剪,别把有效像素裁没了。
经验值:仿射矩阵的精度要控制在0.1像素以内,否则鬼影比不补偿还严重。用光流法(Optical Flow)做亚像素级匹配,别用SIFT这种特征点匹配,速度太慢,车载场景扛不住。
4.2 局部运动补偿:光流法的“轻量版”
对于局部运动(比如行人、车辆),用稠密光流(Dense Optical Flow)太耗资源。我常用的是“稀疏光流+插值”的方案:在运动区域提取角点特征,计算光流向量,然后用径向基函数(RBF)插值到全图。这样计算量只有稠密光流的1/10,效果在大多数场景下够用。
这里踩过坑:某次在夜间场景,光流计算出来的向量全是噪声,因为暗区没有纹理。后来加了“纹理置信度”门限——只有纹理强度超过阈值的区域才做光流计算,平坦区域直接用全局仿射矩阵。效果立竿见影。
4.3 运动预测:给融合“打提前量”
在车载场景,运动是连续的。如果能在当前帧融合时,预测下一帧的运动状态,就能提前调整曝光序列和融合权重。我做过一个“运动预测器”,用卡尔曼滤波对运动向量做预测,然后动态调整曝光比。比如预测到车辆即将进入隧道(亮度骤降),提前把长帧曝光时间拉长,避免融合时暗区细节丢失。
别这样写:有些方案用深度学习做运动预测,模型太大,推理延迟高,不适合实时系统。卡尔曼滤波轻量、稳定,够用了。
五、个人经验性建议
调试HDR,先看时序图:拿到一个HDR方案,第一件事不是调参数,而是画曝光序列的时序图。标出每一帧的曝光开始时间、结束时间、帧间隔。你会发现,80%的鬼影问题都出在时序上。
运动检测的“黄金阈值”:运动幅度阈值不要固定,要根据场景动态调整。车载场景,车速越快,阈值越高;安防场景,阈值要低,连树叶晃动都要检测到。我习惯用“运动幅度中位数”作为自适应阈值,简单有效。
别追求“完美HDR”:真实场景里,HDR的极限是12档动态范围(约120dB)。超过这个范围,要么鬼影,要么噪声。学会“妥协”——在动态范围和运动清晰度之间找平衡点。客户要的是“看得清”,不是“实验室数据”。
最后一条,也是最重要的:HDR融合的最终效果,70%取决于曝光序列的设计,20%取决于运动检测,10%取决于后处理。别把精力花在后处理上,那是“亡羊补牢”。把曝光序列调好了,鬼影自然少。
以上,就是我在HDR多帧融合上踩过的坑和总结的经验。下次遇到鬼影,别急着调算法参数,先看看你的曝光时序是不是“先天不足”。