4. 局部运动检测:基于块匹配的局部运动检测算法
HDR 合成中最头疼的问题是什么?我个人觉得,运动伪影排第一。你想想看,三张不同曝光的图,场景里有人动了一下,或者树叶晃了晃,合成出来就是重影、鬼影,看着特别难受。
全局运动检测只能判断整张图有没有动,但实际场景里往往是局部在动。比如一个人站在背景前挥手,背景是静止的,只有手在动。这时候就需要局部运动检测上场了。
块匹配(Block Matching)是局部运动检测里最经典、最实用的方法之一。我在多个 HDR 项目里都用过它,效果稳定,实现也不复杂。
4.1 块匹配的基本思想
说白了,就是把图像切成一个个小方块,然后去另一张图里找最相似的方块。如果找到了,两个方块的位置差就是运动向量;如果找不到,说明这个区域可能有大运动或者遮挡。
举个例子:
- 把参考帧(通常是中曝光图)分成 8×8 或 16×16 的块
- 对每个块,在另一帧的搜索窗口内找最佳匹配
- 匹配程度用某种代价函数衡量
- 代价最小的位置就是匹配位置
核心公式:块匹配的代价函数通常用 SAD(差值和)或 SSD(平方差值和)。
SAD = Σ|I₁(i,j) - I₂(i+dx, j+dy)|
SSD = Σ(I₁(i,j) - I₂(i+dx, j+dy))²
其中 (dx, dy) 是候选运动向量,求和范围在块内所有像素。
我习惯用 SAD,因为它计算量小,而且对曝光差异不那么敏感。HDR 的多曝光图之间亮度差异很大,SSD 容易被大差异带偏。
4.2 搜索策略:怎么找得快又准
如果暴力搜索整个图像,那计算量太大了。实际工程中需要一些聪明的搜索策略。
| 搜索策略 | 原理 | 计算量 | 精度 |
|---|---|---|---|
| 全搜索 | 遍历搜索窗口内所有位置 | 极高 | 最高 |
| 三步搜索 | 从粗到细逐步缩小范围 | 低 | 较高 |
| 菱形搜索 | 用菱形模板迭代逼近 | 低 | 高 |
| 对数搜索 | 指数递减步长 | 低 | 中等 |
我在项目中用得最多的是菱形搜索。为什么?因为它能在精度和速度之间取得很好的平衡。三步搜索有时候会掉进局部最优,菱形搜索的收敛性更好。
我的经验:搜索窗口大小一般设为 ±16 像素就够了。HDR 的多帧之间曝光时间不同,运动幅度通常不会太大。如果设得太大,不仅慢,还容易匹配到错误位置。
4.3 运动向量的后处理
匹配完以后,直接拿原始运动向量用?不行。我踩过这个坑。原始匹配结果里有很多噪声和错误匹配,必须做后处理。
常用的后处理步骤:
- 中值滤波:对运动向量场做 3×3 或 5×5 的中值滤波,去除孤立异常值
- 一致性检查:正向匹配和反向匹配的结果应该一致,不一致的标记为不可靠
- 阈值截断:运动向量模长超过某个阈值的,认为是遮挡或大运动区域
注意:一致性检查非常重要。我曾经在一个项目中忽略了这一步,结果运动物体边缘出现了严重的撕裂伪影。正向匹配和反向匹配的差异超过 1 个像素就应该标记为不可靠。
4.4 块匹配的局限性
块匹配不是万能的。有几个场景它表现不好:
- 纹理贫乏区域:比如纯色墙壁,所有块看起来都一样,匹配结果随机
- 重复纹理:比如栅栏、砖墙,容易匹配到错误周期
- 非刚性运动:比如飘动的旗帜、流水,块内运动不一致
- 大曝光差异:过曝或欠曝区域,像素值被截断,匹配失效
对于纹理贫乏区域,我一般会结合梯度信息来做匹配。梯度大的区域权重高,梯度小的区域权重低。这样能有效减少误匹配。
4.5 块匹配在 HDR 中的特殊处理
HDR 的多曝光图有个特点:同一场景在不同曝光下的亮度差异很大。直接做块匹配会有问题。
我的做法是:
- 先做亮度归一化:用曝光时间比例把不同曝光的图像缩放到同一亮度水平
- 使用梯度图匹配:梯度对亮度变化不敏感,比原始像素更稳定
- 分区域处理:过曝和欠曝区域单独设置匹配参数
实用技巧:如果使用梯度图匹配,SAD 的公式变成:
SAD_grad = Σ|G₁(i,j) - G₂(i+dx, j+dy)|
其中 G = |∇I| = sqrt(Gx² + Gy²)
梯度图匹配对曝光差异的鲁棒性明显优于原始像素匹配。
4.6 块匹配的工程实现要点
最后分享几个工程实现中的细节:
- 块大小选择:8×8 适合细节丰富的区域,16×16 适合平滑区域。我通常用 8×8,配合重叠块策略
- 搜索步长:整数像素搜索就够了,亚像素精度对 HDR 合成提升不大
- 提前终止:如果 SAD 值小于某个阈值,直接认为匹配成功,不用继续搜索
- 多尺度策略:先降采样做粗匹配,再在原分辨率做精匹配,速度能提升 3-5 倍
嗯,块匹配这部分内容就这些。下一节我们会讨论如何把检测到的运动信息用到 HDR 合成中去。