第1章:立体匹配——从像素到深度的桥梁

各位同学,今天咱们来聊聊立体匹配。说实话,这是整个双目视觉系统里最核心、也最让人头疼的一环。我做了这么多年机器人避障,踩过的坑有一半都跟立体匹配有关。

立体匹配到底在干什么?说白了,就是给左图和右图里的像素点「找对象」。左图里的某个像素,在右图里找到它的孪生兄弟,然后根据这对兄弟的位置差,算出深度信息。你想想看,这不就是模拟人眼的功能吗?

核心思想:立体匹配 = 寻找对应点 + 计算视差

1.1 立体匹配算法分类

立体匹配算法分几大类?我习惯把它们分成三类:全局匹配、局部匹配、半全局匹配。嗯,这里要注意,分类标准其实挺多的,但按计算方式分最实用。

算法类别 代表算法 精度 速度 适用场景
全局匹配 Graph Cut、Belief Propagation 高精度离线处理
局部匹配 BM、SAD、NCC 实时避障、嵌入式
半全局匹配 SGBM 中高 平衡场景、主流选择

我在项目中遇到过最尴尬的事:用全局匹配算法跑一个720p的图像,结果一帧算了3秒钟。机器人早就撞墙了。所以啊,选算法一定要看你的硬件平台。

1.2 BM算法——简单粗暴但有效

BM算法,全称Block Matching,块匹配。它的思路特别直白:在左图取一个小窗口,然后在右图的水平方向上滑动,找最像的那个窗口。

怎么判断「像不像」?常用的有SAD(绝对差之和)和SSD(平方差之和)。我一般用SAD,因为计算量小,嵌入式上跑得动。

// BM算法核心伪代码
for each pixel (x, y) in left image:
    min_cost = INF
    best_disp = 0
    for d in [0, max_disp]:
        cost = SAD(window_left, window_right(x-d))
        if cost < min_cost:
            min_cost = cost
            best_disp = d
    disparity[y][x] = best_disp

我的经验:BM算法在纹理丰富的区域表现不错,但在弱纹理区域(比如白墙)会出很多噪声。我曾经在实验室的白墙前测试,结果视差图跟雪花一样。后来加了预处理,用Canny边缘增强,效果好了不少。

1.3 SGBM算法——BM的进阶版

SGBM,半全局块匹配。它比BM多了什么?多了「全局约束」。BM只考虑局部窗口的相似度,SGBM还考虑了相邻像素之间的视差连续性。

说白了,SGBM在BM的基础上加了一个平滑项。如果相邻像素的视差变化太大,就惩罚它。这样生成的视差图更平滑,噪声更少。

我记得第一次用OpenCV的SGBM时,参数调了一整天。后来发现几个关键参数:

  • minDisparity:最小视差,一般设0
  • numDisparities:视差范围,必须是16的倍数
  • blockSize:匹配窗口大小,奇数,3~11之间
  • P1、P2:平滑惩罚参数,P2一般比P1大

注意:P1和P2设得太大,视差图会过于平滑,丢失细节;设得太小,又跟BM一样全是噪声。我一般从P1=8*blockSize^2、P2=32*blockSize^2开始调。

1.4 视差图生成

视差图,就是一张灰度图。每个像素的值代表该点的视差大小。视差越大,说明物体离相机越近;视差越小,物体越远。

生成视差图其实就两步:

  1. 对每个像素计算最佳视差
  2. 把视差值映射到0~255的灰度范围

这里有个坑:视差图里会有一些「黑洞」——就是那些匹配失败的像素点。为什么会这样?因为遮挡、弱纹理、重复纹理都会导致匹配失败。

我曾经在户外测试,树影斑驳的地面让视差图出现了大量空洞。后来用了后处理:中值滤波+空洞填充,才勉强能用。

// 视差图后处理示例
cv::Mat disparity, filtered;
cv::medianBlur(disparity, filtered, 5);  // 中值滤波去噪

// 空洞填充:用周围有效像素填充无效点
for each invalid pixel:
    fill_with_nearest_valid_pixel()

关键点:视差图的质量直接决定深度估计的精度。我见过太多人把精力花在后面的障碍物检测上,结果视差图本身一塌糊涂。记住:垃圾进,垃圾出。

知识体系总览

下面这张图是我自己画的,把立体匹配的核心逻辑串起来了。你仔细看看,就能明白各个算法之间的关系。

立体匹配知识体系 左右图像对 全局匹配 Graph Cut / BP 局部匹配 BM / SAD / NCC 半全局匹配 SGBM 视差图 后处理:滤波 / 空洞填充 精度 vs 速度的权衡

从这张图你能看出来,立体匹配的核心就是「精度」和「速度」的博弈。全局匹配精度高但慢,局部匹配快但糙,半全局匹配取了个中间值。我在嵌入式平台上做避障,90%的情况都用SGBM,偶尔用BM做快速预览。

好了,这一章的内容就到这儿。立体匹配是双目视觉的基石,后面的深度估计、障碍物检测都依赖它。你先把这些基础概念吃透,后面咱们再深入细节。

专注资料整理