Geometry & 3D Vision

Disparity

The horizontal shift, in pixels, between the images of the same scene point in a rectified stereo pair; it is inversely proportional to depth.

beginner

Disparity is the difference in image position of the same scene point in two views. In a rectified stereo pair it is one number per pixel, the horizontal shift between the left and right images, and it is inversely proportional to depth. Stereo matching outputs disparity, not depth, so how disparity is defined, stored, and converted links matching to 3D measurement.

Definition

For a rectified pair with the left image as reference, a scene point that appears at column xLx_L in the left image and at xRx_R on the same row of the right image has disparity d=xL−xRd = x_L - x_R, measured in pixels. A disparity map assigns such a value to every pixel of the reference image, with some pixels marked as having no value.

Intuition

Seen from two positions side by side, near objects shift a lot and far objects barely move. Disparity is the size of that shift: depth information in image units, so the same 10-pixel shift means a different distance on every rig.

Formal Definition

For two identical rectified cameras with focal length ff in pixels and baseline BB, the stereo vision article derives

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

Disparity is therefore inverse depth scaled by the rig constant fBfB, which Scharstein and Szeliski (2002) call the classical inverse-depth interpretation.

Sign and direction. With the left image as reference and the right camera displaced to the right, a point in front of the cameras appears further left in the right image, so xL>xRx_L > x_R and d>0d > 0; d→0d \to 0 as Z→∞Z \to \infty. The left pixel (x,y)(x, y) matches the right pixel (x−d,y)(x - d, y). A right-reference map is usually stored positive too, matching right pixel (x,y)(x, y) to left pixel (x+d,y)(x + d, y). Scharstein and Szeliski write the correspondence with a sign s=±1s = \pm 1 chosen so that disparities are always positive; since this is a convention, check it before combining maps from different sources.

Principal-point offset. If rectification leaves the principal points at different columns, a point at infinity no longer has zero disparity. The Middlebury 2014 data supplies the offset as doffs =cx1−cx0= c_{x1} - c_{x0} (right minus left principal-point column) and converts with Z=fB/(d+doffs)Z = fB/(d + \text{doffs}), so raw disparities of distant points can be zero or negative.

Properties

  • Search range. Matching tests candidates between dmind_\text{min} and dmax=fB/Zmind_\text{max} = fB/Z_\text{min}, set by the nearest depth of interest, so the range, and the size of the cost volume, grows with baseline, focal length, and resolution.
  • Uniform in disparity, non-uniform in depth. A fixed disparity error Δd\Delta d gives a depth error ΔZ≈Z2Δd/(fB)\Delta Z \approx Z^2 \Delta d/(fB), growing with the square of depth, as derived in stereo vision.
  • Sub-pixel values. Integer disparities make the scene look like a stack of thin layers, so many algorithms refine them to fractional values, for example by fitting a curve to the matching costs around the best integer disparity (Scharstein and Szeliski, 2002). Learned matchers regress fractional disparities directly, for example with the soft argmin (see cost volume).
  • Invalid pixels. Half-occluded pixels, seen in only one image, pixels whose match leaves the other image, and pixels a matcher rejects (for instance by a left-right check) have no valid disparity. Ground truth has gaps too: KITTI’s comes from accumulated, uninterpolated laser scans and covers about 50% of pixels on average (Geiger, Lenz, and Urtasun, 2012).

Inverse depth as a parametrization

Inverse depth ρ=1/Z\rho = 1/Z is often used as the unknown even without a stereo rig, because it stays finite for distant points (ρ=0\rho = 0 at infinity). Okutomi and Kanade (1993) expressed the matching costs of several stereo pairs as functions of inverse distance, shared across baselines, so the costs could be summed. In monocular SLAM, Civera, Davison, and Montiel (2008) parametrized each new point by its inverse depth relative to the camera that first saw it, which keeps the measurement equations close to linear and lets distant, low-parallax points, whose depth uncertainty is far from Gaussian, be used from their first observation. In monocular depth estimation, Ranftl et al. (2022) trained in disparity space, inverse depth up to unknown scale and shift, partly because some training datasets provide only such relative disparity.

Examples

Storing disparity maps

Each benchmark fixes its own encoding, and misreading one gives silently wrong depths:

  • KITTI. The 2012 and 2015 development kits store disparity as a 16-bit unsigned PNG. Dividing the stored value by 256 gives disparity in pixels, and 0 marks a pixel without a value. The evaluation code fills gaps in submitted maps before scoring, row by row, with the smaller (background) of the two neighboring valid disparities.
  • Middlebury. Version 3 of the benchmark stores floating-point disparities in PFM files, with infinity marking unknown values, and each 2014 scene’s calibration file supplies ff, BB (in millimeters), doffs, and ndisp, a conservative bound that lets algorithms search d=0,…,ndisp−1d = 0, \dots, \text{ndisp} - 1.
  • OpenCV. The StereoBM and StereoSGBM matchers return 16-bit fixed-point disparities with 4 fractional bits, so values must be divided by 16; invalid pixels are set to 16(dmin−1)16(d_\text{min} - 1), one step below the minimum disparity.

The code round-trips a small map through the KITTI encoding, converts it to depth for an illustrative rig, and shows the encoding’s limits.

