Structure-from-Motion Pipelines
Every earlier part of this series built one piece of machinery in isolation — a fundamental or essential matrix between a pair (Part 6), a homography (Part 8), a triangulated point (Part 10), a resectioned camera (Part 11), a bundle-adjustment cost that cleans everything up at once (Part 15). A real system like COLMAP is the wiring diagram that turns those pieces into a pipeline: feed it an unordered folder of photos, and it has to decide which pairs overlap, where to start, how to grow one image at a time without drifting apart, and when to stop and re-optimize. This page is that wiring diagram, its "grow everything at once" alternative, and the honest fine print on what an uncalibrated reconstruction actually hands you before a further step called self-calibration is applied.
From "a folder of photos" to a reconstruction
Setup
Hand a real SfM system an unordered pile of JPEGs and it has no idea which ones overlap, in what order, or from where. Feature extraction and matching is the front end that this entire series has quietly assumed away — detect distinctive points in every image (SIFT, ORB, or a learned matcher) and match them pairwise — and it stays out of scope here; this page picks up exactly where a bag of candidate 2D–2D matches per image pair is already sitting on disk. What this page covers is everything from there to a globally consistent set of camera poses and 3D points: how to decide which candidate pairs are real, where to start, how to grow one image at a time without the whole thing drifting apart, a "grow everything at once" alternative, and what you actually get back when no camera was ever calibrated.
Geometric verification: from "probably overlaps" to "confirmed pair"
Stage 1 · turning matches into structure
Feature matching alone is naive — two images can share many "matches" that are pure coincidence (repeated texture, a matching algorithm's false positives) with no real geometric relationship behind them. Geometric verification is the fix: for every candidate pair, fit a fundamental matrix F (Part 6) or, if the shared scene is planar or the pair is a near-pure rotation, a homography H (Part 8) — both via RANSAC (Part 7), since the raw match list is full of outliers. A pair only survives if enough matches turn out to be RANSAC inliers of a consistent F or H.
This is the step that turns "these two images probably show the same place" into "these two images are related by an actual rigid geometric transform, and here is exactly which matches to trust." The surviving pairs and their inlier correspondences are the only raw material the rest of the pipeline touches — everything below assumes matches have already been verified this way.
Choosing the seed pair
Stage 2 · the one decision the whole reconstruction inherits
Incremental SfM needs to start somewhere: a single seed pair whose relative pose is recovered from E/F (Part 6/10), triangulated into the very first 3D points, and used to set the entire reconstruction's coordinate frame and scale. Every later camera and every later point is built relative to this pair, so a bad choice here can doom the whole reconstruction before it starts.
The naive criterion — "pick the pair with the most inlier matches" — is wrong on its own. A pair photographed from almost the same spot (near-pure rotation, or a tiny baseline) can have hundreds of clean inliers and still be nearly degenerate for triangulation: the two viewing rays to any 3D point are almost parallel, so a tiny bit of pixel noise swings the triangulated depth wildly (exactly the ill-conditioning Part 6 warns about for near-planar or near-rotation-only pairs). The real criterion balances two things at once:
i.e. among pairs with enough inlier matches, prefer the one with the widest baseline / largest triangulation angle — the angle at a 3D point between the two rays back to each camera — while still being well short of 180° (a pair that barely overlaps at all). Wide-enough-but-not-degenerate seed geometry is what gives the very first triangulated points, and therefore every PnP solve that follows, a stable numerical footing.
Growing the reconstruction, one image at a time
Stage 3 · the incremental loop
With the seed pair triangulated (Part 10), the pipeline repeats a loop until no image can be added:
2. Register it: solve its pose via PnP + RANSAC against the growing 3D point set (Part 11)
3. Triangulate new points visible in this image and at least one already-registered image (Part 10)
4. Local bundle adjustment: refine just the newest camera and recently-added points (Part 15)
5. Periodically: global bundle adjustment over every camera and point so far (expensive, done less often)
6. Retriangulation: retry points that failed or triangulated poorly earlier — often because their baseline was too small back then — now that more views are available
Step 2 is the direct callback to Part 11: every new image is a fresh PnP problem, using the 2D–3D correspondences between its own feature matches and points already sitting in the reconstruction — no two-view geometry needed for it at all, only 3D structure the pipeline already trusts. Local BA (step 4) keeps each registration step cheap by only touching what just changed; global BA (step 5) is the expensive but necessary occasional cleanup that lets far-apart parts of the reconstruction correct each other, which purely local refinement never does.
Working example: an incremental SfM run
Interactive
A small synthetic scene — 22 scattered 3D points and 7 cameras swept along an arc, each with a limited image frustum so no single camera sees every point — stands in for a real photo set. Click Step to advance the pipeline one stage at a time, or Run to play it through: geometric verification (inlier counts + triangulation angle per pair, computed for real from the synthetic correspondences), seed-pair selection, seed triangulation, then for every remaining camera a real DLT-based linear PnP solve (build the 2n×11 design matrix from its 2D–3D correspondences to already-reconstructed points, solve by least squares, then project the recovered rotation onto the nearest true rotation matrix), followed by triangulating newly-visible points and a Gauss-Newton point refinement pass.
Drag to orbit, scroll to zoom.
How incremental SfM breaks
Honesty
The incremental loop's core weakness is baked into its structure: every registration step only ever corrects the image and points it's currently touching.
Drift. Each PnP solve and each local BA pass has some small residual error. Over a long chain of hundreds or thousands of images, those small errors don't cancel — they accumulate, and the reconstruction can slowly curve away from the true camera path even while every individual step looked fine in isolation.
Broken loops. Walk a camera around a building and eventually it comes back to where it started. If drift has accumulated along the way, the incremental reconstruction's "return" doesn't line up with its "start" — the loop doesn't close, leaving a visible seam. Fixing this needs either periodic global BA catching up (expensive, only a partial fix) or explicit loop-closure detection that recognizes "this new image is actually revisiting an old one" and adds that constraint directly — the same problem the SLAM literature (Part 19) deals with online, in real time.
Left: top-down camera path. Black dashed = true circle; red = incremental registration with noisy odometry (the seam at the start/end is the broken loop); teal = after loop closure + pose-graph relaxation. Right: per-camera registration error vs. camera index, before (red) and after (teal) closure.
Degenerate seed pairs. Since every later camera and point is built relative to the seed pair's frame and scale, a seed pair chosen with too narrow a baseline (Step 2 above) doesn't just produce a bad first triangulation — it can leave the whole reconstruction poorly scaled and numerically unstable from the very first image onward, with no later step able to fully undo it.
The alternative: global SfM
Solve for everything at once, not one image at a time
Incremental SfM's drift is a direct consequence of solving cameras in sequence. Global SfM avoids that by solving for every camera pose simultaneously, in two decoupled averaging stages run over all the pairwise two-view estimates at once, rather than growing a chain:
2. Translation averaging — given many pairwise translation directions t⁅₁⁆₂ (only a direction, not a scale — Part 10's two-view scale ambiguity, now showing up per-pair across the whole graph), solve for globally consistent camera positions/scale
3. One global triangulation pass, then a single global bundle adjustment — not many rounds of local BA
Both averaging stages are consensus problems: rotation averaging is essentially finding the point in the rotation group SO(3) that's closest, on average, to a whole graph of noisy pairwise rotation constraints, and it uses the same robust-loss ideas from Part 15's bundle-adjustment cost to keep a handful of bad pairwise estimates from corrupting the whole average.
Hierarchical SfM is the middle ground: reconstruct small, well-connected clusters of images independently — with either method — then merge the clusters pairwise into one global reconstruction. It gets some of global SfM's parallelism without demanding that the entire averaging problem be well-conditioned all at once.
Stratified reconstruction: what "uncalibrated" actually gets you
The missing chapter — projective, affine, metric
Everything above quietly assumed known camera intrinsics K. Drop that assumption and correspondences alone can no longer pin down the true, metric scene. A camera P⁅ and a point X̃ only ever interact through the product P⁅X̃, so replacing every camera with P⁅H₄⁻¹ and every point with H₄X̃ leaves every image measurement unchanged for any invertible 4×4 matrix H₄ — an uncalibrated reconstruction is only projective. Self-calibration claws that ambiguity back in two conceptual stages: locate the plane at infinity π∞ to reach an affine reconstruction, then pin down the absolute dual quadric Ω*∞ — whose image in every camera is (up to scale) that camera's inverse image of the absolute conic ω = K⁻ᵗK⁻¹ (Part 1) — to reach a metric one.
Working example: projective vs. metric
Interactive
Six true cameras and a scattered cloud of 3D points are set up with known K. Apply H₄ injects a genuine projective distortion — a small plane-at-infinity vector v, so that X̃′ = H₄X̃ divides every point by v₳X + 1 — into both the cameras and the points at once, and recomputes reprojection error from scratch to prove it's still exactly zero. Upgrade (assume known K) then reconstructs a metric-ish shape from the distorted cameras alone, using nothing but the shared, known K.
Drag to orbit, scroll to zoom.
Cheat sheet
Recap
| Piece | What it is |
|---|---|
| Incremental pipeline | verify pairs (F/H + RANSAC) → pick seed pair (inliers + wide baseline) → triangulate seed → loop: PnP-register next image → triangulate new points → local BA → occasional global BA → retriangulate stragglers |
| Seed-pair criterion | most inliers subject to a large-enough triangulation angle — never the narrowest-baseline pair, however many inliers it has |
| Incremental failure modes | drift (small per-step errors compound over a long chain), broken loops (drift means a revisited viewpoint doesn't line up → global BA / loop closure), a degenerate seed pair poisoning the whole reconstruction's scale |
| Global SfM | rotation averaging (many pairwise R⁅₁⁆₂ → one R⁅ per camera) then translation averaging (many pairwise directions t⁅₁⁆₂ → global positions/scale), then one global triangulation + one global BA |
| Incremental vs. global trade-off | global avoids drift and parallelizes better, but one bad pairwise estimate corrupts the averaging directly, and it traditionally handles sparse/disjoint image sets less gracefully |
| Hierarchical SfM | reconstruct small well-connected clusters independently, then merge clusters — middle ground between incremental and global |
| Projective stratum | what correspondences alone give you with unknown K: true scene up to any invertible H₄ — P⁅X̃ = (P⁅H₄⁻¹)(H₄X̃) is unobservable |
| Projective → affine | pinned down by locating the plane at infinity π∞ (a transform is affine iff it fixes π∞) |
| Affine → metric | pinned down by the absolute dual quadric Ω*∞, whose image in each camera is (up to scale) ω⁻¹ = (K⁻ᵗK⁻¹)⁻¹ — ties straight back to Part 1's K/ω |