Reading and display settings

Appearance

System follows your operating system and keeps following it, even if you change it later. The header's sun, moon and monitor cycle the same three options.

Text size (%) 100%

Default. Scales every text size on the site, equations and tables included.

Reading width 70ch

How much text runs across one line of prose. Narrower is easier to track; wider fits more on screen.

Line spacing 1.6

The leading on body text. Taller leading helps a tired eye stay on the line.

Density

Padding and gaps around controls, cards, and tables — how much breathing room the layout leaves itself.

Motion

System follows your operating system. Reduced removes every transition on this site. Full keeps them on unless your system asks for less.

0

The problem, stated precisely

Setup

You have N 3D points X₁, …, Xₙ whose world coordinates are already known — they came from a prior triangulation, a scan, or an earlier reconstruction. You have one new image, and for at least some of those points you know which pixel x₁, …, xₙ each one landed on — the matching itself isn't this page's job, only what to do once it's done. You also know the camera's calibration K (Part 5). What you don't know is the camera's pose: the rotation R and position it was sitting at when it took this photo. PnP is the problem of finding the (R, t) that makes all of these consistent at once:

x̃ₓ ≅ K [R | t] X̃ₓ   for every correspondence i
💡 Contrast with Parts 6 and 10: there, both cameras' positions were unknown and you had only 2D–2D pixel correspondences between them — you had to solve for relative pose via E/F, and the reconstructed 3D structure only existed up to an unknown overall scale (and, for F alone, an unknown projective distortion). Here the 3D structure is already fixed and metric, and you're solving for one camera against it. That's a fundamentally easier, better-conditioned problem — with enough points, PnP has a closed-form linear solution and no scale ambiguity at all, since the known Xₓ already fix the scale.
1

DLT resection: the same trick, a third time

Foundations

The unknown here is the 3×4 camera matrix P = K[R | t] — 12 numbers, but only 11 independent (it's defined up to an overall scale). Exactly the same cross-product idea used for homography DLT (Part 8) and the 8-point algorithm (Part 6, epipolar) applies directly: since x̃ₓ and PX̃ₓ point the same direction, their cross product vanishes.

x̃ₓ × (P X̃ₓ) = 0

Writing P's three rows as 4-vectors p₁ᵀ, p₂ᵀ, p₃ᵀ stacked into one 12-vector p, and xₓ = (xₓ, yₓ) for the observed pixel, two of the three cross-product components are independent and linear in p:

[ −X̃ₓᵀ  0ᵀ   xₓX̃ₓᵀ ] p = 0
[ 0ᵀ   −X̃ₓᵀ  yₓX̃ₓᵀ ] p = 0

Two rows per correspondence, 12 unknowns up to scale (11 DOF) — so 6 correspondences (12 equations) pin it down exactly; more than 6 gives an overdetermined, noise-averaging system. Stack every correspondence's row pair into one matrix A and the whole problem collapses to the same homogeneous least-squares shape this series keeps returning to — the 8-point algorithm's Af = 0, the homography DLT's Ah = 0, and now:

A p = 0  —  solve by SVD: p = the right-singular vector of the smallest singular value of A

Reshape the resulting p back into the 3×4 matrix P. Because K is already known here (unlike Part 4's calibration problem, where K is what RQ-decomposition extracts from P's leading 3×3 block), the same decomposition runs in reverse: use K to strip the calibration out of P instead of reading it off. In practice it's simplest to normalize the pixels first — multiply every observation by K⁻¹ to get calibrated rays — so the DLT solves directly for [R | t] (up to scale), and only R needs cleaning up:

M = leading 3×3 block of the recovered [R | t]  (≈ s·R for some scale s, but not exactly orthogonal — noise)
R = polar(M) = M (MᵀM)⁻¹⁄²  —  the nearest proper rotation to M, by SVD/polar decomposition
t = p₄ / s    s = mean singular value of M
⚠️ Why polar decomposition, not RQ: Part 4's RQ-decomposition of P's leading block gives both an upper-triangular K and an orthogonal R, because that block is KR with K unknown. Here K was already divided out before the DLT ran, so the leading block should be exactly s·R for a pure rotation R — it just isn't, exactly, because of pixel noise. Polar decomposition finds the closest orthogonal matrix in the least-squares sense, which is the right tool once K is no longer part of the unknown.
2

Play: resect a camera from noisy 2D–3D correspondences

Interactive

🎯 Learning goal: the black camera is the (hidden, ground-truth) pose that generated the observations; the colored one is recovered from nothing but the 3D points and their noisy pixels, live, by the DLT + polar-decomposition code above. Crank up the noise or outlier fraction and watch it drift — then switch on Gauss–Newton refinement and watch it snap back.

14 known 3D points (think: a small patch of an existing reconstruction) are observed by one new camera. Pixel noise and a few injected outliers (mismatches) are added to the observations before the solver ever sees them — exactly the situation a real resection step is in.

Drag to orbit, scroll to zoom.

Observed pixels fed to the solver (orange = outlier draws).

Gauss–Newton convergence: total reprojection cost vs iteration. Pure least squares dives fast when the outliers are absent — drag the outlier slider up and watch it plateau at a wrong answer, no matter how many steps it takes.

true camera DLT-recovered (raw) after Gauss–Newton refinement

With zero noise the raw DLT solve is already essentially exact — there's nothing to refine. Push noise up and the raw estimate drifts measurably; refinement (a handful of Gauss–Newton steps minimizing true reprojection error, the same machinery as Part 10's nonlinear triangulation) pulls it most of the way back. Add outliers, though, and refinement stops helping — a few wildly wrong correspondences drag the least-squares solve off no matter how well you polish it afterward. That's the motivation for the RANSAC demo below.

