Skip to content

Estimate depth from rectified stereo

CookbookGeometry

Compute disparity, reject invalid matches and convert positive disparities into depth using the supplied focal length and camera baseline.

Try the recipe ↓

The pipeline

VISUAL WALKTHROUGHGeometry
Follow the images, then read what passes to the next algorithm.
STARTING IMAGEFirst frame448 × 320
Synthetic rectified left view with three textured planes. Their disparities are 8, 16 and 24 px; larger disparity means nearer depth.

Synthetic rectified left view with three textured planes. Their disparities are 8, 16 and 24 px; larger disparity means nearer depth.

STEP 01 · 1 / 2Rectified grayscale pair896 × 320
Rectified grayscale pair: Left and right grayscale views must already have corresponding points on the same rows. This recipe assumes rectification; it does not calibrate cameras.

Left and right grayscale views must already have corresponding points on the same rows. This recipe assumes rectification; it does not calibrate cameras.

STEP 01 / 03

Match rectified rows

Explore the algorithm →
StereoSGBM.createStereoSGBM.compute

Stereo SGBM estimates horizontal disparity.

Receives
Left and right grayscale views with matching rectified rows.
Passes to the next step
A signed 16-bit disparity map encoded with four fractional bits.

Why this step? Semi-global block matching compares candidate horizontal offsets and regularizes neighbouring disparities. Rectification is a prerequisite: otherwise the corresponding point may lie on a different row that this search never considers.

Actual OpenCV 5.0.0 results on the illustrated sample, using the lab’s default algorithm settings. Masks, overlays and normalized fields are labelled previews; the data contracts above describe what the algorithms really exchange. Try this chain with your images ↓

Why this chain works

In a rectified stereo pair, a scene point appears at different horizontal positions in the two views. Nearby points shift more than distant points. Estimate that disparity first, then convert it to depth with calibrated camera geometry.

  1. 01

    Match rectified rows

    StereoSGBM.createStereoSGBM.compute

    Semi-global block matching compares candidate horizontal offsets and regularizes neighbouring disparities. Rectification is a prerequisite: otherwise the corresponding point may lie on a different row that this search never considers.

    Receives
    Left and right grayscale views with matching rectified rows.
    Passes on
    A signed 16-bit disparity map encoded with four fractional bits.
  2. 02

    Reject invalid disparities

    Mat.convertToPositive-disparity selection

    Multiplying by 1/16 recovers disparity in pixels. The lab excludes nonpositive values from depth conversion because they are invalid for this positive-baseline setup or would cause division by zero. This validity test alone does not establish a correct correspondence.

    Receives
    The fixed-point disparity map.
    Passes on
    Positive pixel disparities plus invalid samples.
  3. 03

    Convert disparity to depth

    Depth = focal length × baseline / disparity

    Similar-triangle geometry gives Z = fB/d for this calibrated, rectified arrangement. Converting units consistently is essential: resizing images changes the focal length in pixels. Small disparity errors at long distance can produce large depth errors because disparity is in the denominator.

    Receives
    Positive disparity, focal length in processed pixels and baseline in metres.
    Passes on
    A float32 depth field in metres. The lab uses zero as an invalid-value marker.

Tune and diagnose

Choose the parameters

Choose a disparity range large enough for the nearest expected objects and smaller than image width. Supply calibrated focal length adjusted for the processing scale and the real camera baseline. Arbitrary values yield arbitrary depth units or scale.

Read the result

The demo pair illustrates the calculation, not a calibrated camera measurement. Inspect disparity before depth, especially around occlusions, repeated texture and flat areas. Temporal camera panning without a known stereo setup is not sufficient for metric depth.

Try it with your images

Choose an input image and a second image. Without uploads, the lab uses a labelled synthetic pair. Run the recipe, then use the stage buttons to inspect intermediate results without rerunning it.

YOUR IMAGE · REAL OPENCV

Experiment at pixel level

Open full lab ↗

Compute disparity, reject invalid matches and convert positive disparities into depth using the supplied focal length and camera baseline.

The engine loads on your first run. Your images stay in this browser.

Input448 × 320
OutputWaiting for a result

Scroll over either image to zoom at the pointer. Use the scrollbars to pan both views over the same relative area. Zoom is relative to the input; pixel coordinates belong to each image. Warps can change scene correspondence.

Pixel inspector RGBA · native values · matched scale · 9 × 9 output pixels
Hover to inspect. Click to pin a pixel.
Input
Select a pixel

Output
Select a pixel

Sample models and licenses

Assumptions and limits

Input views must be calibrated and rectified. Focal length is in processed-image pixels; baseline determines the depth unit. The default pair is synthetic.

TypeScript core chain

Initialize the shared engine once with await initOpenCV(), then use these named imports. In a bundled browser app, pass the WASM URL as shown in the quickstart. image is an 8-bit BGR Mat from that same engine; paired recipes receive an equally sized nextImage. Region recipes use an in-bounds pixel rect. Read outputs before the using scope ends. See matrix ownership.

import {
  COLOR_BGR2GRAY,
  Mat,
  StereoSGBM_create,
  cvtColor
} from '@banou/opencv-wasm'

// The engine is already initialized; image is an 8-bit BGR Mat.
// nextImage is the equally sized second frame from the same engine.
// "using" releases native handles at scope exit; inspect or copy outputs before then.

// 1. Allocate grayscale stereo views and a signed fixed-point disparity map.
// Inputs must already be calibrated/rectified so matches lie on the same image row.
using left = new Mat(), right = new Mat(), disparity = new Mat()
// Convert the left colour view to grayscale for block matching.
cvtColor(image, left, COLOR_BGR2GRAY)
// Convert the right view in the same way; matching uses horizontal offsets.
cvtColor(nextImage, right, COLOR_BGR2GRAY)
// 2. Search 64 disparities starting at 0 with a 9x9 matching block.
// The last two arguments are smoothness penalties P1 and P2; 81 is the
// block area (9*9) for one grayscale channel, with P2 larger to discourage jumps.
using stereo = StereoSGBM_create(0, 64, 9, 8 * 81, 32 * 81)
// Check the nullable matcher factory before computing disparity.
if (!stereo) throw new Error('Stereo factory failed')
// Estimate disparity x_left - x_right in pixels and store it multiplied by 16.
stereo.compute(left, right, disparity)
// 3. Example calibration: focal length 500 processed-image pixels, baseline 0.1 metres.
// Replace these with real calibration, adjusting focal length when images are resized.
const focalPixels = 500, baselineMetres = 0.1
// Create an independent JavaScript float array, one depth value per pixel.
const depthMetres = Float32Array.from(disparity.data16S, fixed => {
  // Divide the signed 16-bit fixed-point value by 16 to recover pixel disparity.
  const pixels = fixed / 16
  // Rectified stereo geometry gives Z = focalLength * baseline / disparity.
  // Reject nonpositive disparity and use zero as an invalid marker, not zero distance.
  return pixels > 0 ? focalPixels * baselineMetres / pixels : 0
})
// Inspect this numeric depth field; arbitrary calibration produces arbitrary depth scale.
console.log('Depth values; 0 means invalid:', depthMetres)

The snippets isolate the core operations. The complete runnable recipes also include validation, filtering, overlays, intermediate previews and resource cleanup.