Skip to content
AI360Xpert
Beta

Homogeneous Coordinates in Vision

Homogeneous coordinates add one extra number to every point so that projection, rotation, and translation all become matrix multiplies. The rule is simple: scaled versions name the same point.

Scaling a homogeneous point changes no geometry: (4,2,2) and (2,1,1) both mean (2,1)
Scaling a homogeneous point changes no geometry: (4,2,2) and (2,1,1) both mean (2,1)

Why Does This Exist?

Translation cannot be written as a 3×33 \times 3 matrix acting on a 33-vector, and perspective projection needs a division by depth that no matrix can do either. Vision pipelines chain dozens of rotations, translations, and projections, so expressing each step in a different language would be unworkable. Appending one coordinate fixes both gaps at once: translations become matrix multiplies, and the projection P=K [R∣t]P = K\,[R \mid t] runs as a single multiply followed by one final divide.

The pure algebra of this trick is developed in homogeneous coordinates and projective geometry. This page covers what vision does with it: the projection pipeline, back-projection into rays, and the line arithmetic that later pages build on.

Think of It Like This

A recipe with servings attached

A recipe card reading "2 cups flour, 1 spoon sugar, serves 1" and another reading "4 cups flour, 2 spoons sugar, serves 2" describe the same dish. Divide by servings and both reduce to the same per-person recipe. The servings number is ww: it carries no flavor, only scale.

The analogy stops at zero servings: a card serving nobody is nonsense in a kitchen, but w=0w = 0 in geometry is a legitimate point at infinity, a pure direction with no location.

How It Actually Works

A 2D image point (x,y)(x, y) becomes the homogeneous triple (x,y,1)(x, y, 1), and in general (X,Y,W)(X, Y, W) with W≠0W \neq 0 names the point (X/W,Y/W)(X/W, Y/W). Any nonzero multiple names the same point, so (4,2,2)(4, 2, 2) and (2,1,1)(2, 1, 1) are two spellings of (2,1)(2, 1). Projection exploits this: x∼PXwx \sim P X_w computes a triple whose first two entries are only proportional to pixels, and the final divide by the third entry finishes the job. Going backwards, a pixel plus known KK recovers not a point but a ray: (Xc,Yc,1)(X_c, Y_c, 1) in camera coordinates is the whole line through the optical center, which is exactly why one view cannot fix depth.

Lines fit the same scheme. The line through points aa and bb is their cross product l=a×bl = a \times b, and the intersection of lines l1l_1 and l2l_2 is x=l1×l2x = l_1 \times l_2, all in homogeneous triples.

Worked numbers

Divide (2,1,1)(2, 1, 1) by w=1w = 1 to get (2,1)(2, 1), and (4,2,2)(4, 2, 2) by w=2w = 2 to get (4/2,2/2)=(2,1)(4/2, 2/2) = (2, 1): same point confirmed. For lines, take a=(1,0,1)a = (1, 0, 1) and b=(0,1,1)b = (0, 1, 1):

l=a×b=(0⋅1−1⋅1, 1⋅0−1⋅1, 1⋅1−0⋅0)=(−1,−1,1)l = a \times b = (0 \cdot 1 - 1 \cdot 1,\ 1 \cdot 0 - 1 \cdot 1,\ 1 \cdot 1 - 0 \cdot 0) = (-1, -1, 1)

which is the line −x−y+1=0-x - y + 1 = 0, or x+y=1x + y = 1. Check: aa gives 1+0=11 + 0 = 1 and bb gives 0+1=10 + 1 = 1. Both lie on it, as constructed.

Code

def to_cart(h: tuple[float, float, float]) -> tuple[float, float]:    return (h[0] / h[2], h[1] / h[2])  # divide by w

print(to_cart((2.0, 1.0, 1.0)))  # -> (2.0, 1.0)print(to_cart((4.0, 2.0, 2.0)))  # -> (2.0, 1.0)

Watch Out For

Forgetting the final divide

After x∼PXwx \sim P X_w the triple's third entry is depth, not 11. Using the raw triple as pixels scales every coordinate by depth, so near points explode off the frame and far points huddle at the origin. Always divide by the third entry before comparing against pixels.

Solving geometry in raw pixel coordinates

Direct solvers like DLT build matrices from raw pixel values in the thousands, which makes the system ill-conditioned: tiny noise steers the answer. Normalize first, translating points to zero mean and scaling to unit spread, solve, then map back. This is the Hartley trick, and homography estimation applies it before every fit.

The Quick Version

  • Appending w=1w = 1 turns translations and projections into matrix multiplies.
  • Nonzero scalings name the same point: (4,2,2)(4, 2, 2) is (2,1,1)(2, 1, 1) is (2,1)(2, 1).
  • Cartesian coordinates come back by dividing by ww.
  • w=0w = 0 encodes a direction, a point at infinity with no location.
  • Lines come from point cross products, and intersections from line cross products.
  • Back-projecting a pixel yields a ray, never a point: depth stays unknown.