1. 从稀疏到稠密:增量式SFM的工程化挑战
如果你做过三维重建,尤其是从无序的二维图像序列中恢复出相机姿态和稀疏点云,那么你一定绕不开增量式SFM(Structure from Motion)这个核心流程。它不像全局式SFM那样追求一次性求解所有参数,而是像搭积木一样,从两张图片开始,一张一张地“增量”添加,逐步构建起整个三维场景。听起来很直观,对吧?但当你真正动手去实现它,或者试图优化一个开源库(比如COLMAP)的某个环节时,你会发现,这个看似线性的流程背后,充满了工程上的权衡、数值上的陷阱和效率上的瓶颈。今天,我们就来彻底拆解这个流程,不聊那些教科书上的数学公式推导,而是聚焦于一个从业者视角下的“实现”:我们如何选择初始图像对?三角化失败的点怎么处理?BA(Bundle Adjustment)的尺度爆炸问题如何控制?以及,为什么说增量式SFM的核心是鲁棒性,而不仅仅是精度。
2. 流程总览:一个不断迭代的“建图-优化”循环
在深入细节之前,我们先建立一个宏观认知。一个完整的增量式SFM流程,本质上是一个循环。它始于一个精心挑选的“种子”,然后不断重复“注册新图像-三角化新点-全局优化”这三步。下图清晰地展示了这个迭代过程的核心步骤与数据流:
flowchart TD A[输入: 无序图像集<br>及特征匹配] --> B{初始图像对选择}; B --> C[两视图几何恢复与三角化]; C --> D[初始全局BA优化]; D --> E{是否还有未注册图像?}; E -- 是 --> F[下一最佳图像选择]; F --> G[新图像姿态估计<br>(PnP)]; G --> H[新观测点三角化]; H --> I[局部或全局BA优化]; I --> E; E -- 否 --> J[输出: 稀疏点云<br>相机参数];这个流程图就是我们的“行军地图”。接下来,我们将逐一攻克图中的每一个关键节点,从最开始的初始图像对选择,到循环内的下一最佳图像选择、姿态估计(PnP)、三角化,再到灵魂步骤BA优化,最后讨论如何处理失败与歧义。每个环节都有其“坑点”和工程上的最佳实践。
3. 万事开头难:如何选择“正确”的初始图像对
整个增量重建的稳定性,极大地依赖于初始化的质量。一个糟糕的初始图像对,会导致后续的BA难以收敛,或者重建出的模型尺度扭曲、甚至完全崩溃。
3.1 评价图像对质量的几个硬指标
选择初始对,不是简单地找匹配点最多的两张图。我们需要一个综合的评价体系:
- 匹配数量(Number of Matches):这是基础。通常需要一个较高的阈值(例如,COLMAP默认可能要求至少100个或更多的匹配点),以确保有足够的信息进行两视图几何估计。
- 匹配点分布的均匀性(Distribution):匹配点不能全部挤在图像的一个小角落。我们需要它们在图像平面上尽可能均匀分布。这可以通过计算匹配点的二维凸包面积,或者将图像划分网格并统计每个网格内的匹配数来实现。均匀的分布意味着更稳定的基础矩阵或本质矩阵估计。
- 视差(Parallax):这是最关键也最容易被忽视的指标。两张图像之间需要有足够的视差(即拍摄角度有差异)。视差太小,三角化出的点深度会非常大(趋于无穷远),导致数值不稳定,这就是所谓的“临界配置”。一个简单的判断方法是:三角化出一批初始点后,计算这些点的重投影误差的中位数。如果中位数非常小(比如小于0.5像素),但三角化点的深度却异常大,这很可能就是低视差导致的退化情况。
- 两视图几何的内点比率(Inlier Ratio):在通过RANSAC估计基础矩阵F或本质矩阵E时,内点的比例要高。一个高内点比率意味着匹配质量好,外点(错误匹配)少,估计出的几何关系更可靠。
3.2 工程实现中的策略
在实际代码中,我们不会对所有图像对进行全量评估,那复杂度是O(n²)。常见的策略是:
- 启发式筛选:首先用匹配数量过滤掉大部分明显不合格的对。
- 排序与尝试:对剩余的图像对,根据(匹配数 * 内点比率)等综合分数进行排序,从高分到低分依次尝试初始化。
- 尝试与回退:对每一个候选对,执行完整的初始化流程(几何估计、三角化、BA)。如果BA优化后,重投影误差过大、三角化成功率过低、或观测到尺度异常,则丢弃该结果,尝试下一个候选对。
这里有一个重要的工程细节:初始BA必须使用带鲁棒核函数(如Huber核)的重投影误差。因为初始三角化的点可能包含不少误差较大的点,鲁棒核可以抑制这些“外点”对整体优化结果的影响,防止BA被带偏。
4. 循环的核心:如何选择“下一张”要添加的图像
当有了一个不断增长的重建模型(一些已注册的相机和三维点)后,我们需要从剩余图像中选出一张最有利于模型扩展的图片加入。这就是“下一最佳图像选择”。
4.1 选择标准:信息增益与几何稳定性
理想的新图像应该满足:
- 能看到大量已有的三维点:这样我们可以用PnP(Perspective-n-Point)更稳定地估计它的相机姿态。
- 与现有模型有足够大的视差:以便三角化出新的、质量高的三维点。
- 与已注册图像有良好的共视关系,但又不是完全重叠:用于增强现有点的约束,提高其精度。
4.2 可量化的评分函数
一个广泛使用的评分函数是:Score = 看到的三维点数量 + λ * 可能三角化的新点数
- 看到的三维点数量:对于每张未注册图像,我们检查它与已注册图像的特征匹配。这些匹配中,如果某个特征点在已注册图像中已经被三角化为三维点,那么这个三维点就是该未注册图像的“潜在观测”。统计这样的三维点数量。数量越多,PnP越稳定。
- 可能三角化的新点数:同样通过特征匹配,找出那些在多张(至少两张)已注册图像中都有匹配,但尚未被三角化的特征点。这些点可以在新图像注册后,与已注册图像一起进行三角化。λ是一个权重因子,用于平衡“注册稳定性”和“模型增长”。
在COLMAP的实现中,这个过程非常高效:它维护了一个从三维点到图像的索引,以及一个从图像特征到三维点的索引,可以快速地进行上述统计。
4.3 工程上的权衡
有时,得分最高的图像可能因为某些原因(比如光照突变、遮挡严重)导致PnP失败。因此,一个健壮的系统需要实现“尝试-回退”机制:如果对得分最高的图像进行PnP和三角化失败,或者导致BA结果急剧变差,应该将其标记为“失败”并尝试得分次高的图像。
5. 新图像的注册:从PnP到三角化的细节
选定了新图像,下一步就是确定它在这个已建三维世界中的位置和朝向(姿态)。
5.1 PnP:姿态估计的基石
PnP问题求解新相机的旋转R和平移t。通常使用RANSAC框架下的EPnP或UPnP等直接线性方法求解初值,再用非线性优化(如高斯-牛顿法)精化。
注意:这里有一个关键陷阱——尺度。在增量SFM中,所有已重建的三维点和相机平移都是在同一个“未知尺度”下的。新图像通过PnP求解出的平移t,其尺度必须与现有模型的尺度一致。PnP算法本身无法恢复绝对尺度,它求解出的t的模长是任意的。因此,在将PnP结果融入现有模型前,必须进行尺度对齐。通常的做法是:计算PnP解出的三维点到新相机光心的距离,与这些点在现有模型中的深度值,求取一个尺度因子s,然后用s对PnP求解的平移t进行缩放。
5.2 三角化:创造新的地图点
新图像注册成功后,我们就可以利用它和已注册图像之间的匹配,三角化出新的三维点。这里常用的方法是多视图三角化,而不仅仅是两视图。
- 线性三角化(DLT):对于每个匹配点对,可以构建一个线性方程组(Ax=0)。多视图情况下,可以将所有视图的方程堆叠起来,通过SVD求最小二乘解。这是最基础的方法。
- 中点法:对于两视图,可以求两条射线在空间中的最近点,取其中点。这种方法对噪声更鲁棒一些。
- 最优三角化(非线性优化):将三角化问题建模为一个非线性优化问题,直接最小化重投影误差。这是精度最高的方法,但计算量也最大。通常的做法是:用线性方法或中点法得到一个初始的三维点,然后用高斯-牛顿或莱文贝格-马夸特方法迭代优化。
5.3 三角化的质量检查
不是所有三角化出来的点都是可靠的,必须设置严格的验收条件:
- 重投影误差:点在所有观测到的图像中的重投影误差必须小于阈值(如4-6个像素)。
- 三角化角度:观测到该点的所有相机光心与该点形成的夹角不能太小。太小的夹角会导致深度估计极度不确定(深度的不确定性与1/sin(夹角)成正比)。通常要求夹角大于2度。
- 深度为正(Cheirality Constraint):点必须在所有观测到它的相机前方。
- 离群点过滤:可以使用基于统计的方法(如计算所有点重投影误差的分布,剔除3σ以外的点)。
6. 捆绑调整:增量式SFM的“定海神针”
BA是SFM的灵魂,它通过非线性优化同时精化所有三维点坐标和相机参数,使整体重投影误差最小。在增量式流程中,BA的调用策略直接影响效率和精度。
6.1 全局BA vs 局部BA
- 全局BA:优化所有已注册的相机和所有三维点。精度最高,但计算量随模型规模增长呈立方级增长,非常耗时。通常在初始化后、或每添加若干张(如10-20张)图像后执行一次。
- 局部BA:为了平衡效率和精度,更常用的策略是局部BA。当新图像注册后,我们只优化:
- 新注册的相机。
- 与新图像有共视关系(即能看到相同三维点)的一部分旧相机(例如,共视关系最强的k个,或共视点数量超过阈值的)。
- 这些相机所观测到的所有三维点。 这样,优化的变量规模大大减小,可以更频繁地执行(例如每添加一张图像就执行一次),及时修正误差,防止其累积。
6.2 BA的工程实现要点
- 参数化:
- 相机旋转:使用李代数so(3)(轴角)或四元数进行参数化,避免欧拉角的万向节锁问题。
- 三维点:直接使用三维坐标 (X, Y, Z)。
- 内参:如果标定已知,则固定;如果未知,可与姿态一起优化(自标定)。
- 鲁棒核函数:必须使用Huber或Cauchy等鲁棒核函数。重投影误差服从长尾分布,存在不少误差较大的误匹配,鲁棒核可以降低这些“外点”的权重,防止它们破坏优化。
- 稀疏性利用:BA的雅可比矩阵和海塞矩阵是高度稀疏的(一个点只被少数相机看到,一个相机只看到少数点)。必须使用稀疏线性代数库(如SuiteSparse, Eigen的稀疏模块)来求解增量方程
HΔx = -b,这是BA能够处理大规模问题的关键。 - 先验与正则化:对于序列图像,可以加入运动平滑性先验;对于漂移问题,可以考虑加入闭环检测后的位姿图优化作为BA的约束。
6.3 尺度漂移与处理
纯视觉增量式SFM没有绝对尺度信息,且误差会累积,导致重建的模型尺度缓慢变化(漂移)。虽然这不影响模型的相对几何正确性,但会影响观感和后续应用。处理尺度漂移的一个实用技巧是:在局部BA中,固定一个“基准”相机或一组“基准”点的尺度。例如,固定初始图像对的基线长度为1,或者在优化中固定某个点的深度值。这相当于为优化问题引入了一个软约束,抑制尺度的自由漂移。
7. 失败处理与歧义解除:让系统更健壮
即使上述每个步骤都小心翼翼,失败仍会发生。一个工业级的SFM系统必须有完善的异常处理机制。
7.1 图像注册失败
当对新图像执行PnP失败(内点太少)时:
- 记录失败:将该图像标记为“注册失败”,放入一个待重试队列。
- 分析原因:可能是特征匹配质量差,也可能是该图像视角过于独特,与当前模型重叠区域太少。
- 后续重试:当模型随着更多图像的加入而扩大后,之前失败的图像可能会因为看到了更多已重建的点而变得可以注册。因此,需要定期扫描失败队列,用更新后的模型重新尝试注册。
7.2 三角化点质量监控
在每次三角化后和BA优化后,都要对三维点进行质量检查:
- 过滤 outlier:基于重投影误差统计,动态剔除误差过大的点。
- 处理“大误差点”:有些点可能在大部分视图中投影很好,但在某一两个视图中误差巨大。这可能是由于该视图中的特征匹配错误,或者该点在该视图被遮挡/反射。BA中的鲁棒核函数会处理这类点,但在后期清理时,可以考虑直接删除该点在该问题视图中的观测,而不是删除整个点。
7.3 闭环检测与全局一致性
对于长序列或大规模无序图像,增量式SFM必然会产生累积误差,导致模型扭曲。闭环检测是解决这一问题的关键。当系统发现一张新图像与一个很早之前注册的图像(非临近图像)匹配非常成功时,就检测到了一个“闭环”。
- 位姿图优化(Pose Graph Optimization):检测到闭环后,可以先不进行昂贵的全局BA,而是进行位姿图优化。位姿图只优化相机位姿节点,忽略三维点,将闭环约束(相对位姿变换)作为边加入图中进行优化,可以快速、有效地纠正累积的旋转和平移漂移。
- 基于位姿图优化的结果,再触发一次全局BA,可以获得全局一致且高精度的模型。
8. 核心数据结构与性能优化
一个高效的增量式SFM实现,离不开精心设计的数据结构。
8.1 核心数据管理
- 图像(Image):存储图像元数据(路径、尺寸)、特征点、描述子。
- 相机(Camera):存储内参模型(如针孔、径向畸变)和参数值。
- 三维点(Point3D):存储三维坐标、颜色、以及一个关键的数据——观测列表(Observations)。这是一个列表,记录哪些图像(Image)的哪个特征点(Point2D)观测到了这个三维点。这是实现快速共视查询的基础。
- 特征索引:需要建立从图像特征点到三维点的反向索引,以及从三维点到图像特征点的正向索引。这在三角化、BA雅可比矩阵构建、下一最佳图像选择等步骤中至关重要。
8.2 加速策略
- 特征匹配缓存:图像间的特征匹配是耗时操作。一旦计算好,应将其序列化到磁盘,避免重复计算。
- 共视图(Covisibility Graph):维护一个图,节点是图像,边的权重是两幅图像共视的三维点数量。这个图对于快速查找与某张图像共视最强的k张图(用于局部BA)极其高效。
- 增量式BA求解:当模型只有微小变化时(如新增一张图像),可以利用之前BA求解的海塞矩阵H的因子分解结果,进行低秩更新或迭代求解,而不是从头开始因子化。这属于比较高级的优化技巧。
- 并行化:特征提取、特征匹配、三角化中的多个独立任务都可以并行处理。
实现一个鲁棒、高效的增量式SFM系统,是一个将多视图几何理论、非线性优化、软件工程和大量经验性调参紧密结合的过程。它没有唯一的“标准答案”,每个环节的阈值选择、策略权衡都需要在具体的数据集上进行验证和调整。理解这个核心流程的每一个环节及其背后的考量,是掌握三维重建技术从理论走向实践的关键一步。