2、双目立体视觉:双目相机模型、对极几何、视差图计算

好,咱们进入正题。上一章聊了人眼怎么感知深度,这一章我们来看看机器怎么模仿这个能力。说白了,就是给相机装上两只「眼睛」,然后让算法算出距离。

我刚开始接触这个领域时,总觉得双目视觉很神秘。后来拆了几个产品,自己搭了一套系统,才发现核心原理其实并不复杂。今天咱们就把这三个东西讲透:双目相机模型、对极几何、视差图计算。

2.1 双目相机模型

先问一个问题:为什么单目相机很难测距?

你想想看,单目相机拍一张照片,丢失了深度信息。一个篮球和一个乒乓球,如果距离不同,在图像上可能一样大。这就是所谓的「尺度不确定性」。

双目相机怎么解决?很简单——用两个相机同时拍,利用它们之间的已知距离(基线)来三角测量。

核心公式:

深度 Z = (f × B) / d

其中 f 是焦距,B 是基线长度,d 是视差。

这个公式我建议你记在心里,几乎所有双目系统的标定和计算都绕不开它。

2.1.1 理想平行光轴模型

最理想的情况,是两个相机完全平行放置,光轴互相平行,成像平面共面。这种配置叫「理想双目系统」。

在这种模型下,左图和右图只在水平方向有偏移。也就是说,左图上的一个点,在右图上出现在同一行,只是列坐标不同。这个偏移量就是视差。

我在项目中遇到过一个问题:实际装配时,两个相机很难做到绝对平行。哪怕偏差0.1度,都会导致后续匹配困难。所以后来我习惯先用标定板做一次立体标定,再通过软件校正。

个人经验: 如果你自己搭双目系统,建议基线长度选在 5-15cm 之间。太短了视差太小,测距精度差;太长了左右视图差异太大,匹配困难。我一般用 8cm 左右,兼顾室内和室外场景。

2.1.2 一般双目模型与校正

实际系统中,两个相机往往有旋转和平移偏差。这时候就需要做立体校正(Stereo Rectification)。

校正的目标是什么?就是把两个相机的图像变换到同一个平面上,让对应点在同一行上。这样就把一般模型变成了理想模型。

常用的算法是 Bouguet 算法。它把左右相机的旋转矩阵拆成两半,各旋转一半,再调整平移向量。嗯,这里要注意:校正后图像会有裁剪,边缘会损失一些像素。

参数 理想模型 一般模型(校正后)
光轴平行度 完全平行 校正后平行
对应点行坐标 严格相同 校正后相同
视差方向 仅水平 仅水平
适用场景 理论分析 实际工程

2.2 对极几何

对极几何,听起来很高大上。说白了,它研究的就是:左图上的一个点,在右图上可能出现在哪里。

你想想看,如果不加约束,左图上的一个点,要在右图整张图上搜索匹配,那计算量太大了。对极几何告诉我们:不用搜整张图,只需要搜一条线——极线。

2.2.1 基本概念

先记住几个术语:

  • 基线:两个相机光心的连线
  • 极平面:空间点 + 两个光心构成的平面
  • 极点:基线与成像平面的交点
  • 极线:极平面与成像平面的交线

核心约束:左图上的点 p,它在右图上的对应点 p',一定在 p 对应的极线上。这就是对极约束。

避坑指南: 我曾经在做一个室外场景重建项目时,忽略了极线约束的精度。结果匹配点对大量错误,重建出来的点云全是噪点。后来发现是标定参数不准,导致极线计算偏差了2个像素。记住:对极几何的精度完全依赖于相机标定,标定不好,后面全白搭。

2.2.2 本质矩阵与基础矩阵

这两个矩阵是对极几何的数学表达。

  • 本质矩阵 E:描述两个相机在归一化坐标系下的对极关系。只包含旋转和平移信息。
  • 基础矩阵 F:描述两个相机在像素坐标系下的对极关系。包含了内参和外参。

