3、立体匹配原理:对极几何、视差计算、代价聚合、视差优化
好,咱们今天聊聊立体匹配。这名字听着挺唬人,说白了就是——让计算机学会“两只眼睛”看世界。
我刚开始接触这个领域的时候,总觉得这东西玄乎。后来自己动手做项目,踩了不少坑,才慢慢摸清楚门道。今天我就把这几块核心内容掰开揉碎了讲给你听。
3.1 对极几何:两张图之间的“约束关系”
先问个问题:给你左右两张图,你怎么知道左边图里的一个点,在右边图里对应哪个位置?
最笨的办法是整张图搜。但那样太慢了,而且容易出错。对极几何就是来解决这个问题的——它告诉我们:左图的一个点,在右图里只可能出现在一条线上。
这条线叫“极线”。
为什么会这样?你想想看,两个相机拍同一个物体,物体在左图成像的位置,和它在右图成像的位置,其实受相机相对位置约束。这个约束关系,就是基础矩阵 F 和本质矩阵 E 来描述的。
核心公式:
x₂ᵀ · F · x₁ = 0
其中 x₁ 是左图点坐标,x₂ 是右图对应点坐标,F 是基础矩阵。
我在项目中遇到过一件事:有一次标定没做好,基础矩阵算出来全是错的。结果极线画出来歪七扭八,匹配结果自然一塌糊涂。所以啊,对极几何的前提是相机标定要准,这个坑我替你们踩过了。
3.2 视差计算:深度信息的“直接来源”
有了极线约束,我们就可以沿着极线找匹配点了。找到匹配点之后,左右图坐标的差值,就是“视差”。
视差和深度是什么关系?简单说:视差越大,物体离你越近;视差越小,物体离你越远。
公式长这样:
深度 Z = (焦距 f × 基线距离 B) / 视差 d
嗯,这里要注意:基线距离是两个相机光心之间的距离。这个值越大,深度分辨率越高,但相机也得摆得够开。
我个人习惯在项目初期先用小基线试试,等算法稳定了再加大基线。为什么呢?因为基线大了,遮挡问题会更严重,后面处理起来麻烦。
3.3 代价聚合:别让“噪声”毁了你的视差图
直接算出来的视差,往往噪声很大。为什么?因为像素级别的匹配太敏感了——光照变化、纹理重复、弱纹理区域,都会让匹配出错。
代价聚合就是来解决这个问题的。它的思路是:别只看一个像素,看看它周围的邻居怎么说。
常用的方法有:
- 局部聚合:用窗口滤波,比如 Box Filter、Guided Filter
- 半全局聚合:SGM(Semi-Global Matching)算法,沿着多个方向做动态规划
- 全局聚合:用图割(Graph Cut)或置信度传播(Belief Propagation)
我曾经在做一个室内深度图项目时,直接用局部聚合,结果纹理少的墙面全是洞。后来换成 SGM,效果好了不止一个档次。说白了,代价聚合就是在“精度”和“速度”之间找平衡。
我的小技巧:如果场景纹理丰富,局部聚合就够了。如果场景有大片平坦区域(比如白墙、地面),建议用 SGM 或全局方法。
3.4 视差优化:让结果“更干净”
代价聚合之后,我们得到了一个“初始视差图”。但别高兴太早,这图里还有不少问题:
- 遮挡区域的视差是错的
- 边缘处可能有锯齿
- 弱纹理区域可能是一团糊
视差优化就是来做“善后工作”的。常用的手段包括:
- 左右一致性检查:左图到右图的视差,和右图到左图的视差,应该一致。不一致的地方,就是遮挡或错误区域,直接标记为无效。
- 亚像素插值:视差通常是整数,但真实世界是连续的。用二次曲线拟合,可以得到亚像素精度的视差。
- 中值滤波/双边滤波:去掉孤立的噪声点,同时保留边缘。
- 区域生长/填充:对无效区域,用周围有效视差来填充。
注意:视差优化不是越多越好。过度平滑会丢失细节,过度填充会引入假深度。我一般会保留一部分“不确定区域”,宁可让它空着,也不要给错误数据。
知识体系总览
下面这张图,是我自己整理的立体匹配核心流程。你看一眼,心里就有谱了:
总结一下
立体匹配这四步,每一步都有讲究:
| 步骤 | 核心任务 | 常见方法 | 我的经验 |
|---|---|---|---|
| 对极几何 | 确定搜索范围 | 基础矩阵、极线校正 | 标定不准,后面全白搭 |
| 视差计算 | 找到匹配点 | SAD、NCC、Census | 光照变化大时用Census |
| 代价聚合 | 抑制噪声 | Box Filter、SGM | 平坦区域用SGM效果好 |
| 视差优化 | 提升质量 | 一致性检查、亚像素 | 别过度平滑,保留细节 |
嗯,今天就先聊到这儿。这四步你捋顺了,立体匹配的基本功就算打扎实了。后面咱们再深入讲每一步的代码实现和调参技巧。