4、对极几何与基础矩阵:对极约束、本质矩阵、基础矩阵、八点法求解

说到三维重建,有个绕不开的坎——对极几何

我记得刚入行那会儿,第一次做双目视觉标定,看着两张图片里那些匹配点,心里就在想:这两张图到底有什么关系?怎么从一堆像素点里算出相机的运动?

后来我才明白,这一切的答案,都藏在对极几何里。

4.1 对极约束:两张图之间的“隐形锁链”

先看一个场景。你拿着相机,拍了一张照片。然后你移动了一下相机,又拍了一张。这两张图里,同一个三维点(比如墙角)会落在不同的像素位置。

问题是:这两个像素点之间,有没有什么固定的数学关系?

答案是:有。这就是对极约束

说白了,对极约束描述的是:第一张图里的一个像素点,在第二张图里对应的匹配点,一定落在一条特定的直线上。这条线叫极线

核心思想:匹配点不需要在整张图里瞎找,只需要沿着极线搜索就行。这大大降低了匹配的计算量。

我在项目中遇到过一个问题:用暴力匹配做特征点匹配,速度慢得离谱。后来改用极线约束做引导匹配,速度直接提升了10倍。嗯,这就是对极几何的实用价值。

4.2 本质矩阵:相机运动的内在密码

对极约束有了,但怎么用数学表达?

这里就要引入本质矩阵(Essential Matrix)了。它用符号 E 表示。

本质矩阵描述的是:两个相机坐标系之间的旋转和平移关系。它只跟相机的运动有关,跟相机内参无关。

数学上,本质矩阵满足一个关键方程:

p2^T * E * p1 = 0

其中 p1p2 是归一化平面上的坐标(不是像素坐标)。

这个方程的意思就是:点 p1 在第二张图里的对应点 p2,一定满足这个约束。

我的习惯:在实际代码里,我会先把像素坐标转成归一化坐标,再计算本质矩阵。这样能避免内参带来的干扰,结果更稳定。

4.3 基础矩阵:像素层面的对极约束

本质矩阵只关心相机运动,但实际中我们拿到的是像素坐标。怎么办?

把相机内参加进去就行了。这就得到了基础矩阵(Fundamental Matrix),用 F 表示。

基础矩阵的方程是:

x2^T * F * x1 = 0

其中 x1x2 是像素坐标。

本质矩阵和基础矩阵的关系很简单:

F = K^(-T) * E * K^(-1)

其中 K 是相机内参矩阵。

矩阵 输入坐标 依赖信息 自由度
本质矩阵 E 归一化坐标 旋转 R、平移 t 5
基础矩阵 F 像素坐标 旋转 R、平移 t、内参 K 7

你想想看,基础矩阵把内参和运动信息都打包在一起了。所以它可以直接从像素匹配点中求解出来。

4.4 八点法求解:最经典的线性方法

怎么从一堆匹配点里算出基础矩阵?

最经典的方法就是八点法。为什么叫八点法?因为基础矩阵有7个自由度,理论上7个点就够了。但8个点可以构成一个线性方程组,求解更稳定。

具体步骤是这样的:

  1. 构建矩阵 A:对每一对匹配点 (x1, y1) 和 (x2, y2),构建一行:
A_i = [x2*x1, x2*y1, x2, y2*x1, y2*y1, y2, x1, y1, 1]
  1. 求解线性方程组:解 A * f = 0,其中 f 是基础矩阵的9个元素。
  2. 奇异值约束:基础矩阵的秩必须是2。所以对求出的矩阵做SVD分解,把最小的奇异值置为0,再重构。

我曾经踩过的坑:直接用原始像素坐标做八点法,数值稳定性很差。后来我学会了先做归一化——把坐标缩放到均值为0、方差为1的范围。这一步能显著提升精度。

归一化后的八点法,代码实现大概是这样的:

def compute_fundamental_matrix(pts1, pts2):
    # 1. 归一化
    T1 = normalize_points(pts1)
    T2 = normalize_points(pts2)
    pts1_norm = apply_transform(T1, pts1)
    pts2_norm = apply_transform(T2, pts2)
    
    # 2. 构建矩阵 A
    A = []
    for p1, p2 in zip(pts1_norm, pts2_norm):
        x1, y1 = p1
        x2, y2 = p2
        A.append([x2*x1, x2*y1, x2, y2*x1, y2*y1, y2, x1, y1, 1])
    A = np.array(A)
    
    # 3. 求解 Af = 0
    U, S, Vt = np.linalg.svd(A)
    f = Vt[-1, :]
    F = f.reshape(3, 3)
    
    # 4. 秩2约束
    U, S, Vt = np.linalg.svd(F)
    S[2] = 0
    F = U @ np.diag(S) @ Vt
    
    # 5. 反归一化
    F = T2.T @ F @ T1
    
    return F

4.5 从基础矩阵到相机运动

算出基础矩阵之后,怎么得到相机的旋转和平移?

流程是这样的:

  1. 从基础矩阵 F 和相机内参 K 恢复本质矩阵 EE = K^T * F * K
  2. E 做SVD分解,得到4组可能的 (R, t) 解。
  3. 用三角化验证,选出正确的那个。

我的经验:4组解里只有1组能让所有三维点都在相机前方。用这个条件筛选,基本不会出错。

4.6 本章知识体系

下面这张图,把对极几何的核心逻辑串起来了:

对极几何与基础矩阵知识体系 匹配点对 (x1, x2) 八点法求解 基础矩阵 F 本质矩阵 E 旋转 R、平移 t 对极约束 极线搜索 输入匹配点 → 八点法 → 基础矩阵 → 本质矩阵 → 相机运动

从这张图可以看得很清楚:整个流程就是从匹配点出发,一步步解出相机的运动参数。每一步都有明确的数学工具支撑。

好了,对极几何的核心内容就这些。说白了,它就是连接二维图像和三维空间的一座桥梁。掌握了它,你就能从两张图片里读出相机的运动轨迹。

下次做视觉里程计或者三维重建的时候,别忘了回头看看这个基础。很多看似复杂的问题,追根溯源,都落在这几个矩阵上。


专注资料整理