import numpy as np
np.set_printoptions(suppress=True)   # fixed-point rather than scientific notation

f, B = 720.0, 0.54   # focal length (px) and baseline (m) of an illustrative rectified rig

# A 2 x 4 disparity map in pixels; inf marks pixels with no disparity (Middlebury-style).
disp = np.array([[64.25, 32.0, 8.5, np.inf],
                 [ 4.0,   1.0, 0.3, 0.0]])

# KITTI-style encoding: uint16 = round(256 * d); 0 means "invalid", so a valid 0 is stored as 1.
valid = np.isfinite(disp) & (disp >= 0)
png = np.zeros(disp.shape, np.uint16)
png[valid] = np.maximum(np.round(disp[valid] * 256), 1).astype(np.uint16)
print("stored uint16:\n", png)

# Decoding, as in the KITTI development kit: d = I / 256, valid where I > 0.
d = png.astype(np.float64) / 256.0
ok = png > 0
Z = np.full(d.shape, np.nan)
Z[ok] = f * B / d[ok]
print("depth (m):\n", np.round(Z, 2))

# The encoding quantizes disparity to 1/256 px and caps it just below 256 px.
print(f"largest storable disparity: {65535 / 256:.3f} px "
      f"-> nearest depth {f * B / (65535 / 256):.3f} m")
for d0 in [64.0, 8.0, 1.0]:
    Z0 = f * B / d0
    step = Z0**2 / (f * B) / 256          # depth change for one 1/256 px step
    print(f"d = {d0:5.1f} px: Z = {Z0:6.2f} m, one quantization step = {100 * step:.3f} cm")

Output:

stored uint16:
 [[16448  8192  2176     0]
 [ 1024   256    77     1]]
depth (m):
 [[    6.05    12.15    45.74      nan]
 [   97.2    388.8   1292.63 99532.8 ]]
largest storable disparity: 255.996 px -> nearest depth 1.519 m
d =  64.0 px: Z =   6.08 m, one quantization step = 0.037 cm
d =   8.0 px: Z =  48.60 m, one quantization step = 2.373 cm
d =   1.0 px: Z = 388.80 m, one quantization step = 151.875 cm

Zero cannot be stored as a valid disparity, since it means “no value”; the development kit’s writers store a valid zero as 1, that is 1/256 pixel, which decodes to a depth of about 100 km here. The 1/256-pixel step is negligible at 64 pixels, but at 1 pixel it moves the depth by 1.5 m, and the 0.3-pixel value decodes to 1293 m instead of 1296 m. The 16-bit range also caps disparity just below 256 pixels, so on this rig nothing closer than about 1.5 m can be stored.

Common Misconceptions

  • “Disparity is depth.” It is in pixels and inversely proportional to depth; converting it needs ff, BB, and any principal-point offset.
  • “Averaging disparities averages depths.” Since Z=fB/dZ = fB/d is nonlinear, filtering or interpolating in disparity and then converting differs from doing so in depth, most at small disparities.
  • “A stored zero is a measurement.” In KITTI files it means “no value”; read as a disparity it would put the point at infinity.

Where It Is Used

Related

  • Stereo Matching

    Finding, for every pixel of one rectified stereo image, its corresponding pixel on the same row of the other image, producing a disparity map from which depth follows.

  • Cost Volume

    A tensor of matching costs or similarities between each pixel of one image and its candidate correspondences in another, indexed by pixel position and candidate disparity, displacement, or depth.

  • Triangulation

    Recovering the 3D position of a point from its projections in two or more images taken by cameras whose calibration and poses are known.

  • KITTI

    A benchmark suite of real driving data from Karlsruhe, with stereo, optical flow, scene flow, odometry, object detection, and tracking benchmarks built from camera, LiDAR, and GPS/IMU recordings.

  • Middlebury Benchmarks

    Small, high-accuracy stereo and optical flow benchmarks from Middlebury College and Microsoft Research that defined how dense correspondence methods were evaluated in the 2000s.

  • Depth Map

    An image whose pixels store the distance to the scene, usually the depth Z along the optical axis, so that each pixel with a known camera can be lifted to a 3D point.

References

  1. Scharstein, D. & Szeliski, R. (2002). A Taxonomy and Evaluation of Dense Two-Frame Stereo Correspondence Algorithms. International Journal of Computer Vision, 47(1–3), 7–42.
  2. Geiger, A., Lenz, P. & Urtasun, R. (2012). Are We Ready for Autonomous Driving? The KITTI Vision Benchmark Suite. Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 3354–3361.
  3. Okutomi, M. & Kanade, T. (1993). A Multiple-Baseline Stereo. IEEE Transactions on Pattern Analysis and Machine Intelligence, 15(4), 353–363.
  4. Civera, J., Davison, A. J. & Montiel, J. M. M. (2008). Inverse Depth Parametrization for Monocular SLAM. IEEE Transactions on Robotics, 24(5), 932–945.
  5. Ranftl, R., Lasinger, K., Hafner, D., Schindler, K. & Koltun, V. (2022). Towards Robust Monocular Depth Estimation: Mixing Datasets for Zero-Shot Cross-Dataset Transfer. IEEE Transactions on Pattern Analysis and Machine Intelligence, 44(3), 1623–1637.