Stereo Vision and Depth

A robot arm needs to know how far away a cup is before it can grasp it. A phone needs to blur the background of a portrait photo without a laser rangefinder. A self-driving car needs to know whether the shape ahead is ten meters away or one hundred. All three problems share a solution that predates digital computers. Look at the same scene from two different viewpoints. Use the geometric disagreement between the two images to recover distance. This is stereo vision, and it is one of the oldest and most thoroughly understood problems in computer vision. The core insight is deceptively simple. If you know exactly where two cameras are relative to each other, and you can find the same physical point in both images, then simple trigonometry tells you how far away that point is. The entire difficulty of stereo vision reduces to one question. For a pixel in the left image, which pixel in the right image shows the same 3D point? This correspondence problem is what almost the entire field is built around solving, efficiently and robustly. This article builds on Epipolar Geometry, which tells us that once a stereo pair is calibrated and rectified, the search for a corresponding point collapses from a 2D image search to a 1D search along a single scanline. It also builds on Camera Calibration, which supplies the intrinsic and extrinsic parameters needed to convert a pixel disagreement into a physical distance. Given those two ingredients, this article delivers the complete disparity-estimation pipeline. It covers rectification, matching cost design, and the aggregation and optimization algorithms. These algorithms turn noisy per-pixel costs into a clean disparity map. It also covers the triangulation formula that converts disparity into depth. Stereo depth estimation is the direct ancestor of two much larger ideas covered elsewhere. These are multi-view geometry with more than two cameras, and eventually Structure from Motion. In Structure from Motion, the camera poses themselves are unknown. They must be recovered jointly with the scene structure. Understanding two-view stereo carefully is the cleanest way to understand the harder, more general problem.

Key takeaways
  • Stereo vision recovers depth by finding corresponding points in two calibrated images and triangulating.
  • After rectification, correspondence search is 1D: matching pixels lie on the same scanline, differing only in horizontal offset (disparity).
  • Depth is inversely proportional to disparity: Z=fB/dZ = fB/d, so nearby objects shift more between views than distant ones.
  • Matching costs (SAD, SSD, normalized cross-correlation, census) score candidate correspondences; aggregation and global optimization (dynamic programming, graph cuts, semi-global matching) resolve ambiguity.
  • The aperture problem and occlusions are the fundamental sources of error; textureless regions and depth discontinuities are the classic failure modes.
  • Stereo is the two-view special case of the more general multi-view reconstruction problem behind Structure from Motion.
The core insight

Once images are rectified, depth reduces to measuring a single horizontal pixel shift (disparity), and geometry converts that shift into distance via Z=fB/dZ = fB/d.

The idea

Stereo vision estimates the depth of scene points by comparing two (or more) images taken from different, known viewpoints. The central geometric quantity is disparity: the difference in horizontal image position between the projections of the same 3D point in the left and right images of a rectified stereo pair. Rectification is a pair of image warps, computable from calibration, that make the epipolar lines horizontal and aligned, so that a point at row yy in the left image can only correspond to a point at the same row yy in the right image. This is the payoff promised by Epipolar Geometry: the correspondence search drops from two dimensions to one. Formally, for a rectified pair with baseline BB (the distance between the two camera centers) and focal length ff (in pixels, identical for both cameras after rectification), a 3D point at depth ZZ projects to horizontal coordinates xLx_L and xRx_R in the left and right images satisfying

xL−xR=d=fBZ.x_L - x_R = d = \frac{fB}{Z}.

Here dd is the disparity. Solving for depth gives the triangulation equation

Z=fBd.Z = \frac{fB}{d}.

This single equation is why stereo works: disparity and depth are related by simple inverse proportionality, calibrated entirely by quantities (ff, BB) known in advance from Camera Calibration. The practical problem — the one that consumes the algorithmic effort — is estimating dd at every pixel from image content alone. That is the correspondence problem, and it is fundamentally ambiguous: a single pixel in isolation, especially in a textureless or repetitive region, may match equally well to many candidates. Resolving this ambiguity is what distinguishes local, greedy stereo algorithms from global, energy-minimizing ones.

How it works

The stereo pipeline has four stages: rectification, matching-cost computation, cost aggregation/optimization, and triangulation.

