双目立体视觉核心:对极几何、基础矩阵与立体校正全解
上一章我们搞定了相机标定,相当于给两个相机都配好了"精准的眼镜"。但光有眼镜还不够,要想让两个相机像人眼一样判断距离,还需要搞懂它们之间的"协作关系"------这就是对极几何。
如果说标定是解决"单个相机怎么看世界",那对极几何就是解决"两个相机怎么一起看世界"。它是立体匹配、三维重建的数学基础,没有它,双目视觉就是两个独立的摄像头,永远算不出深度。
一、人眼的立体视觉:对极几何的"生物原型"
先给大家一个灵魂比喻 :
你闭上一只眼睛,用另一只眼睛看面前的一支笔,然后快速换另一只眼睛,你会发现笔的位置"跳"了一下。这个"跳"的距离,就是视差 。
大脑就是根据这个视差,计算出笔离你的距离。
对极几何,就是把这个生物过程,用数学公式精确描述出来。它告诉我们:一个点在左相机的像,一定在右相机的某条线上 。这条线就是极线。
这个约束太重要了!原来我们要在右相机整幅图里找匹配点,现在只需要在一条线上找,计算量直接减少了几个数量级。
二、对极几何基础概念
2.1 核心术语(每个都要懂)
先看对极几何的标准示意图:

对极几何基本模型
-
光心 O1,O2O_1, O_2O1,O2:左右相机的投影中心
通俗解释:两个相机的"瞳孔"
-
基线 BBB:两个光心之间的连线
通俗解释:两个相机之间的距离,基线越长,深度测量精度越高
-
极点 e1,e2e_1, e_2e1,e2:基线与左右成像平面的交点
通俗解释:一个相机的光心,在另一个相机成像平面上的投影
特殊情况:如果两个相机平行,极点在无穷远处,极线都是水平的
- 极平面:由空间点P和两个光心O1、O2组成的平面
通俗解释:三个点确定的一个平面,像一把刀,同时切过两个成像平面
- 极线 l1,l2l_1, l_2l1,l2:极平面与左右成像平面的交线
通俗解释:空间点P在右相机的像p2,一定在左相机的极线l1上;反之亦然
2.2 对极约束:双目视觉的"黄金法则"

这是对极几何最核心的公式
p2TFp1=0 p_2^T F p_1 = 0 p2TFp1=0
每个符号的通俗解释:
- p1=u1,v1,1Tp_1 = u_1, v_1, 1^Tp1=u1,v1,1T:左相机图像上点的齐次坐标
- p2=u2,v2,1Tp_2 = u_2, v_2, 1^Tp2=u2,v2,1T:右相机图像上对应点的齐次坐标
- FFF:基础矩阵(Fundamental Matrix),3×3矩阵,秩为2
- 公式含义:如果p1和p2是空间同一点在两个相机的成像,那么它们一定满足这个等式
通俗理解 :
这就像两个相机之间的"秘密约定"。只要知道了基础矩阵F,给你左图的一个点p1,你就能算出右图中对应的极线l2,匹配点一定在这条线上。
三、本质矩阵E与基础矩阵F的关系
很多人搞不清E和F的区别,其实很简单:
- 本质矩阵E :描述两个相机坐标系之间的对极约束,与相机内参无关
- 基础矩阵F :描述两个像素坐标系之间的对极约束,包含了相机内参信息
它们的关系是:
F=K2−TEK1−1 F = K_2^{-T} E K_1^{-1} F=K2−TEK1−1
每个符号解释:
- K1,K2K_1, K_2K1,K2:左右相机的内参矩阵(上一章标定得到)
- K−TK^{-T}K−T:内参矩阵的逆的转置
本质矩阵E还有一个非常优美的性质:
E=t×R E = t_\times R E=t×R
每个符号解释:
- RRR:右相机相对于左相机的旋转矩阵
- ttt:右相机相对于左相机的平移向量
- t×t_\timest×:平移向量t的反对称矩阵
反对称矩阵定义 :
如果 t=tx,ty,tzTt = t_x, t_y, t_z^Tt=tx,ty,tzT,那么
t×=0−tztytz0−tx−tytx0 t_\times = \begin{bmatrix} 0 & -t_z & t_y\\ t_z & 0 & -t_x\\ -t_y & t_x & 0 \end{bmatrix} t×= 0tz−ty−tz0txty−tx0
通俗理解 :
本质矩阵E只和两个相机之间的相对位姿(R和t)有关。也就是说,只要知道了E,就能分解出两个相机之间的旋转和平移关系------这就是运动恢复结构(SfM)的核心思想。
四、基础矩阵F的求解方法
基础矩阵F是对极几何的核心,怎么求它呢?书里第三章重点讲了8点法 和RANSAC鲁棒估计。
4.1 经典8点法
最简单、最经典的方法
- 最少需要 8对对应点
- 把F的9个元素当成未知数,解线性方程组
- 优点:简单易懂,计算速度快
- 缺点:对噪声敏感,只要有一个点错了,结果就会差很多
核心方程:
对于每一对对应点 (p1,p2)(p_1, p_2)(p1,p2),都有
u2u1F11+u2v1F12+u2F13+v2u1F21+v2v1F22+v2F23+u1F31+v1F32+F33=0 u_2 u_1 F_{11} + u_2 v_1 F_{12} + u_2 F_{13} + v_2 u_1 F_{21} + v_2 v_1 F_{22} + v_2 F_{23} + u_1 F_{31} + v_1 F_{32} + F_{33} = 0 u2u1F11+u2v1F12+u2F13+v2u1F21+v2v1F22+v2F23+u1F31+v1F32+F33=0
把8个这样的方程组合起来,就可以解出F的9个元素(差一个尺度因子)。
4.2 RANSAC鲁棒估计

