Topological Data Analysis in Vision
Counting holes survives stretching, bending, and noisy pixels. Topological data analysis tracks when components merge and loops form as a threshold sweeps, turning shape into a robust signature.
Why Does This Exist?
Geometric features are brittle. Stretch a digit and template distances explode; add speckle noise and corner counts double; change the threshold and a contour splits in two. Yet a human sees the same "8" through all of it, because the eye counts something stretch-proof: two holes.
Topological data analysis (TDA) counts exactly such invariants. Sweeping a threshold over an image, it records when connected components appear and merge and when loops form and fill. Features that persist across a wide threshold range mark true shape; flickering ones mark noise. The curved-surface sibling is manifold learning; the classical boundary tool is contour detection.
The one idea you need first. Betti numbers count holes by dimension: counts connected components, counts loops, counts enclosed voids. A disk has . A figure-8 (two loops sharing a point) has . Persistence tracks each feature's birth threshold and death threshold ; the lifetime separates signal from noise.
Think of It Like This
Islands in a rising flood
Watch a mountain range flood in slow motion. At first every peak is its own island ( large). Water rises, saddles drown, islands merge. Lakes form inside crater rims ( appears), then fill and vanish. Each island and lake has a water level where it is born and one where it dies.
Long-lived islands are the true mountains; ones that flash in and out between two close levels are pebbles. TDA is that flood watch applied to pixel brightness: the threshold is the water level, and persistence (death minus birth) tells mountains from pebbles without ever measuring a single slope.
The analogy stops at the terrain: images have no gravity, and the "water" is an arbitrary intensity sweep whose direction (bright-to-dark or reverse) must be chosen and reported.
How It Actually Works
Build a filtration: threshold the image at increasing levels, adding pixels as they pass. Maintain components with union-find as pixels join (births and merges) and track loops as they enclose and fill. Each feature becomes one interval plotted as a bar in the persistence barcode or a point in the persistence diagram. Long bars survive to the classifier; short bars near the diagonal are discarded as noise.
Barcodes feed models as vectorized summaries (persistence images, landscapes) or kernel comparisons. The pipeline is deterministic given the filtration, which makes TDA features unusually reproducible next to learned ones.
Worked example
Sweep brightness on a noisy figure-8 image with thresholds to :
- At the whole figure connects: one component born, .
- At the left loop encloses: first loop born. At the right loop encloses: second loop born. Now .
- At both loops fill with brightness: both die. Lifetimes: and .
- Speckle noise makes a tiny loop born at , dead at : lifetime .
Signal lifetimes (, ) tower over the noise (). Threshold anywhere sensible and the figure still reads : the count survives the exact threshold choice, which is the robustness classical features lack.
Code
The point of this snippet is the count: union-find over a tiny binary grid shows components merging deterministically.
def count_components(grid: list) -> int: parent = {}
def find(a: tuple) -> tuple: while parent[a] != a: # path halving keeps lookups short parent[a] = parent[parent[a]] a = parent[a] return a
for y, row in enumerate(grid): for x, v in enumerate(row): if v: parent[(x, y)] = (x, y) for nb in ((x - 1, y), (x, y - 1)): if nb in parent: # merge with lit neighbors parent[find((x, y))] = find(nb) return len({find(p) for p in parent})
print(count_components([[1, 0, 1], [1, 1, 0], [0, 0, 1]])) # -> 3Three lit groups stay three components however the grid stretches: the count is deformation-proof by construction.
Watch Out For
Persistence still needs a scale decision
"Long-lived" is relative: a lifetime of means nothing without the threshold range (– here) and the noise floor. Teams that threshold barcodes with a magic constant keep different features per dataset and wonder why results do not transfer.
Calibrate on noise: measure lifetimes on shuffled or pure-noise images and keep features clearly above that band. Report the cutoff rule with the result, or the signature is not reproducible.
Full filtrations cost more than the classifier
Naive persistence on megapixel images with fine threshold steps is heavy — easily slower than the downstream model it feeds. Running TDA at full resolution "for accuracy" routinely blows the feature budget.
Downsample first (topology survives), coarsen threshold steps, and restrict to regions of interest. The invariants are coarse by nature; computing them finely wastes the exact robustness you chose TDA for.
The Quick Version
- TDA sweeps a threshold and records births and deaths of components () and loops ().
- The worked fixture gives loop lifetimes and versus noise at : signal separates cleanly.
- Long bars mark true shape; short bars near the diagonal mark noise to discard.
- Calibrate the lifetime cutoff against noise images instead of using a magic constant.
- Downsample before filtering: topology is coarse, so fine computation wastes the budget.