3

P3P: the minimal solver, and a third instance of a familiar pattern

Foundations

DLT needs 6 points. But given K, the true minimum is 3 — and getting there means leaving linear algebra behind for plain geometry. Three known 3D points X₁, X₂, X₃ and the (unknown) camera center C form a tetrahedron. The three known inter-point distances dₓᵀ (from the 3D points themselves) are fixed; the three angles θₓᵀ between the viewing rays C→Xₓ and C→Xᵀ are also known — they come straight out of K and the observed pixels, with no camera pose needed at all, since an angle between two rays through the same center doesn't depend on where that center is. What's unknown are the three distances from the camera to each point, d₁ = |CX₁|, d₂, d₃. The law of cosines in the triangle C, X₁, X₂ ties them together:

d₁² + d₂² − 2 d₁d₂ cos θ₁₂ = d₁₂²

The same relation holds for the (1,3) and (2,3) pairs, giving three simultaneous quadratics in d₁, d₂, d₃ — known coefficients (θₓᵀ, dₓᵀ), three unknowns. Eliminating variables between the three equations produces a single quartic polynomial in one of them, which has up to 4 real roots — so P3P alone hands back up to 4 algebraically valid camera poses, all consistent with the same three rays and three distances.

⚠️ A pattern you've now seen three times. Part 8's homography decomposition gave 2 candidate poses off a flat scene; Part 6's essential-matrix decomposition gave 4 candidates for relative pose. P3P's quartic is the same story a third time: a minimal solver, working from too little information to be unambiguous by itself, algebraically returns several equally valid answers — and one extra piece of information (a cheirality check, a positive-depth check, or here a 4th point) is what picks the physical one. With a 4th correspondence in hand, whichever of the ≤4 candidates reprojects it with the least error wins.
🎯 Learning goal: three points + the ray angles from K, solved for camera-to-point distances — watch the solver hand back every algebraically valid pose as a frustum, then add the 4th point and see it pick the winner. Slide the camera azimuth to change the ray geometry and watch the candidate count change — the quartic really does have a variable number of real roots.

Drag to orbit. Black frustum = ground truth; colored frustums = P3P candidates (up to 4); the winner by 4th-point reprojection is drawn brighter with a dashed line to the 4th point.

The three law-of-cosines quadratics are solved numerically here (multi-start Newton on the distance triplet, clustered); the textbook route eliminates them into one closed-form quartic in a single variable. Same roots, either way.

4

EPnP: linear again, and fast for any N

Foundations