1. Rectification. Using the calibrated intrinsics and the relative pose (rotation RR, translation TT) between the two cameras, each image is warped by a homography so that: (a) both image planes lie in the same plane, (b) epipolar lines become horizontal, and (c) corresponding points lie on identical rows. After rectification the two virtual cameras have parallel optical axes and a purely horizontal baseline — the canonical stereo configuration.

2. Matching cost. For every pixel (x,y)(x,y) in the left image and every candidate disparity dd in a search range [0,Dmax⁡][0, D_{\max}], compute a cost C(x,y,d)C(x,y,d) measuring how well the left patch around (x,y)(x,y) matches the right patch around (x−d,y)(x-d,y). Common costs:

  • SAD (sum of absolute differences): ∑(i,j)∈W∣IL(x+i,y+j)−IR(x−d+i,y+j)∣\sum_{(i,j)\in W} |I_L(x+i,y+j) - I_R(x-d+i,y+j)|
  • SSD (sum of squared differences): same but squared — penalizes large errors more, more sensitive to outliers.
  • Normalized cross-correlation (NCC): invariant to affine brightness changes between the two cameras (different gains/exposures).
  • Census transform / Hamming distance: encodes each pixel as a bit-string of local intensity comparisons (brighter/darker than center), then compares bit-strings — highly robust to radiometric differences and a workhorse of production stereo systems.

3. Aggregation / optimization. Raw per-pixel costs are noisy. Algorithms aggregate cost over a window or globally optimize over the whole scanline/image to produce a stable disparity map. Three tiers, in increasing sophistication:

  • Local (winner-take-all): for each pixel independently, pick d∗=arg⁡min⁡dC(x,y,d)d^* = \arg\min_d C(x,y,d), possibly after box-filtering the cost volume spatially. Fast, parallelizable, but fails in textureless regions.
  • Scanline dynamic programming: for each row independently, find the disparity sequence minimizing total cost plus a smoothness penalty for disparity changes, solved by DP in O(W⋅D)O(W \cdot D) per row (see below). Enforces 1D smoothness and monotonicity along the scanline but produces horizontal "streaking" artifacts because rows are optimized independently.
  • Global 2D optimization: minimize an energy over the whole image,
E(D)=∑pC(p,D(p))+∑(p,q)∈Nλ⋅ρ(D(p)−D(q)),E(D) = \sum_{p} C(p, D(p)) + \sum_{(p,q)\in\mathcal{N}} \lambda \cdot \rho(D(p) - D(q)),

where N\mathcal{N} is a 4- or 8-neighborhood and ρ\rho penalizes disparity discontinuities between neighbors. Solved (approximately) via graph cuts or belief propagation; semi-global matching (SGM) approximates the 2D energy by aggregating DP-like 1D costs along many directions (typically 8 or 16) through each pixel and summing them, giving near-global quality at near-linear cost.

4. Triangulation. Once a disparity map D(x,y)D(x,y) is produced (with sub-pixel refinement, typically by fitting a parabola to the cost curve around the integer-minimum disparity), each pixel's depth is recovered via Z=fB/D(x,y)Z = fB/D(x,y), and the full 3D point is reconstructed via back-projection using the calibrated intrinsics. Below is the semi-global matching-style pipeline in pseudocode, the dominant practical algorithm balancing quality and speed. ```

# Input: rectified images I_L, I_R (H x W), max disparity D_max

# Output: disparity map D (H x W) function COMPUTE_COST_VOLUME(I_L, I_R, D_max): for y in 0..H-1: for x in 0..W-1: for d in 0..D_max: if x - d >= 0: C[y][x][d] = CENSUS_HAMMING(I_L, I_R, x, y, d) else: C[y][x][d] = INF # no valid match, out of frame return C function AGGREGATE_SGM(C, directions):

# Sum 1D dynamic-programming cost paths from several directions

