SfM 原理详解:从多张照片恢复三维结构
SfM(Structure from Motion,运动恢复结构)解决的问题很朴素:只给一组从不同位置拍摄的照片,能不能同时恢复相机的运动轨迹和场景的三维结构?
答案是可以,但它靠的不是“AI 猜形状”,而是一整套严密的多视图几何。
目录
一、SfM 到底在求什么
把 SfM 拆开看:
- Structure:场景里的三维点在哪里;
- Motion:相机拍每张照片时在哪里、朝向哪里。
输入通常是一组有重叠视野的图片,输出则是相机参数和稀疏点云。
| 项目 | SfM 中的含义 | 典型结果 |
|---|---|---|
| 输入图像 | 同一场景的多视角照片,可以有序也可以无序 | JPG / PNG |
| 相机内参 | 焦距、主点、畸变等相机自身参数 | $K, d$ |
| 相机外参 | 每张图像对应的位姿 | $R, t$ |
| 三维结构 | 被多张图共同观测到的空间点 | sparse point cloud |
| 观测轨迹 | 哪些 2D 特征点来自同一个 3D 点 | tracks |
最核心的一句话是:SfM 通过同名点约束,把“二维像素坐标”反推出“三维点 + 相机位姿”。
二、整体流程
一条典型的 SfM 流水线可以拆成 6 步:
- 特征提取:在每张图里找稳定的局部特征点,例如角点、纹理块、边缘交汇处。
- 特征匹配:在不同图片之间找“看起来像同一个三维点”的 2D 特征。
- 两视图几何估计:用匹配点估计基础矩阵 $F$ 或本质矩阵 $E$,过滤错误匹配。
- 初始化重建:选择一对质量好的图片,估计相对位姿并三角化出第一批 3D 点。
- 增量注册相机:不断把新图片加入已有重建,用 PnP 求位姿,再三角化新点。
- Bundle Adjustment:把所有相机和三维点一起优化,最小化重投影误差。
这条流水线有一个重要特点:SfM 先追求几何一致性,而不是追求稠密表面。 因此它输出的点云通常是稀疏的,但相机位姿很有价值。
三、特征与匹配
SfM 的第一步不是直接估计三维,而是先回答一个更基础的问题:照片 A 里的这个点,和照片 B 里的哪个点是同一个真实空间点?
这通常靠局部特征完成。一个特征点包含两部分:
- 关键点位置:它在图像中的 $(u, v)$ 像素坐标;
- 描述子:一段向量,用来描述它周围局部纹理的样子。
常见特征包括 SIFT、SURF、ORB、AKAZE 等。经典 SfM 系统很喜欢 SIFT,因为它对尺度、旋转、光照变化相对稳定;实时或移动端系统则常用 ORB 这类更快的二进制特征。
匹配时最直观的方法是:对图像 A 的每个描述子,在图像 B 中找距离最近的描述子。但这会产生很多误匹配,所以还需要过滤:
| 过滤策略 | 作用 |
|---|---|
| 最近邻比值测试 | 如果第一近和第二近太接近,说明匹配不可靠 |
| 双向一致性检查 | A 匹配到 B,B 也必须匹配回 A |
| RANSAC | 用几何模型反复抽样,剔除不满足对极约束的离群点 |
如果匹配阶段错得太多,后面的几何估计会全部崩掉。所以很多 SfM 工程问题,本质上都是“匹配图不够好”的问题。
四、对极几何
有了两张图片之间的匹配点后,SfM 会估计它们之间的相对几何关系。这里最核心的是对极约束:
$$ x_2^\top F x_1 = 0 $$
其中 $x_1$ 和 $x_2$ 是同一个三维点在两张图中的齐次像素坐标,$F$ 是基础矩阵。
这条公式表达了一个强约束:
给定图 1 中的点 $x_1$,它在图 2 中的对应点 $x_2$ 不可能出现在任意位置,而必须落在一条对极线上。
如果相机内参 $K$ 已知,可以进一步使用本质矩阵:
$$ E = K_2^\top F K_1 $$
本质矩阵可以分解出两台相机之间的相对旋转 $R$ 和平移方向 $t$。注意这里说的是平移方向,不是绝对距离,因为纯视觉 SfM 天生存在尺度不确定性。
在真实数据里,匹配点必然有噪声和误匹配。因此实际估计 $F$ 或 $E$ 时,通常会配合 RANSAC:
- 随机抽一小组匹配点;
- 估计一个候选 $F$ 或 $E$;
- 统计有多少匹配点满足对极约束;
- 重复多次,选择内点最多的模型。
这一步的意义很大:它把“看起来像”的特征匹配,变成了“几何上说得通”的匹配。
五、三角化
当两台相机的相对位姿已知,并且知道同一个点在两张图中的像素位置,就可以把两条观测射线反投影到三维空间。理想情况下,两条射线会在真实三维点处相交。
但真实世界不理想:
- 像素定位有误差;
- 镜头畸变不可能完全校正;
- 特征匹配可能有轻微偏移;
- 两条射线往往不会精确相交。
所以三角化通常求的是一个最小二乘意义下的三维点,使它投影回各个相机后的误差尽量小。
用投影矩阵 $P_i$ 表示第 $i$ 个相机,用三维点 $X$ 表示空间点,观测像素为 $x_i$,重投影关系可以写成:
$$ x_i \sim P_i X $$
三角化后还要做几个检查:
- 正深度检查:点必须在相机前方,不能跑到相机背后;
- 重投影误差检查:投影回图像后不能偏离观测点太远;
- 视差检查:两张图视角太接近时,三角化会非常不稳定。
这也是为什么拍摄 SfM 数据时,不能只原地转圈拍。原地旋转能估计相机朝向,但几乎没有平移基线,很难稳定恢复深度。
六、增量式重建
工程上最常见的是增量式 SfM。它不是一次性把所有照片全部解出来,而是先找一对靠谱的种子图像,再逐步扩张。
典型循环如下:
| 阶段 | 做什么 | 判断好坏的关键 |
|---|---|---|
| 选择种子对 | 找匹配多、视差足、内点比例高的两张图 | baseline 不能太小 |
| 初始化 | 分解 $E$ 得到相对位姿,三角化第一批点 | 正深度点足够多 |
| 注册新图像 | 用已知 3D 点和新图 2D 特征做 PnP | 2D-3D 对应足够多 |
| 三角化新点 | 新相机加入后,与旧相机共同生成更多 3D 点 | 视差和误差合格 |
| 局部 BA | 优化最近加入的相机和相关点 | 快速修正局部漂移 |
| 全局 BA | 间隔性优化所有相机和点 | 抑制累计误差 |
其中 PnP(Perspective-n-Point)解决的是这个问题:
已知若干个 3D 点 $X_j$,以及它们在当前图片中的 2D 投影 $x_j$,求这张图片对应的相机位姿 $R, t$。
PnP 是增量式 SfM 能“接上新照片”的关键。只要一张新图能和已有点云建立足够多的 2D-3D 对应,就可以估计它的相机位姿,再继续扩展重建。
七、Bundle Adjustment
前面的步骤会不断累积误差:匹配有误差、相机位姿有误差、三角化有误差。Bundle Adjustment(BA)就是 SfM 的全局校准器。
它的目标是最小化所有观测的重投影误差:
$$ \min_{{P_i}, {X_j}} \sum_{i,j} \rho \left(\left|x_{ij} - \pi(P_i, X_j)\right|^2\right) $$
其中:
- $P_i$:第 $i$ 个相机的参数,包括位姿,也可能包括内参;
- $X_j$:第 $j$ 个三维点;
- $x_{ij}$:第 $j$ 个点在第 $i$ 张图中的实际观测;
- $\pi(P_i, X_j)$:把三维点投影到第 $i$ 张图的预测位置;
- $\rho$:鲁棒损失函数,用来减小离群点影响。
BA 的直觉很简单:
如果某个三维点和相机位姿是对的,那么这个点投影回所有观测过它的照片时,都应该落回原来的特征点附近。
BA 很耗时,因为变量很多:几百张图、几十万个点、上百万条观测都很常见。但它的稀疏结构非常强:一个观测只连接一个相机和一个三维点。因此实际系统会利用稀疏矩阵和 Schur Complement 来加速求解。
没有 BA 的 SfM 往往会出现“局部看起来对,整体慢慢弯掉”的问题;有 BA 后,整个重建会被全局观测一起拉回更一致的位置。
八、尺度、失败场景与下游任务
8.1 尺度不确定性
单目 SfM 最大的限制是没有绝对尺度。如果所有相机平移和所有三维点坐标同时放大 10 倍,投影到图像上的像素位置不会变。
因此 SfM 输出的模型只能确定“相对形状”。要恢复真实尺度,需要额外信息,例如:
- 已知相机间基线;
- 标定板或已知尺寸物体;
- GPS / IMU;
- 深度传感器;
- 人工指定两点距离。
8.2 常见失败场景
| 场景 | 为什么难 | 典型表现 |
|---|---|---|
| 纹理少 | 找不到稳定特征 | 白墙、玻璃、纯色物体重建失败 |
| 重复纹理 | 匹配容易混淆 | 楼宇窗格、栅栏、瓷砖产生错误结构 |
| 动态物体多 | 几何假设被破坏 | 行人、车辆形成漂浮点 |
| 视角变化太大 | 特征外观差异大 | 匹配数量不足 |
| 基线太小 | 深度三角化不稳定 | 点云很扁、深度噪声大 |
| 纯旋转拍摄 | 没有平移视差 | 能拼全景,但难恢复三维 |
8.3 和 MVS、NeRF、3DGS 的关系
SfM 不是完整的三维重建终点,更像是几何地基:
- MVS(Multi-View Stereo):用 SfM 的相机位姿进一步估计稠密深度图、网格和纹理;
- NeRF:通常需要 SfM 提供训练图像对应的相机位姿;
- 3DGS:常用 SfM 输出的稀疏点云初始化高斯位置;
- 视觉 SLAM:和 SfM 共享很多几何模块,但更强调实时性和在线定位。
所以当你看到 COLMAP 生成的 sparse reconstruction 时,不要误以为“只重建出这么几个点”。它真正值钱的是:这些点和相机位姿已经形成了一个几何一致的坐标系。
九、总结
SfM 的主线可以压缩成一句话:
先用特征匹配建立跨图像对应关系,再用对极几何估计相机关系,用三角化恢复三维点,最后通过 BA 同时优化所有相机和点。
更工程化地看,它是一套不断回答下面三个问题的系统:
-
哪些像素来自同一个三维点?
靠特征匹配、track 构建和 RANSAC 过滤。 -
每张照片拍摄时相机在哪里?
靠两视图几何初始化,靠 PnP 增量注册新相机。 -
三维点到底在哪里最合理?
靠三角化生成初值,靠 Bundle Adjustment 全局修正。
理解了 SfM,再看 COLMAP、MVS、NeRF、3DGS 这些系统时,很多流程都会突然清晰起来:它们并不是孤立的技术,而是建立在同一套多视图几何地基之上。