066、宽动态范围(HDR)多帧融合:曝光序列合成、鬼影抑制与运动补偿
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帧周期比如33ms30fps。但问题来了——如果长帧曝光时间本身就有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多帧融合上踩过的坑和总结的经验。下次遇到鬼影别急着调算法参数先看看你的曝光时序是不是“先天不足”。