# through every pixel to approximate global 2D smoothness. S = zeros_like(C) for r in directions: # e.g. 8 directions: horiz, vert, diagonals L_r = zeros_like(C) for each pixel p in scan order along direction r: for d in 0..D_max: prev = p - r # previous pixel along this path if prev is outside image: L_r[p][d] = C[p][d] continue best_prev = min_d'( L_r[prev][d'] ) L_r[p][d] = C[p][d] + min( L_r[prev][d], # same disparity: no penalty L_r[prev][d-1] + P1, # small shift: small penalty L_r[prev][d+1] + P1, min_over_all_d'(L_r[prev][d']) + P2 # large jump: big penalty ) - best_prev # normalize to avoid overflow S += L_r return S function WINNER_TAKE_ALL(S): D = zeros(H, W) for y in 0..H-1: for x in 0..W-1: D[y][x] = argmin_d( S[y][x][d] ) return D function SUBPIXEL_REFINE(D, S):

# Fit a parabola through the three cost samples around the minimum for each pixel (x, y): d = D[y][x] if 0 < d < D_max: c0, c1, c2 = S[y][x][d-1], S[y][x][d], S[y][x][d+1] D[y][x] = d + 0.5 (c0 - c2) / (c0 - 2c1 + c2) return D function TRIANGULATE(D, f, B): Z = zeros(H, W) for each pixel (x, y): if D[y][x] > 0: Z[y][x] = f * B / D[y][x] else: Z[y][x] = INF # zero disparity: point at infinity / invalid return Z

# Main C = COMPUTE_COST_VOLUME(I_L, I_R, D_max) S = AGGREGATE_SGM(C, directions = [E, W, N, S, NE, NW, SE, SW]) D = WINNER_TAKE_ALL(S) D = SUBPIXEL_REFINE(D, S) Z = TRIANGULATE(D, f, B) ``` Left-right consistency checking is typically added: compute disparity independently using the right image as reference, and invalidate any pixel where the two disparity estimates disagree by more than one pixel — this flags occluded regions cheaply.

Figure
Rectified stereo geometry: baseline, disparity, and triangulation
P (X,Y,Z) C_L C_R baseline B left image plane right image plane x_L x_R disparity d = x_L - x_R Z = f·B / d

Complexity & correctness

Time complexity. Let the image be W×HW \times H pixels, the disparity range have DD candidate values, and the matching window (if used) be k×kk \times k.

  • Naive cost-volume construction with windowed SAD/SSD: O(WHDk2)O(W H D k^2). Using running-sum ("integral image" or box-filter) tricks for aggregation reduces this to O(WHD)O(WHD) — each of the DD disparity planes is box-filtered in O(WH)O(WH) regardless of kk, since box filters admit constant-time updates via prefix sums.
  • Census transform cost is O(1)O(1) per pixel per disparity to compute (fixed-size bit string, Hamming distance via popcount), giving O(WHD)O(WHD) total for the cost volume, independent of kk.
  • Winner-take-all local matching: O(WHD)O(WHD) total — this is the whole pipeline's asymptotic floor, since every pixel must at minimum be compared against every candidate disparity once.
  • Scanline dynamic programming: for each of HH rows independently, the DP visits WW columns and DD disparities, and each state transition considers O(D)O(D) or O(1)O(1) predecessor states depending on the smoothness term's structure (a Potts-like penalty allows O(1)O(1) transitions via running minima). Total: O(HWD)O(HWD).
  • SGM: aggregates along rr directions (constant, typically 8), each an O(WD)O(WD) or O(HD)O(HD) scanline pass with O(1)O(1) per-step work using the running-minimum trick. Total: O(r⋅WHD)=O(WHD)O(r \cdot WHD) = O(WHD) since rr is a constant.
  • Full 2D global optimization via graph cuts (e.g., alpha-expansion for multi-label disparity): each expansion move solves a min-cut on a graph with O(WH)O(WH) nodes, and O(D)O(D) expansion rounds are typically run, giving O(D⋅maxflow(WH))O(D \cdot \text{maxflow}(WH)) — polynomial but markedly more expensive than SGM, and without a guaranteed global optimum for more than two labels (only a strong approximation guarantee: alpha-expansion is within a constant factor of the optimum for metric smoothness costs).

So the dominant practical algorithms (WTA, SGM) run in O(WHD)O(WHD) time and O(WHD)O(WHD) space for the cost volume, which is why disparity range DD is always bounded in practice (typically 64–256 for real-time systems) — it appears as a linear multiplier on both cost dimensions.

