Epipolar Geometry
极线几何描述场景的两个视图之间的关系,将一个点在另一张图像中的匹配位置限制在一条直线上,是双目深度估计和 3D 映射的基础。
在计算机视觉中,极线几何描述同一场景的两个相机视图之间的几何关系。一张图像中的像素对应一条可能的 3D 点射线,这条射线投影到第二张图像上会形成一条线,即极线,而该像素的匹配点必须位于这条线上。这种约束只取决于相机的内参和相对位姿,与场景结构无关;它将匹配点搜索范围从整张图像缩小到一条线上。因此,极线几何是立体视觉、运动恢复结构以及引导机器人和头显的视觉 SLAM的基础。Richard Hartley 和 Andrew Zisserman 所著的《计算机视觉中的多视几何》是标准参考书。
极点、极线和极平面#
两个相机中心和任意一个场景点共同确定一个平面,即极平面。连接两个相机中心的直线是基线,它与每个图像平面相交于极点,也就是另一台相机中心在该图像中的投影;极点可能位于画面之外,也可能在无穷远处。每个极平面与每幅图像相交形成一条极线,因此图像中的所有极线都经过该图像的极点。场景点沿第一台相机的视线移动时,它在第二幅图像中的投影也会沿极线移动。
基础矩阵和本质矩阵#
秩为二的 3 × 3 基础矩阵 F 用像素坐标表示完整的几何关系。对于以齐次形式 (x, y, 1) 表示的对应点 x 和 x′,极线约束为 x′ᵀ F x = 0,而乘积 F x 给出点 x 在第二幅图像中的极线系数。通过相机标定完成相机标定后,本质矩阵 E = K′ᵀ F K 会在归一化坐标中表示相同的约束,并分解为相对旋转 R 和平移 t:E = [t]ₓ R。Christopher Longuet-Higgins 于 1981 年提出了本质矩阵和八点算法,Olivier Faugeras、Quang-Tuan Luong 和 Richard Hartley 则在 20 世纪 90 年代初发展了非标定基础矩阵。由于 E 只能确定 t 的方向,因此仅凭两个视图只能恢复场景形状,尺度未知;已知基线或物体尺寸才能确定尺度。
估计这两个矩阵时,会使用匹配的keypoint,例如 SIFT 特征,以及 RANSAC 等鲁棒估计器。RANSAC 会反复使用最小匹配样本拟合矩阵,并保留与最多匹配点一致的模型。下面的示例构建了两个包含 100 个点的视图,将 20 个匹配替换为随机错误匹配,并使用 OpenCV 的 MAGSAC 变体 RANSAC 估计 F:
import cv2
import numpy as np
rng = np.random.default_rng(0)
K = np.array([[800.0, 0, 320], [0, 800.0, 240], [0, 0, 1]]) # intrinsics shared by both cameras
# 100 scene points seen by two cameras; the second sits 0.5 m to the right, turned 5 degrees
points = rng.uniform([-2, -1, 4], [2, 1, 8], (100, 3))
rvec = np.array([0.0, np.radians(5), 0.0])
tvec = -cv2.Rodrigues(rvec)[0] @ np.array([0.5, 0.0, 0.0])
x1 = cv2.projectPoints(points, np.zeros(3), np.zeros(3), K, None)[0].reshape(-1, 2) + rng.normal(0, 0.5, (100, 2))
x2 = cv2.projectPoints(points, rvec, tvec, K, None)[0].reshape(-1, 2) + rng.normal(0, 0.5, (100, 2))
x2[:20] = rng.uniform([0, 0], [640, 480], (20, 2)) # replace 20 matches with wrong ones
# Robust estimation keeps the matches that agree with a single fundamental matrix
F, inliers = cv2.findFundamentalMat(x1, x2, cv2.USAC_MAGSAC, 1.0)
kept = inliers.ravel() == 1
print(f"Kept {kept[20:].sum()} of 80 true matches and {kept[:20].sum()} of 20 wrong ones")
# Each point in image 1 defines a line a*x + b*y + c = 0 in image 2 that its match must lie on
lines = cv2.computeCorrespondEpilines(x1, 1, F).reshape(-1, 3)
dist = np.abs((lines[:, :2] * x2).sum(axis=1) + lines[:, 2])
print(f"Median distance to epipolar line: {np.median(dist[20:]):.2f} px (true), {np.median(dist[:20]):.0f} px (wrong)")
print(f"Wrong match that survived: {dist[:20][kept[:20]].round(2)} px from its line")该方法保留了 80 个正确匹配中的 75 个,并剔除了 20 个错误匹配中的 19 个。正确匹配与其极线的中位距离为 0.43 像素,这是由半像素噪声决定的;错误匹配的中位距离则为 144 像素。一个未被剔除的错误匹配恰好落在距其极线 0.61 像素的位置,因为该约束只将匹配点限制在一条线上,只有外观信息才能确定线上哪个点才是正确的。
纯旋转没有基线,而单个平面可以同样适配多个基础矩阵,因此这两种情况都会使用单应性,即平面视图之间的 3 × 3 映射。ORB-SLAM 会并行拟合单应矩阵和基础矩阵来初始化地图,并保留对匹配点解释效果更好的矩阵。
校正、视差和深度#
校正会对立体图像对进行变换,使极线变为水平且对齐,从而让每个匹配点都位于同一图像行。立体匹配器随后会逐行搜索,而匹配点之间的列偏移量,即视差 d,可根据焦距 f 和基线 B 得出深度 Z = f × B / d。立体相机正是通过这种方式生成深度估计图。KITTI 立体视觉基准使用自动驾驶场景中经过校正的图像对评估这类匹配器。
立体视觉之外的应用#
在3D 重建中,COLMAP会通过拟合基础矩阵、本质矩阵或单应矩阵来验证每对图像,并丢弃几何支持不足的图像对和匹配点。图像匹配流程使用相同的检查来剔除错误对应,而图像配准会在场景平坦或相机仅发生旋转时(例如拼接全景图)采用上述单应性方案。对于移动相机拍摄的两帧图像,极线约束仅适用于静态表面,因此违反该约束的光流向量可以揭示独立运动的物体。多相机拍摄人体姿态时,可根据同一人物的关节与彼此极线的接近程度,在三角测量出 3D 骨架前完成跨视图关联;Ultralytics YOLO26 姿态估计模型会为每个视图中的每个人提供 17 个 COCO 人体关键点。