工业界实际使用的方法
- 解决8点法对噪声敏感的问题
- 随机选择8个点,计算F,然后统计有多少个点符合这个F(内点)
- 重复多次,选择内点最多的那个F作为最终结果
- 优点:可以容忍高达50%的外点(错误匹配点)
- 缺点:需要迭代多次,计算量稍大
工程经验 :
实际应用中,永远不要用纯8点法,一定要用RANSAC+8点法。否则只要有一两个错误匹配点,你的基础矩阵就会完全错误。
五、立体校正:把歪的图像变"平"
为什么需要立体校正?
因为在实际应用中,两个相机很难做到完全平行、光轴垂直于基线。如果相机是歪的,极线就不是水平的,立体匹配的时候就不能直接在同一行找匹配点,计算量会非常大。
立体校正的目标就是:
通过图像变换,把两个相机的成像平面变换到同一个平面上,并且让极线变成水平的、平行的。
校正后的效果:
- 左右图像的对应点,一定在同一行上
- 视差只存在于水平方向,垂直方向视差为0
5.1 立体校正原理
立体校正的核心是找到两个变换矩阵 H1H_1H1 和 H2H_2H2,分别对左右图像进行投影变换,使得:
- 两个变换后的成像平面共面
- 极线水平且平行
- 行对齐
5.2 Bouguet校正算法
OpenCV默认使用的校正算法
- 基于两个相机的内参和外参(R和t)
- 最小化两个图像的重投影误差
- 最大化有效图像区域
校正前后对比图:

校正效果分析 :
校正前,左图的点对应的极线是斜的,需要在整幅图里找匹配;校正后,极线是水平的,只需要在同一行找匹配,计算量减少了99%以上。
六、三目视觉的对极几何扩展
书里第三章还专门讲了三目视觉的对极几何,这是很多教材没有的内容,非常实用。
三目视觉有三种经典构型:

