Disparity Estimation in Stereo Pairs
Slide the right photo over the left until each patch lines up. The size of that slide per pixel is the disparity, and big slides mean close things.
Why Does This Exist?
Epipolar geometry and stereo matching cuts the search to one row, but something still has to pick the winning pixel on that row. That picker is disparity estimation: for every left pixel it scores candidate right pixels and keeps the shift with the lowest cost. Without it there is no disparity map, and without a disparity map the depth formula has no input.
This page covers the matching cost and the search itself. The geometry behind the rows lives in epipolar geometry; the depth conversion and its failure modes live in stereo vision.
Think of It Like This
Sliding tracing paper over a drawing
Lay tracing paper over a drawing and slide it right until the outlines line up. The distance you slid is the answer, and flat blank areas never snap into place because any position looks the same.
Pixels are the drawing and the matching cost is your eye judging alignment. The analogy stops at lighting: paper never changes brightness while real cameras auto-expose, which is why raw brightness costs fail across views.
How It Actually Works
After rectification every match sits on the same image row, so disparity is a one-dimensional shift. A data cost such as sum of absolute differences over a small window scores each candidate ; semi-global matching adds a smoothness penalty that charges for a one-pixel disparity jump and for larger jumps, then optimizes along several path directions. The winner-takes-all disparity is , followed by a left-right consistency check that keeps only matches both views agree on.
Depth follows from similar triangles as , where is focal length in pixels and is baseline in meters. Error grows with : differentiating gives .
Worked numbers
Take px, m, and measured disparity px. Then m. A one-pixel mistake to gives m, off by about m. At px (about m out) the same one-pixel error swings depth by roughly m, which is why far stereo is brittle.
Code
def depth_from_disparity(d: float, f: float, baseline: float) -> float: return f * baseline / d # similar triangles on a rectified pair
print(depth_from_disparity(80.0, 800.0, 0.12)) # -> 1.2print(depth_from_disparity(79.0, 800.0, 0.12)) # -> 1.2151898734177216Watch Out For
Matching textureless walls by cost alone
On blank paint every candidate shift scores nearly the same, so winner-takes-all returns noise with high confidence. The symptom is speckled disparity across walls and floors. Fix it with a larger window, a smoothness penalty, or an active texture projector instead of trusting the raw argmin.
Reading far disparity as exact depth
One pixel spans meters, so a single-pixel disparity error at m outweighs a ten-pixel error at m. Clamp the usable range by baseline, and never feed far stereo depth into grasping without inflating its uncertainty.
The Quick Version
- Disparity estimation scores candidate shifts along each rectified epipolar row.
- The disparity map holds per pixel for the depth formula.
- Semi-global matching adds smoothness penalties and to tame noise.
- Depth is , with error growing as range squared.
- With 800 and baseline m, disparity means depth m.
- Blank regions and far range are the two standard failure modes.