它们的关系很简单:F = K-T E K-1,其中 K 是相机内参矩阵。

实际应用中,我们通常用 8 点法来估计基础矩阵。我个人的习惯是:先用 RANSAC 剔除误匹配,再计算 F 矩阵,这样鲁棒性会好很多。

// 伪代码:8点法估计基础矩阵
Input: 至少8对匹配点 (x1, y1) <-> (x2, y2)
1. 构建约束矩阵 A (8x9)
2. 对 A 做 SVD 分解
3. F 矩阵 = 最小奇异值对应的右奇异向量
4. 强制 F 的秩为 2(再次 SVD,置最小奇异值为0)
5. 输出 F 矩阵

2.3 视差图计算

有了校正后的图像和对极几何约束,接下来就是计算视差图了。这是双目视觉中最耗时、也最考验工程能力的部分。

视差图,说白了就是一张灰度图,每个像素的值表示该点在左右图中的视差大小。视差越大,说明物体越近。

2.3.1 立体匹配算法分类

立体匹配算法大致分两类:

类型 代表算法 特点 我推荐的使用场景
局部匹配 BM、SAD、NCC 速度快,精度一般 实时系统、嵌入式设备
全局匹配 GC、SGM、Belief Propagation 精度高,速度慢 离线重建、高精度需求

我个人最常用的是 SGM(半全局匹配)。它兼顾了速度和精度,在 OpenCV 里直接有实现。我在一个自动驾驶项目中用过 SGM,在 1080p 图像上能做到 15fps,效果还不错。

2.3.2 匹配代价计算

匹配的核心是:怎么判断左图的一个像素和右图的一个像素是同一个点?

常用的代价函数有:

  • SAD(绝对差和):计算窗口内像素差的绝对值之和。简单粗暴,但对光照敏感。
  • NCC(归一化互相关):对光照变化不敏感,但计算量大。
  • Census 变换:把像素与邻域比较,生成二进制编码。我比较喜欢这个,因为它对光照变化鲁棒,而且可以用位运算加速。

技巧: 实际项目中,我经常把 Census 变换和 SAD 结合起来用。先用 Census 做粗匹配,再用 SAD 做精匹配。这样既快又准。

2.3.3 视差优化与后处理

算出来的视差图往往有噪声和空洞。需要做后处理:

  1. 左右一致性检查:从左图算到右图,再从右图算到左图,如果视差不一致,说明匹配不可靠,直接丢弃。
  2. 中值滤波:去除孤立噪点。
  3. 亚像素插值:用二次曲线拟合,把视差精度提升到亚像素级别。

我记得有一次做高精度测量,直接用整数视差,误差有 2mm。后来加了亚像素插值,误差降到了 0.3mm。嗯,这一步千万别省。

// 亚像素插值(二次曲线拟合)
// cost 数组在最优视差 d 及其相邻视差处的代价
d_sub = d + (cost[d-1] - cost[d+1]) / (2 * (cost[d-1] - 2*cost[d] + cost[d+1]))

本章知识结构

下面这张图总结了双目立体视觉的核心流程和知识点之间的关系:

双目立体视觉知识结构图 双目立体视觉核心知识结构 双目相机模型 理想平行光轴模型 一般模型 + 立体校正 对极几何约束 极线 / 极点 / 极平面 本质矩阵 E 基础矩阵 F 视差图计算 局部匹配 (BM/SAD) 全局匹配 (SGM/GC) 后处理优化 亚像素插值 核心流程:双目相机模型 → 对极几何约束 → 立体匹配 → 视差图 → 深度图

好了,这一章的内容就到这里。双目视觉的核心就是这三步:理解相机模型、利用对极几何约束搜索空间、用匹配算法算视差。每一步都有坑,但每一步也都有成熟的解决方案。多动手搭一套系统,你会理解得更深。