Stereo rectification

Stereo rectification warps the two images of a calibrated stereo pair so that they appear to come from two ideal, parallel cameras: image planes coplanar, optical axes parallel, and rows aligned. After rectification, the epipolar line of any pixel in the left image is simply the same row in the right image.

Why this works: for any pair of camera poses, epipolar geometry constrains the match of a left-image point to a 1D curve (a line for pinhole cameras) in the right image. Rectification is a pair of homographies HL,HRH_L, H_R applied to the images that map both epipoles to infinity along the horizontal axis, making all epipolar lines horizontal and vertically aligned.

How the rectifying transforms are computed

Given calibrated intrinsics KL,KRK_L, K_R, lens distortion, and the extrinsic transform (R,t)(R, t) between the cameras, the classic (Bouguet) construction used by OpenCV’s stereoRectify proceeds as:

  1. Split the relative rotation. Rotate each camera by “half” of RR so the two optical axes become parallel while disturbing each image as little as possible.
  2. Align with the baseline. Apply a common rotation so the new xx-axes point along the baseline vector tt. Both epipoles now sit at infinity, so epipolar lines are horizontal.
  3. Choose common virtual intrinsics. A single new camera matrix KnewK_{\text{new}} (same ff, same principal point row) is chosen for both views, giving the rectified pair one focal length and one baseline for Z=fB/dZ = fB/d.
  4. Bake remap tables. The per-camera warp H=KnewRrectKold1H = K_{\text{new}} R_{\text{rect}} K_{\text{old}}^{-1}, combined with the lens-undistortion model, is precomputed into lookup tables, so rectifying each frame costs a single image remap.
R1, R2, P1, P2, Q, roi1, roi2 = cv2.stereoRectify(K1, D1, K2, D2, size, R, T)
mapLx, mapLy = cv2.initUndistortRectifyMap(K1, D1, R1, P1, size, cv2.CV_32FC1)
mapRx, mapRy = cv2.initUndistortRectifyMap(K2, D2, R2, P2, size, cv2.CV_32FC1)
rectL = cv2.remap(imgL, mapLx, mapLy, cv2.INTER_LINEAR)
rectR = cv2.remap(imgR, mapRx, mapRy, cv2.INTER_LINEAR)

The returned QQ matrix reprojects a rectified pixel-plus-disparity (u,v,d)(u, v, d) directly to 3D — the disparity-to-depth conversion in matrix form.

What it buys you

Checking rectification quality

A quick sanity check: match features between the rectified images and inspect the vertical disparity vLvRv_L - v_R. On a well-rectified pair it should be a small fraction of a pixel; a systematic offset or a gradient across the image means the extrinsic calibration has drifted. Rising vertical disparity over a robot’s lifetime is the standard symptom of a rig that has been bumped or is flexing thermally.

Practical caveats

Why it matters for SLAM

Nearly every stereo SLAM front-end — ORB-SLAM2’s per-keypoint stereo matching, S-PTAM, dense SGM-based mapping in Kimera — assumes rectified input, because scanline search is the only way to get per-frame depth at real-time rates. Understanding rectification also tells you where stereo pipelines break: bad calibration, unsynchronized shutters, or fisheye optics all violate the rectified model before any SLAM code runs.