P3P is minimal but ambiguous; DLT is unambiguous but its cost grows with N unknowns tied one-to-one to the points. EPnP is the standard middle path used by real systems (it's what OpenCV's solvePnP reaches for by default, and what runs inside solvePnPRansac's minimal-sample loop for larger samples). The key idea: instead of treating each of the N 3D points as its own unknown, pick just 4 virtual control points once, and express every real 3D point as a fixed weighted combination of those 4 (barycentric-style coordinates, computed once from the known Xₓ and frozen for the rest of the solve). The problem of "where is every one of these N points, really, in the camera's frame" becomes "where are these 4 control points" — 12 unknowns total, regardless of how large N is. That turns what would otherwise be an N-scaling problem into a small, fixed-size linear system solved once per image, which is exactly why EPnP is the fast, accurate default for N ≥ 4 points in production structure-from-motion and SLAM pipelines.

5

PnP + RANSAC: how a camera actually gets registered

Foundations

Real correspondences come from feature matching, and feature matching produces outliers — wrong matches that no amount of least-squares polishing will fix, exactly as the demo above showed. The fix is the same one Part 7 used for the 8-point and homography DLT: wrap the minimal solver in RANSAC. Draw a minimal sample (3 points for P3P, plus a 4th to disambiguate its candidates — or, as this page's demo does, 6 points for a minimal DLT solve, which needs no disambiguation step but is otherwise the identical loop), solve for a candidate pose, count how many of the other correspondences it reprojects within a small pixel threshold, and repeat. Whichever candidate wins the most inliers gets refined — typically with EPnP or a nonlinear reprojection-error minimization like the Gauss–Newton step above — on its full inlier set, and that refined pose is the camera's registered position.

💡 This is the actual registration step. Every time an incremental structure-from-motion pipeline (Part 16) adds one more photo to a growing reconstruction, this loop — match 2D features against the existing 3D point cloud, run PnP+RANSAC, refine — is how that new camera's pose gets found.
6

Play: PnP + RANSAC against contaminated correspondences

Interactive

🎯 Learning goal: resect naively on every correspondence (outliers included) and compare it to a RANSAC loop that discovers which correspondences are trustworthy on its own.
⚠️ Simplification, stated plainly: this demo's RANSAC loop draws minimal samples of 6 points and solves them with the DLT resection above, not a true 3-point P3P solver with its quartic. The RANSAC logic — sample, solve, count inliers, keep the best, refine — is identical either way; only the algebra inside "solve" differs, and P3P's is a further layer this page describes but doesn't implement from scratch.

Same 14-point cloud and true camera as above, plus a controllable outlier fraction. "Naive DLT" resects on every correspondence at once. "PnP + RANSAC" repeatedly resects on random 6-point samples, scores each by reprojection inliers, and refines the winner on its inlier set.

Drag to orbit. Point color: green = RANSAC inlier, red = outlier.

The image plane: observed pixels colored by the RANSAC verdict (green = inlier, red = outlier, gray = not yet scored).

true camera naive DLT (all points) PnP + RANSAC (refined)
✓

Cheat sheet

Recap

MethodCore ideaMin. pointsAmbiguity / notes
DLT resectionx̃ₓ×(PX̃ₓ)=0 → 2 linear rows per point in P's 12 entries, solved by SVD null vector6Unique (up to noise); decompose with K known via polar decomposition, not RQ
P3PLaw of cosines on the 3-point–camera tetrahedron: d₁²+d₂²−2d₁d₂cosθ₁₂=d₁₂², ×3, eliminated to one quartic3 (+1 to disambiguate)Up to 4 algebraic solutions — same pattern as H's 2 and E's 4 candidates
EPnPExpress all N points via 4 virtual control points → 12 unknowns regardless of N → linear, O(N)4Standard fast/accurate default (e.g. OpenCV's solvePnP)
PnP + RANSACMinimal solver (P3P, or DLT with a larger sample) inside a RANSAC loop; refine the inlier-set winner3–6 per sampleHow every new camera gets registered into a growing SfM reconstruction (Part 16)
Two views constrain a point to a line, then a point. Add a third view and there's an even tighter constraint — one that can predict a point's location without any triangulation at all. Continue: the trifocal tensor →