3、运动补偿模型:全局运动模型(仿射/透视)、局部运动模型(光流法)、混合模型
运动补偿,说白了就是解决「怎么把两张图对齐」的问题。你拍多帧 HDR 时,手一抖、风一吹、人一动,像素就全跑偏了。不对齐就融合,鬼影能让你怀疑人生。
我这些年做运动补偿,最深的体会是:没有万能的模型,只有合适的场景。全局运动、局部运动、混合模型,各有各的脾气。咱们一个一个拆开聊。
3.1 全局运动模型:仿射与透视
全局运动模型假设:整张图的运动可以用一个统一的变换来描述。说白了,就是认为画面里所有像素都跟着同一个「大动作」在动——比如相机平移、旋转、缩放。
3.1.1 仿射变换(6 参数)
仿射变换是我用得最多的全局模型。它用 6 个参数描述运动:
// 仿射变换矩阵形式
[x'] [a b tx] [x]
[y'] = [c d ty] [y]
[1 ] [0 0 1 ] [1]
// 展开后:
x' = a*x + b*y + tx
y' = c*x + d*y + ty
这 6 个参数能干什么?平移、旋转、缩放、剪切,全包了。但注意,它不能处理透视变形——平行线变换后还是平行线。
3.1.2 透视变换(8 参数)
透视变换比仿射多 2 个参数,能处理「近大远小」的透视效果:
// 透视变换矩阵形式
[x'] [a b c] [x]
[y'] = [d e f] [y]
[w ] [g h 1] [1]
// 实际坐标:
x'' = x' / w
y'' = y' / w
多了 g 和 h 两个参数,就能描述「梯形变形」了。比如你拍一个广告牌,从侧面拍过去,透视变换能把歪的拉正。
3.1.3 全局模型的参数估计
怎么算这些参数?最经典的方法是 Lucas-Kanade 光流 + RANSAC。流程大致是:
- 提取特征点(比如 ORB、SIFT)
- 用光流法跟踪特征点
- 用 RANSAC 剔除离群点
- 用最小二乘法拟合变换矩阵
嗯,这里要注意:RANSAC 的阈值设置很关键。设得太小,好点被误杀;设得太大,坏点混进来。我一般从 3 个像素开始试,根据图像分辨率微调。
3.2 局部运动模型:光流法
全局模型搞不定「局部运动」——比如人走路、树叶摇晃、汽车开过。这时候就得请出光流法了。
光流法的核心假设:同一个像素点在相邻帧之间,亮度不变。数学表达就是:
I(x, y, t) = I(x+dx, y+dy, t+dt)
展开泰勒级数,忽略高阶项,得到光流约束方程:
Ix * u + Iy * v + It = 0
其中:
Ix = ∂I/∂x, Iy = ∂I/∂y (空间梯度)
It = ∂I/∂t (时间梯度)
u = dx/dt, v = dy/dt (光流向量)
一个方程两个未知数,解不了。所以需要加约束——这就是各种光流法的区别所在。
3.2.1 稀疏光流 vs 稠密光流
| 类型 | 代表算法 | 输出 | 速度 | 适用场景 |
|---|---|---|---|---|
| 稀疏光流 | Lucas-Kanade | 仅特征点处的运动向量 | 快 | 特征跟踪、全局模型初始化 |
| 稠密光流 | Farneback、RAFT、FlowNet | 每个像素的运动向量 | 慢 | 逐像素运动补偿、视频插帧 |
我个人习惯:先跑稀疏光流做全局对齐,再用稠密光流做局部修正。这样又快又准。
3.2.2 光流法的常见问题
- 大运动失效:光流法假设运动很小,运动太大时金字塔层数不够就崩了。我一般用 3-5 层金字塔,每层缩小 2 倍。
- 纹理缺失:纯色区域没有梯度,光流算不出来。这时候需要结合全局模型做先验。
- 遮挡问题:物体移开后露出的背景,光流是「无中生有」的。需要做双向光流 + 一致性检查。
3.3 混合模型:全局 + 局部
实战中,我几乎不用纯全局或纯局部模型。原因很简单:真实场景既有相机运动,又有物体运动。混合模型才是王道。
3.3.1 混合策略
我常用的混合流程:
- 全局对齐:用仿射或透视模型,把参考帧和待对齐帧做粗对齐
- 残差计算:对齐后的两帧做差,得到残差图
- 局部修正:在残差大的区域,用稠密光流做精细对齐
- 融合权重:根据运动置信度,决定用全局结果还是局部结果
3.3.2 混合模型的实现框架
下面是我在一个项目里用过的混合模型框架,用伪代码表示:
def hybrid_motion_compensation(ref_img, target_img):
# 1. 全局对齐
global_params = estimate_affine(ref_img, target_img)
warped_global = warp_affine(target_img, global_params)
# 2. 计算残差
residual = abs(ref_img - warped_global)
# 3. 生成运动掩码
motion_mask = residual > threshold # 阈值自适应
# 4. 局部修正(只在运动区域)
if motion_mask.sum() > 0:
local_flow = compute_dense_flow(ref_img, warped_global)
warped_local = warp_flow(warped_global, local_flow)
else:
warped_local = warped_global
# 5. 融合
result = blend_global_local(warped_global, warped_local, motion_mask)
return result
3.4 三种模型的对比与选择
| 模型 | 参数数量 | 计算量 | 运动类型 | 典型场景 |
|---|---|---|---|---|
| 仿射变换 | 6 | 低 | 旋转、平移、缩放 | 手持相机、三脚架微动 |
| 透视变换 | 8 | 中 | 透视变形 | 广角镜头、倾斜拍摄 |
| 稠密光流 | 2×像素数 | 高 | 任意局部运动 | 人物走动、树叶摇晃 |
| 混合模型 | 全局+局部 | 中高 | 全局+局部 | 大多数真实场景 |
你想想看,如果场景里只有相机在动,仿射模型就够了。但如果画面里有人在挥手,光靠全局模型肯定不行。反过来,如果相机在剧烈抖动,光流法又容易算飞。所以混合模型才是工程上的最优解。
3.5 本章知识体系
下面这张图总结了三种运动补偿模型的关系和适用场景: