Homography: Mapping Planes Across Views
A homography is a 3 by 3 matrix mapping one plane to another: the same wall seen from two angles, or a tilted document photographed and flattened. It holds only while everything mapped stays flat.
Why Does This Exist?
Walls, roads, documents, and sports fields are planes, and planes map to planes with a simple linear rule where general scenes need the heavier fundamental matrix. Whenever the world cooperates by staying flat, one matrix aligns the views, flattens the document, or stitches the panorama. This page covers when that deal holds, what contains, and how to read a decomposition. The fitting machinery, DLT inside RANSAC, lives in homography estimation and is not repeated here.
Think of It Like This
A slide projector sheet
Tilt a projector sheet and its rectangle keystones into a trapezoid; the keystone recipe that restores it is fixed by the tilt. That recipe is : four corner pins determine the whole warp, and every interior point follows.
The analogy stops at crumpling: tilt the sheet and one recipe still fits, but crumple it and no single matrix works. Flatness is the whole contract, and parallax is the crumple.
How It Actually Works
A homography maps homogeneous points up to scale: , with a matrix carrying degrees of freedom (nine entries minus global scale). It applies exactly when the mapped points share a plane, or when the camera purely rotates (, which is why panoramas stitch without any plane at all). For a plane with normal at distance , the plane-induced form is , with and the relative motion. Decomposing recovers that motion plus the plane, up to a twofold ambiguity resolved by visibility, the same cheirality idea behind essential-matrix decomposition.
Worked numbers
Pure image translation by is the homography with , , and last row . Apply it to :
Every point shifts identically, the signature of a translation. A tilt would instead show the bottom row bending parallels together toward a vanishing point.
Code
def apply_h(pt: tuple[float, float]) -> tuple[float, float]: return (pt[0] + 2.0, pt[1] + 1.0) # translation homography, last row (0, 0, 1)
print(apply_h((3.0, 4.0))) # -> (5.0, 5.0)Watch Out For
Fitting H to a non-flat scene
With real depth variation, no single aligns both the foreground and the background: warping by one plane ghosts the other. The symptom is double images after alignment that no amount of RANSAC tuning removes. Confirm planarity first; with parallax, graduate to two-view geometry via the fundamental matrix or stereo vision.
Trusting a decomposition without disambiguation
Decomposing returns multiple candidates, and most place the plane behind a camera or facing away. Accepting the first candidate flips the wall inside out. Keep only the solution with the plane visible in front of both views, exactly as the cheirality check does for .
The Quick Version
- A homography is a matrix with degrees of freedom mapping .
- It holds for planar scenes and for pure camera rotations, and nothing else.
- The plane-induced form exposes the motion and the plane.
- Fitting from point pairs belongs to homography estimation, linked above.
- Decomposition needs a visibility check to pick the physical solution.
- Parallax breaks single- alignment; that failure selects two-view geometry instead.