3. 双目视觉基础:对极几何、视差与深度的三角测量关系

好,咱们进入双目视觉的核心部分。说实话,单目深度估计虽然灵活,但天生缺一把“尺子”。双目视觉就不一样了,它直接模拟了人眼的工作原理——用两个摄像头之间的“视差”来推算距离。这章我会把对极几何、视差计算和三角测量这几个硬骨头,用我实际项目中的经验给你掰开揉碎了讲。

3.1 对极几何:两个相机之间的“约束关系”

先问个问题:左眼看到的一个点,在右眼图像中会出现在哪里?

答案是——它一定在一条特定的直线上。这就是对极几何的核心思想。说白了,它描述的是两个相机之间的几何约束,帮我们把搜索范围从整张图缩小到一条线。

关键概念速览:

  • 极点(Epipole): 一个相机光心在另一个相机成像平面上的投影点。
  • 极线(Epipolar Line): 空间点P与两个光心构成的平面(极平面)与成像平面的交线。
  • 对极约束: 左图上的点p,在右图上的匹配点p',一定位于p对应的极线上。

我记得刚做双目系统时,总以为要暴力匹配全图。后来才明白,有了对极几何,匹配效率能提升几个数量级。你想想看,本来要在整张1920x1080的图像里找匹配点,现在只需要沿着一条线搜,这计算量省了多少?

3.2 视差:深度的“直接读数”

视差(Disparity)这个概念,我建议你把它理解成“左右眼看到的同一个物体在图像上的位置差”。

公式很简单:

d = x_left - x_right

其中x_left和x_right分别是同一个空间点在左右图像中的水平像素坐标。注意,这里的前提是两幅图像已经完成了极线校正——也就是左右图的对应行已经对齐了。

我的经验: 极线校正是双目系统里最容易出坑的环节。我曾经在一个项目中,因为标定板没放平,导致校正后的图像有0.5像素的误差,结果视差图全是条纹噪声。后来我养成了一个习惯:每次校正完,先拿一个已知距离的物体验证一下,确认没问题再往下走。

视差和深度的关系,其实是个反比关系:

  • 物体越近,视差越大
  • 物体越远,视差越小
  • 无限远处的物体,视差趋近于0

这个特性在实际应用中很重要。比如在ADAS场景中,前车的距离从5米变到50米,视差的变化会越来越小。这就意味着,双目视觉对近距离物体的深度估计精度很高,但对远距离物体的精度会急剧下降

3.3 三角测量:从视差到深度的“几何推导”

好,现在咱们来看最核心的部分——怎么从视差算出深度。

假设两个相机完全相同,焦距为f,基线距离(两个光心之间的距离)为B。空间点P的深度为Z。根据相似三角形原理:

Z = (f * B) / d

其中d就是视差。这个公式简洁得让人舒服,对吧?

但实际项目中,事情没那么简单。我列几个你一定会遇到的坑:

避坑指南(我曾经踩过的):

  1. 基线长度选择: 基线越长,对远距离物体的深度分辨率越高,但近距离物体的视差会变得很大,容易超出搜索范围。我做过一个项目,基线选了1.2米,结果最近探测距离只能到3米,再近就匹配不上了。
  2. 焦距影响: 长焦镜头能提高深度精度,但视野会变窄。在ADAS中,你需要平衡探测距离和视野范围。
  3. 量化误差: 视差是整数像素,这会导致深度值出现阶梯状误差。我一般会用亚像素插值来缓解这个问题。

3.4 实际项目中的“三角测量”实现

说了这么多理论,咱们看看代码怎么实现。下面是一个简化的深度计算函数:

def compute_depth(disparity_map, baseline, focal_length):
    """
    从视差图计算深度图
    :param disparity_map: 视差图 (单位: 像素)
    :param baseline: 基线距离 (单位: 米)
    :param focal_length: 焦距 (单位: 像素)
    :return: 深度图 (单位: 米)
    """
    # 避免除零错误
    disparity_map[disparity_map <= 0] = 0.1
    
    depth_map = (baseline * focal_length) / disparity_map
    
    # 限制深度范围,去除无效值
    depth_map[depth_map > 100] = 100  # 最大100米
    depth_map[depth_map < 0.5] = 0.5  # 最小0.5米
    
    return depth_map

这段代码看起来简单,但实际工程中要考虑的事情多着呢。比如:

  • 视差图的噪声怎么处理?我习惯用中值滤波或者置信度图来过滤。
  • 遮挡区域的视差怎么补?可以用左右一致性检查来检测遮挡。
  • 深度图的实时性要求?在嵌入式平台上,我一般会用FPGA或者GPU来加速。

一个实用的经验公式:

深度分辨率(即深度值变化1米对应的视差变化量)可以近似为:

Δd ≈ (f * B) / Z² * ΔZ

这意味着:距离越远,视差对深度变化的敏感度越低。所以双目视觉在远距离场景下,天生不如激光雷达。

3.5 双目系统的“标定”与“校正”

嗯,这里要特别强调一下。没有好的标定,上面所有的公式都是白搭。

双目标定要做两件事:

  1. 内参标定: 每个相机自己的焦距、主点、畸变参数。
  2. 外参标定: 两个相机之间的旋转矩阵R和平移向量T(其实就是基线)。

标定完成后,还要做极线校正。校正的目的是让左右图像的行完全对齐,这样视差就只体现在水平方向上。

我的习惯: 标定完成后,我会在左右图像上画几条水平线,然后检查对应点是否真的在同一条线上。如果偏差超过1个像素,我会重新标定。别嫌麻烦,这一步省了,后面调试视差图的时候你会哭的。

3.6 总结一下

双目视觉的核心,说白了就是利用两个相机之间的几何关系,把深度估计问题转化成一个匹配问题。对极几何告诉我们“去哪里找”,视差告诉我们“找到了多少”,三角测量告诉我们“怎么换算成距离”。

我个人觉得,双目视觉最大的优势是物理可解释性强——每个深度值都有明确的几何意义。不像单目深度学习,有时候你都不知道它为什么输出那个值。但缺点也很明显:计算量大、对光照敏感、远距离精度差

下一章,我会带你看看怎么把视差计算做得又快又准,包括那些经典的匹配算法和现代的深度学习方法。咱们到时候见。