Nodes/ComfyUI CV/CV Stereo Disparity (SGBM)
ComfyUI Node

CV Stereo Disparity (SGBM)

SGBM Is the Stereo Depth Matcher You Actually Want by Default

By bmad4ever·Created 4 months ago·Updated 15 days ago· 1
CV Stereo Disparity (SGBM)
  • left
  • right
  • disparity
  • valid
◄num_disparities64►
◄block_size7►
◄min_disparity0►
◄modeSGBM (balanced)►
◄uniqueness_ratio10►
◄speckle_window_size100►
◄speckle_range2►
◄disp12_max_diff-1►

Learned depth models guess. A stereo pair measures. This node takes two rectified views of the same scene and computes, for every pixel, how far it shifted between them - the disparity - which is inversely proportional to depth. Large disparity means near, small means far, and with a real baseline and focal length it's arithmetic, not inference.

SGBM (semi-global block matching) is the workhorse of classic stereo, and it's the default you want: fast enough to be usable, and it fills in far more of the frame than the plain block matcher, because it adds a smoothness term across scanlines instead of comparing each window in isolation.

How it works

cv2.StereoSGBM is a class API, and the pack's auto-generated wrappers only reach top-level cv2.* functions - so this node exists to own the object: create it, run it, throw it away, all inside one execution. Both frames are grayscaled internally, and the right frame is resized to the left if the sizes disagree.

The P1/P2 smoothness penalties aren't inputs. They're derived from block_size using OpenCV's own heuristic (8 * bs² and 32 * bs²), which is why there's one block-size knob rather than three. The raw result is int16 scaled by 16; the node converts it to float32 disparity in pixels and marks anything below min_disparity as invalid.

Both frames must be rectified. That means epipolar lines are horizontal and a point appears on the same image row in both views. Get there with cv2_stereoRectify + initUndistortRectifyMap + cv2_remap after a calibration (CV Stereo Calibrate (Chessboard), or CV Stereo Rectify (Uncalibrated) if you have no intrinsics). Feed an unrectified pair and you won't get an error - you'll get a smeared, half-empty map and assume the node is broken.

What you actually set

num_disparities (default 64) is the search range: how far left each pixel looks for its match. Bigger covers nearer objects and wider baselines but costs time, and it's rounded up to a multiple of 16. If your nearest subject is clipping into the invalid zone, raise this first.

block_size (default 7) is the matched window. 3–11 is the useful band, forced odd. Smaller keeps detail and gets noisier; larger smooths your depth edges into mush.

mode picks the SGBM variant - the balanced default, a faster lower-quality 3-way, or full-scale HH which is the best and the slowest.

In the advanced group, disp12_max_diff is the one worth knowing: it's the left-right consistency check, and at -1 (OpenCV's default) it's off, which leaves occlusion artefacts in the map. Set it to 1 whenever the disparity is heading for interpolation or 3-D points. uniqueness_ratio, speckle_window_size and speckle_range filter out bad matches and isolated blobs.

Outputs are disparity (HxW float32, in pixels) and valid (a uint8 0/255 mask of pixels that produced a real value). Preview the disparity with Preview CV Array in normalize or heatmap mode - remember it's a BGR ndarray, so CV Array → Image is your route to anything that expects a ComfyUI IMAGE. valid is exactly what you want for masking downstream points; on a rectified driving pair expect a big chunk of holes in flat sky and road.

CV Stereo Disparity (WLS filtered) is the same SGBM underneath with an ximgproc filter that fills those holes while respecting edges - the better answer for a finished depth map.

Install

ComfyUI Manager → search ComfyUI CV (publisher bmad4ever), or:

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"

Then restart. Python ≥ 3.12 and a modern V3-API build. The contrib wheel matters: dropping a plain opencv-python over it shares site-packages/cv2 and silently removes the contrib submodules. tools/repair_opencv_contrib.py --check and --apply exist for the fallout.

The repo's rectified sample pairs (stereo_bethlehem_L/R, stereo_gallery_L/R) come in via workflows/01_install_example_inputs.json - run it once, reload the page. 58_stereo_bm_vs_sgbm.json is a ready-made comparison, and 59_disparity_refinement.json shows the cleaning path.

Where it bites

Rectification first, sandpaper later. Also: disparity is only as metrically meaningful as the calibration behind it - leave square_size at 1.0 during calibration and your disparity is in pixels with an arbitrary scale, which is fine for masking and effects and useless for "how many metres away is that".

One pack-level caveat, from the author himself rather than from me: the README describes the codebase as heavily LLM-generated, curated against one OpenCV build, with no support planned and the stereo tuning overfitted to a specific dataset. The SGBM call here is textbook; treat any default in the surrounding workflows as a suggestion to re-tune on your own footage.

Categoryimage/CV/features

Inputs (10)

NameTypeDefaultDescription
leftNPARRAY,IMAGELeft rectified frame (grayscaled internally). Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size.
rightNPARRAY,IMAGERight rectified frame; resized to left if sizes differ. Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size.
num_disparitiesINT6416–512Disparity search range (0..num_disparities), rounded up to a multiple of 16. Larger covers nearer objects / wider baselines but is slower.
block_sizeINT71–21Matched block size in pixels (forced odd; 3-11 is typical). Smaller = more detail but noisier.
min_disparityINT0-256–256Smallest disparity to search from (usually 0).
modeCOMBOSGBM (balanced)SGBM is the standard; 3-way is faster at lower quality; HH runs the full-scale two-pass (best, slowest).
uniqueness_ratiooptINT100–100Margin (%) by which the best match must beat the runner-up to be accepted.
speckle_window_sizeoptINT1000–1000Largest smooth disparity blob treated as speckle noise and invalidated (0 = off).
speckle_rangeoptINT20–64Max disparity variation within a speckle component.
disp12_max_diffoptINT-1-1–64Left-right consistency check, in pixels: a match whose reverse match disagrees by more than this is invalidated. -1 (cv2's default) turns it off and leaves occlusion junk in the map; 1 is the usual setting when the disparity is about to be interpolated or turned into 3D points.

Outputs (2)

NameTypeDescription
disparityNPARRAYHxW float32 disparity in pixels; invalid pixels are below min_disparity (see 'valid').
validNPARRAYuint8 0/255 mask of pixels with a valid disparity.