Hough Transform
Stop hunting lines in the image and let every edge pixel vote for the lines that could pass through it. The busiest ballot boxes are the real lines.
Why Does This Exist?
Edge maps hand you disconnected pixels, and fitting lines through them directly breaks on the first gap, speck, or crossing. Hough (1962) inverted the problem: instead of grouping pixels into lines, each pixel votes for all compatible lines in a parameter space, and lines emerge as vote peaks that survive gaps and clutter. Lane finding, document borders, and industrial metrology still run on this vote.
Clean edges are the prerequisite, via edge filtering. This page covers the polar parametrization, the accumulator, and resolution tuning. Applied line segments get their own page (Hough lines), and round shapes get Hough circles.
Think of It Like This
An election among witnesses
Three witnesses glimpse a getaway car from different corners. Each lists every route consistent with what they saw: hundreds of possibilities per witness. Tallies accumulate, and one route appears on all three lists while every decoy appears once. The winner is not the cleverest testimony but the most corroborated.
It stops holding when ballots cost money. Every extra angle and radius bin multiplies the count, the same way the accumulator's memory and time explode with fine resolution. Democracy works, but the ballot printing bill is real.
How It Actually Works
Why slope-intercept fails first
The schoolbook line cannot represent vertical lines (infinite ) and stretches the parameter space unevenly. The polar form fixes both: , where is the line's normal angle and its distance from the origin. Every line, vertical included, is one finite point.
Voting in the accumulator
Fix an edge pixel and sweep through : each angle gives one , tracing a sinusoid of votes in parameter space. Repeat for every edge pixel into a 2D histogram, the accumulator. Pixels sharing a real line have sinusoids crossing at that line's , so true lines become bright peaks while noise spreads thin.
Verify with three pixels , , on the line . At , , and for all three. Three sinusoids, one crossing, peak with votes.
Resolution and threshold
Bin sizes set the deal: resolution near pixel and near suit most images. Finer bins split one line's votes across neighbours so nothing peaks; coarser bins merge distinct lines into one. The vote threshold then filters peaks: it must sit below the faintest true line's pixel count and above the clutter's accidental crossings.
Code
The three fixture pixels voting at degrees:
import math
pixels = [(0, 2), (1, 1), (2, 0)]theta = math.radians(45)rhos = [x * math.cos(theta) + y * math.sin(theta) for x, y in pixels]print(f"rhos {[round(r, 3) for r in rhos]}, votes at 1.414: {sum(abs(r - 1.414) < 0.001 for r in rhos)}")# -> rhos [1.414, 1.414, 1.414], votes at 1.414: 3All three sinusoids cross at , casting votes for one line.
Watch Out For
Voting on raw grayscale instead of edges
Symptom: the accumulator glows everywhere and every returned line is fiction, because thousands of textured pixels vote for thousands of accidental crossings. The transform assumes sparse edge input. Run Canny first, view the edge map, and only vote when a human can see the lines in the edges.
Splitting votes with over-fine resolution
Symptom: a strong obvious line returns nothing while a coarse rerun finds it instantly. Bins narrower than the edge localization error scatter one line's votes across neighbours so no cell crosses threshold. Start at pixel and degree, and coarsen before lowering the threshold.
The Quick Version
- Hough turns line finding into voting: each edge pixel votes for every line through it in space.
- The polar form represents every line, vertical ones included.
- True lines are accumulator peaks where many sinusoids cross; noise spreads its votes thin.
- Bin resolution near pixel and balances split votes against merged lines.
- It tolerates gaps and clutter but needs clean edges first, or every peak is fiction.