Space complexity. The cost/aggregated-cost volume dominates: O(WHD)O(WHD). Memory-conscious implementations stream one disparity plane at a time or use 8-bit costs to keep this volume compact (WHDWHD bytes for D=128D=128, W=1280W=1280, H=720H=720 is about 118 MB — non-trivial but standard for embedded stereo hardware).

Correctness argument. The triangulation step is exact given exact calibration and exact disparity: it follows directly from similar triangles in the rectified, axis-aligned two-camera configuration, which is precisely the configuration Epipolar Geometry and rectification guarantee. The harder question is why the matching step should recover the correct disparity at all. The underlying assumption is photo-consistency: the same 3D surface point produces similar intensity/appearance in both cameras, up to noise and modest radiometric differences. Under Lambertian reflectance and small camera baseline, this assumption is a good approximation, and the true correspondence minimizes the matching cost in expectation — so winner-take-all is a maximum-likelihood estimate under an i.i.d. Gaussian noise model for SSD, or an ℓ1\ell_1 noise model for SAD. The deeper correctness question is why global smoothness terms help rather than hurt. The scene is assumed to be piecewise smooth: disparity varies continuously across most of a physical surface and only jumps at object boundaries. The energy

E(D)=∑pC(p,D(p))+λ∑(p,q)ρ(D(p)−D(q))E(D) = \sum_p C(p, D(p)) + \lambda \sum_{(p,q)} \rho(D(p)-D(q))

encodes exactly this prior — a Markov Random Field with a data term and a pairwise smoothness term. Minimizing it is a maximum a posteriori (MAP) estimate under this prior. The correctness of SGM's approximation rests on the fact that summing several independent 1D scanline optimizations (each of which is exactly solvable by DP, since a 1D chain-structured MRF has no loops) approximates the true 2D MRF's marginals reasonably well, though it is not an exact minimizer of the full 2D energy — it is a fast, empirically strong heuristic with no general optimality guarantee, unlike DP on an actual chain, which the Bellman equation-style recursion solves exactly for that reduced 1D problem. The two fundamental sources of irreducible error are: (1) the aperture problem — in a textureless or perfectly periodic region, many disparities yield identical or near-identical cost, so the minimum is not unique, and no purely local algorithm can distinguish them without a smoothness prior pulling information in from textured neighbors; and (2) occlusion — pixels visible in only one image have no true correspondence at all, and any cost-minimization there is meaningless by construction, which is exactly what left-right consistency checking is designed to detect and mask out.

Worked example

Consider a rectified stereo pair with focal length f=700f = 700 pixels and baseline B=0.12B = 0.12 m (a typical short-baseline stereo rig). Suppose the matching stage estimates the following integer disparities for four scene points sampled along one scanline, followed by sub-pixel parabolic refinement:

PointRaw disparity dd (px)Cost at d−1,d,d+1d{-}1, d, d{+}1Refined d∗d^*Depth Z=fB/d∗Z = fB/d^* (m)
A (nearby table edge)11240, 10, 55112 + 0.5(40−55)/(40−20+55) = 111.790.75
B (chair back)4622, 8, 3046 + 0.5(22−30)/(22−16+30) = 45.891.83
C (far wall)95, 2, 69 + 0.5(5−6)/(5−4+6) = 8.939.41
D (textureless wall patch)3 (ambiguous — flat cost curve)6, 6, 6undefined (parabola denominator ≈ 0)flagged invalid

Reading the table: point A has the largest disparity (112 px) and is therefore the closest surface (0.75 m), consistent with the inverse relationship Z=fB/dZ = fB/d — small dd, large ZZ. Point D illustrates the aperture problem directly: the cost is nearly flat across neighboring disparities (6, 6, 6), so the parabola-fit denominator c0−2c1+c2≈0c_0 - 2c_1 + c_2 \approx 0, correctly signaling that this pixel's disparity cannot be trusted from local evidence alone. A well-built pipeline flags D as low-confidence (via a peak-sharpness or cost-curvature threshold) rather than reporting a spurious depth, and a global smoothness term (as in SGM) would instead pull D's disparity toward its textured neighbors (B or C), using the piecewise-smoothness prior described above. Note also the nonlinearity: doubling the disparity from point C (9 px) to a hypothetical 18 px would halve the depth, not linearly rescale it — a direct consequence of Z∝1/dZ \propto 1/d. This is why stereo depth resolution degrades sharply at long range: at d=9d=9 a one-pixel disparity error changes depth from 9.41 m to 700×0.12/8=10.5700 \times 0.12/8 = 10.5 m, over a meter of error, while at d=112d=112 the same one-pixel error changes depth from 0.75 m to 700×0.12/113=0.744700\times0.12/113=0.744 m, only 6 mm of error. Longer baselines or higher-resolution sensors are the standard remedies for this range-dependent precision loss.

