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 视差优化与后处理
算出来的视差图往往有噪声和空洞。需要做后处理:
- 左右一致性检查:从左图算到右图,再从右图算到左图,如果视差不一致,说明匹配不可靠,直接丢弃。
- 中值滤波:去除孤立噪点。
- 亚像素插值:用二次曲线拟合,把视差精度提升到亚像素级别。
我记得有一次做高精度测量,直接用整数视差,误差有 2mm。后来加了亚像素插值,误差降到了 0.3mm。嗯,这一步千万别省。
// 亚像素插值(二次曲线拟合)
// cost 数组在最优视差 d 及其相邻视差处的代价
d_sub = d + (cost[d-1] - cost[d+1]) / (2 * (cost[d-1] - 2*cost[d] + cost[d+1]))
本章知识结构
下面这张图总结了双目立体视觉的核心流程和知识点之间的关系:
好了,这一章的内容就到这里。双目视觉的核心就是这三步:理解相机模型、利用对极几何约束搜索空间、用匹配算法算视差。每一步都有坑,但每一步也都有成熟的解决方案。多动手搭一套系统,你会理解得更深。