6.1 三目对极约束
对于三目视觉,有三个基础矩阵:F12F_{12}F12(相机1和2之间)、F13F_{13}F13(相机1和3之间)、F23F_{23}F23(相机2和3之间)。
空间点P在三个相机的成像 p1,p2,p3p_1, p_2, p_3p1,p2,p3 满足:
p2TF12p1=0p3TF13p1=0p3TF23p2=0 p_2^T F_{12} p_1 = 0 \\ p_3^T F_{13} p_1 = 0 \\ p_3^T F_{23} p_2 = 0 p2TF12p1=0p3TF13p1=0p3TF23p2=0
6.2 三目视觉的优势
- 可以解决双目视觉的遮挡问题:如果一个点在一个相机里被挡住了,还可以在另外两个相机里看到
- 可以提高深度测量精度:三个相机的测量结果可以融合,减少误差
- 可以解决歧义匹配问题:双目视觉中可能有多个点满足对极约束,三目视觉可以消除歧义
七、实验结果与精度对比(原文表格全还原)
7.1 基础矩阵求解方法精度对比(表3-1)
| 方法 | 平均极线误差(像素) | 外点容忍率 | 计算时间(ms) |
|---|---|---|---|
| 纯8点法 | 2.35 | 10% | 0.5 |
| 归一化8点法 | 0.87 | 15% | 0.6 |
| RANSAC+8点法 | 0.32 | 50% | 12.5 |
结论 :
RANSAC+8点法虽然计算时间稍长,但精度最高,外点容忍率最高,是工业界的标准选择。
7.2 立体校正精度对比(表3-2)
| 算法 | 平均垂直视差(像素) | 有效图像区域占比 |
|---|---|---|
| 传统校正法 | 1.23 | 78% |
| Bouguet算法 | 0.18 | 92% |
结论 :
Bouguet算法的校正精度最高,有效图像区域最大,是OpenCV默认使用的算法。
八、核心代码:OpenCV立体校正
python
import cv2
import numpy as np
# 上一章标定得到的内参和畸变系数
left_camera_matrix = np.array([[532.8, 0, 325.1], [0, 530.5, 249.7], [0, 0, 1]])
left_distortion = np.array([[0.123, -0.234, 0.001, 0.002, 0.000]])
right_camera_matrix = np.array([[531.2, 0, 319.8], [0, 529.7, 248.5], [0, 0, 1]])
right_distortion = np.array([[0.118, -0.227, 0.000, 0.003, 0.000]])
# 双目外参(R和t)
R = np.array([[0.9999, 0.0023, -0.0125], [-0.0024, 0.9999, -0.0032], [0.0125, 0.0032, 0.9999]])
T = np.array([[-120.0], [0.5], [0.2]]) # 基线120mm
# 图像尺寸
image_size = (640, 480)
# 立体校正
R1, R2, P1, P2, Q, validPixROI1, validPixROI2 = cv2.stereoRectify(
left_camera_matrix, left_distortion,
right_camera_matrix, right_distortion,
image_size, R, T,
flags=cv2.CALIB_ZERO_DISPARITY, alpha=0.9
)
# 计算校正映射表
left_map1, left_map2 = cv2.initUndistortRectifyMap(
left_camera_matrix, left_distortion, R1, P1, image_size, cv2.CV_16SC2
)
right_map1, right_map2 = cv2.initUndistortRectifyMap(
right_camera_matrix, right_distortion, R2, P2, image_size, cv2.CV_16SC2
)
# 读取左右图像
left_img = cv2.imread('left.jpg')
right_img = cv2.imread('right.jpg')
# 进行校正
left_rectified = cv2.remap(left_img, left_map1, left_map2, cv2.INTER_LINEAR)
right_rectified = cv2.remap(right_img, right_map1, right_map2, cv2.INTER_LINEAR)
# 显示校正结果
cv2.imshow('Left Rectified', left_rectified)
cv2.imshow('Right Rectified', right_rectified)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 保存校正参数
np.savez('stereo_params.npz', R1=R1, R2=R2, P1=P1, P2=P2, Q=Q)
print("立体校正参数已保存到 stereo_params.npz")
关键参数解释:
flags=cv2.CALIB_ZERO_DISPARITY:让两个相机的主点在同一行,垂直视差为0alpha=0.9:保留90%的有效图像区域,alpha=0会裁剪掉所有黑边,alpha=1会保留所有像素
九、趣味案例:对极几何与立体校正的"坑"
-
3D电影重影
3D电影就是利用双目视觉原理,给左右眼播放不同的图像。如果两个放映机的校正不准,极线不水平,就会出现重影,看久了会头晕。
-
VR眼镜的眩晕感
VR眼镜的两个屏幕如果校正不准,视差不符合人眼的对极约束,大脑就会产生冲突,导致眩晕、恶心。
-
工业测量的"鬼点"
如果基础矩阵求解错误,极线不准,立体匹配就会出现很多错误的匹配点,也就是"鬼点",导致三维重建结果出现很多噪点。
-
自动驾驶的车道线检测
自动驾驶的双目相机如果校正不准,极线倾斜,车道线的深度计算就会错误,导致车距判断失误。
十、本章总结
- 对极几何核心:一个点在左图的像,一定在右图的某条极线上
- 基础矩阵F:描述像素坐标系之间的对极约束,3×3矩阵,秩为2
- 本质矩阵E:描述相机坐标系之间的对极约束,只与相对位姿R和t有关
- 基础矩阵求解:RANSAC+8点法是工业界标准
- 立体校正目标:让极线水平平行,对应点在同一行
- Bouguet算法:OpenCV默认的校正算法,精度高,有效区域大
- 三目视觉优势:解决遮挡、提高精度、消除歧义匹配
掌握了这一章,你就掌握了双目视觉立体匹配的基础。