Common pitfalls & traps

PitfallWhy it happensConsequence
Skipping/poor rectificationAssuming epipolar lines are already horizontal without verifying calibration qualityMatching search along wrong rows; systematic disparity error everywhere
Fixed disparity range too smallDmax⁡D_{\max} chosen without checking the true depth range of the sceneClose objects silently clipped to max disparity — wrong (too-far) depth reported instead of failing loudly
Ignoring the aperture problemTrusting winner-take-all in textureless regions (sky, blank walls)Noisy, salt-and-pepper disparity in flat regions; needs smoothness prior or invalidation
No occlusion handlingAssuming every left pixel has a valid right-image matchWrong disparities near depth discontinuities (foreground edges "bleed" into background)
Confusing disparity units with real-world unitsForgetting to multiply by fBfB before treating disparity as depthDepth values off by orders of magnitude; silent unit-consistency bugs
Overweighting the smoothness term λ\lambdaTuning for smooth-looking maps without validating against ground truthOver-smoothed depth: real object boundaries erased, thin structures vanish

Pioneers & history

Origins · 1 minTakeo Kanade and how this was discovered
TK
Takeo Kanade
1945–present · Carnegie Mellon University; pioneer of real-time and multi-baseline stereo vision

Kanade led foundational work on computational stereo throughout the 1980s–1990s, including the development of multi-baseline stereo (combining several camera pairs to reduce ambiguity) and some of the first real-time stereo vision hardware systems, bridging the gap between stereo as a geometric theory and stereo as a deployable engineering system.

History
1838
— Charles Wheatstone invents the stereoscope, demonstrating binocular disparity as the basis of depth perception in humans.
1960
s — Early photogrammetry and computer stereo matching begin using correlation-based matching on aerial and satellite imagery.
1980
s — Marr and Poggio formalize the correspondence problem computationally and propose cooperative algorithms for stereo matching.
1990
s — Dynamic-programming and multi-baseline stereo algorithms mature; Kanade's group builds real-time multi-camera stereo rigs.
2000
s — Global optimization methods (graph cuts, belief propagation) and Hirschmüller's semi-global matching (2005) push accuracy and speed simultaneously; standardized benchmarks (Middlebury, later KITTI) enable rigorous comparison.
2010
s–present — Deep-learning-based stereo networks learn matching costs and regularization end-to-end, while SGM-based systems remain dominant in real-time embedded and automotive stereo due to their efficiency.

Where this matters

Stereo depth estimation underlies binocular robot vision systems used for grasping and navigation, and it is the working principle behind automotive stereo camera systems used for obstacle detection alongside or instead of lidar. Consumer depth sensing in smartphone dual/triple-camera systems (portrait-mode background blur, AR occlusion effects) relies on compact stereo or stereo-assisted depth pipelines. Aerial and satellite photogrammetry uses wide-baseline stereo triangulation to build digital elevation models of terrain. Medical and industrial machine-vision systems use calibrated stereo rigs for precise, contact-free 3D measurement. The same rectify-match-triangulate pipeline, generalized to more than two views and unknown camera poses, is exactly the foundation that Structure from Motion builds on to reconstruct entire 3D scenes from unordered photo collections, and understanding two-view stereo cleanly is the standard on-ramp to that larger problem.

Ask the tutor: How does semi-global matching's per-direction dynamic programming actually avoid the streaking artifacts of independent per-row DP?

Why must the stereo images be rectified before matching?

