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 视差优化:让结果“更干净”

代价聚合之后,我们得到了一个“初始视差图”。但别高兴太早,这图里还有不少问题:

  • 遮挡区域的视差是错的
  • 边缘处可能有锯齿
  • 弱纹理区域可能是一团糊

视差优化就是来做“善后工作”的。常用的手段包括:

  1. 左右一致性检查:左图到右图的视差,和右图到左图的视差,应该一致。不一致的地方,就是遮挡或错误区域,直接标记为无效。
  2. 亚像素插值:视差通常是整数,但真实世界是连续的。用二次曲线拟合,可以得到亚像素精度的视差。
  3. 中值滤波/双边滤波:去掉孤立的噪声点,同时保留边缘。
  4. 区域生长/填充:对无效区域,用周围有效视差来填充。
  5. 注意:视差优化不是越多越好。过度平滑会丢失细节,过度填充会引入假深度。我一般会保留一部分“不确定区域”,宁可让它空着,也不要给错误数据。

    知识体系总览

    下面这张图,是我自己整理的立体匹配核心流程。你看一眼,心里就有谱了:

    立体匹配核心流程 对极几何 极线约束、基础矩阵 视差计算 沿极线匹配、视差值 代价聚合 局部/SGM/全局 视差优化 一致性检查、滤波 最终深度图 高质量、低噪声 迭代优化

    总结一下

    立体匹配这四步,每一步都有讲究:

    步骤 核心任务 常见方法 我的经验
    对极几何 确定搜索范围 基础矩阵、极线校正 标定不准,后面全白搭
    视差计算 找到匹配点 SAD、NCC、Census 光照变化大时用Census
    代价聚合 抑制噪声 Box Filter、SGM 平坦区域用SGM效果好
    视差优化 提升质量 一致性检查、亚像素 别过度平滑,保留细节

    嗯,今天就先聊到这儿。这四步你捋顺了,立体匹配的基本功就算打扎实了。后面咱们再深入讲每一步的代码实现和调参技巧。

    专注资料整理