Nodes/ComfyUI CV/CV Stereo Disparity (WLS filtered)
ComfyUI Node

CV Stereo Disparity (WLS filtered)

WLS Is What Turns a SGBM Depth Map Into Something Usable

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
CV Stereo Disparity (WLS filtered)
  • left
  • right
  • disparity
  • confidence
  • valid
  • disparity_raw
◄num_disparities64►
◄block_size7►
◄min_disparity0►
◄modeSGBM (balanced)►
◄wls_lambda8000►
◄wls_sigma_color1.5►
◄uniqueness_ratio10►
◄speckle_window_size100►
◄speckle_range2►
◄lrc_threshold24►
◄depth_discontinuity_radius0►
◄disp12_max_diff-1►

Plain SGBM gives you a disparity map with big holes wherever the scene is smooth. The usual fixes are all bad - inpaint them and you invent geometry that was never there, blur them and you smear depth edges. The weighted-least-squares filter is the third option: it smooths the disparity while sticking to the left image's edges, so shading gradients that happen to align with real object boundaries stay sharp and flat regions get filled with a smooth interpolation instead of a guess.

In practice this is the node you reach for when SGBM's output is a checkerboard of holes. On the pack's own example driving pair the author reports the valid fraction going from roughly 65% to roughly 95% - same matcher underneath, much more usable map on top.

How it works

WLS only makes sense as a trio, and that's why this is one node rather than three wired together. SGBM is created, a right matcher is created from the same instance, both directions are computed (left→right and right→left), and createDisparityWLSFilter consumes both maps plus the left image. The filter runs a left-right consistency check, then smooths the surviving values with an edge-aware weighting. Three nodes would have to pass live cv2 objects through the graph, which is exactly what you can't do in ComfyUI.

The payoff beyond the filled holes: the filter hands you a confidence map. That's what lets a downstream step keep only the disparity values worth trusting rather than thresholding blindly.

Two knobs live in the required block:

wls_lambda (default 8000, OpenCV's own recommendation) is how hard the filter smooths towards image edges. Higher fills more and bleeds more across depth discontinuities.

wls_sigma_color (default 1.5) is how sensitive it is to the left image's edges - the useful band is roughly 0.8–2.0. Too low and disparity leaks across object boundaries; too high and the filter starts chasing noise as if it were structure.

In the advanced group, lrc_threshold is the consistency tolerance the filter uses, depth_discontinuity_radius (0 = off) widens the discontinuity handling, and disp12_max_diff passes through to SGBM itself. The rest of the matcher settings mirror CV Stereo Disparity (SGBM) exactly: num_disparities, block_size, min_disparity, mode, uniqueness_ratio, speckle_window_size, speckle_range.

Outputs are disparity (WLS-filtered, float32 pixels), confidence (HxW float32, 0–255, low where the two matching directions disagreed - occlusions and textureless areas), valid (uint8 0/255 mask) and disparity_raw, the unfiltered left-to-right SGBM map. Keep the raw one; flipping between the two previews is how you tell whether the filter is helping or quietly hallucinating a smooth wall where there should be a hole.

Preview with Preview CV Array (normalize or heatmap). Both frames must be rectified first - cv2_stereoRectify + initUndistortRectifyMap + cv2_remap.

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"

Restart. Python ≥ 3.12 and a recent V3-API ComfyUI.

This node is the one where the contrib requirement really bites, because the WLS filter lives in cv2.ximgproc, which is contrib-only. Install a plain opencv-python on top of the contrib wheel and the two share site-packages/cv2; the contrib submodules go empty and this node fails where the plain SGBM sibling still works. If that sounds like your situation, tools/repair_opencv_contrib.py --check will confirm it and --apply fixes it.

workflows/58_stereo_bm_vs_sgbm.json runs BM, SGBM and this node side by side on the same rectified pair, and 59_disparity_refinement.json shows the cleaning chain. Run 01_install_example_inputs.json once and reload to get the sample frames.

Where it bites

Turn wls_lambda up until a smooth wall appears and you have quietly undone the point of the filter - check disparity_raw before believing a suspiciously complete map.

Then there's the honest framing: the author's README says this pack is a personal, heavily LLM-generated project with no support promised and stereo tuning overfitted to a specific dataset (StereoGeo-CARLA). The nodes are real OpenCV calls and they behave like them; the defaults are one person's footage, not yours. Re-tune wls_sigma_color on your own pair and watch the confidence map while you do it.

Categoryimage/CV/contrib

Inputs (14)

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 the 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).
wls_lambdaFLOAT80000–100000Regularization strength: how strongly the filtered disparity is smoothed towards the edges of the left image. 8000 is the OpenCV-recommended default; larger = smoother, more hole filling, more bleeding across depth edges.
wls_sigma_colorFLOAT1.50–10How sensitive the filter is to left-image edges. 0.8-2.0 is the useful range: too low leaks disparity across object boundaries, too high makes it follow image noise.
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.
lrc_thresholdoptFLOAT240–256Left-right consistency tolerance in 1/16 pixel units: disagreements above it are marked unconfident and re-filled by the filter.
depth_discontinuity_radiusoptINT00–32Radius (px) around depth discontinuities used when computing confidence. 0 = the filter's own heuristic (block_size / 2).
disp12_max_diffoptINT-1-1–64SGBM's OWN left-right check, in pixels: matches that disagree by more than this are invalidated before the WLS filter ever sees them. -1 (cv2's default) turns it off and leaves occlusion junk in the map; 1 is the usual setting when the disparity feeds an interpolation or a point cloud.

Outputs (4)

NameTypeDescription
disparityNPARRAYHxW float32 WLS-filtered disparity in pixels.
confidenceNPARRAYHxW float32 confidence map, 0-255; low where the two matching directions disagreed (occlusions, texture-less areas). Sample it at your points to reject unreliable 3D.
validNPARRAYuint8 0/255 mask of pixels with a valid filtered disparity.
disparity_rawNPARRAYThe unfiltered left-to-right SGBM disparity (float32 pixels) - keep it to show what the filter changed.