Rectification warps both images so that corresponding epipolar lines become horizontal and row-aligned, which is guaranteed to be possible for any calibrated stereo pair by the epipolar geometry relating the two views. Without rectification, a point's correspondence could lie anywhere along a slanted epipolar line, requiring an expensive 2D search per pixel. After rectification the search shrinks to a 1D scan across disparities on the same row, which is what makes cost-volume algorithms like SGM computationally tractable at O(WHD)O(WHD) rather than O(WHD2)O(WHD^2) or worse. Rectification is purely a geometric preprocessing step computed once from calibration; it does not use image content, so its correctness depends entirely on how accurate the calibration is.

What happens if the true disparity exceeds the algorithm's assumed maximum disparity D_max?

The matching cost volume only stores candidate disparities in [0,Dmax⁡][0, D_{\max}], so any true disparity above that range simply cannot be represented. The winner-take-all step will still report some disparity within range — typically Dmax⁡D_{\max} itself, since cost tends to keep decreasing toward the boundary — producing a systematically wrong, too-small disparity and therefore a too-large depth estimate. This failure is silent: no error is thrown, the output just looks like a valid but incorrect depth map. Practical systems set Dmax⁡D_{\max} based on the closest object distance they need to handle, since dmax⁡=fB/Zmin⁡d_{\max} = fB/Z_{\min}, and validate that assumption against the deployment scenario (e.g., a car's minimum safe following distance).

How is sub-pixel disparity accuracy achieved if the cost volume only has integer disparity samples?

The matching cost is evaluated at integer disparities, but the true cost function (as a function of continuous disparity) is smooth and has a well-defined minimum that generally falls between two integer samples. A common technique fits a parabola through the three cost values at the integer minimum and its two neighbors, then analytically solves for the parabola's vertex, giving a closed-form sub-pixel correction term. This typically improves depth accuracy several-fold at negligible extra cost, since it reuses cost values already computed. It fails, however, when the local cost curve is flat (textureless regions) or asymmetric due to noise, which is why confidence measures based on curvature sharpness are used to flag unreliable sub-pixel estimates.

How does stereo vision differ from monocular depth estimation?

Classical stereo uses explicit geometric triangulation from two calibrated views, so its depth estimates come with a precise, interpretable error model tied to disparity precision, baseline, and focal length, as shown by the Z=fB/dZ=fB/d relationship. Monocular depth estimation, by contrast, must infer depth from a single image using learned statistical priors about scene layout, object size, and perspective cues, since a single image is geometrically insufficient to determine absolute scale without such priors. Stereo is generally more reliable and physically grounded when a calibrated two-camera rig is available, while monocular methods are used when only a single camera exists, at the cost of scale ambiguity and reliance on training-data assumptions.

Why do occlusions cause errors, and how are they detected?

A pixel visible only in the left image, because the corresponding surface point is hidden from the right camera's viewpoint (typically near depth discontinuities), has no true correspondence to find. Any matching cost computed for it is comparing unrelated content, so the resulting "disparity" is essentially noise, though it may still have a spuriously low cost. Left-right consistency checking detects this: compute disparity using the right image as the reference view as well, and compare the two independent estimates for the same physical point; if they disagree by more than roughly one pixel, the point is flagged as occluded or unreliable and excluded or in-painted rather than trusted.

Can stereo vision work with more than two cameras?

Yes — multi-baseline stereo combines three or more calibrated cameras (often in an L-shaped or line configuration) and aggregates matching costs across all camera pairs simultaneously. This reduces the ambiguity from any single pair, since a false match that happens to score well for one baseline is unlikely to score well for a different, independently placed baseline, effectively averaging out spurious matches from repetitive texture. This idea, pioneered in the 1990s, is a direct conceptual bridge to the fully general multi-view setting used in Structure from Motion, where the number and placement of cameras is arbitrary and even the camera poses themselves must be estimated jointly with scene structure.

What role does calibration accuracy play in final depth accuracy?

Since Z=fB/dZ = fB/d, any error in the calibrated focal length ff or baseline BB propagates directly and linearly into every depth estimate, regardless of how accurate the disparity estimation itself is — a 1% baseline error yields roughly a 1% depth error everywhere. This is why Camera Calibration is treated as a prerequisite rather than a detail: poor calibration silently degrades an otherwise perfect matching algorithm, and no amount of algorithmic sophistication in cost aggregation or optimization can correct for miscalibrated geometry, since triangulation is a purely geometric, non-learned final step.

Before this

Where this leads