Nodes/ComfyUI CV/cv2.validateDisparity
ComfyUI Node

cv2.validateDisparity

The left-right check SGBM runs inside itself, exposed for your own maps

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
cv2.validateDisparity
  • disparity
  • cost
  • nparray
◄minDisparity0►
◄numberOfDisparities0►
◄disp12MaxDisp1►

Stereo disparity maps are full of lies. A window-matching algorithm will happily report a confident number for a pixel it matched against the wrong piece of texture half a metre away, and there's no error bar attached. The standard defence is a left-right consistency check: match the left image into the right, then match the right image back into the left, and throw out every pixel where the two answers disagree by more than a pixel or two. StereoSGBM does this internally when you set disp12MaxDiff. cv2.validateDisparity is that same check as a standalone function - and it's the reason this node exists, so you can apply it to a disparity map that came from somewhere else.

What it actually does

You hand it a disparity map and a cost volume, plus the minDisparity and numberOfDisparities your matcher was configured with, and it walks the map invalidating pixels whose right-to-left best match disagrees by more than disp12MaxDisp (default 1, and the tooltip flags it as the OpenCV default). Pixels that fail get stamped invalid rather than removed, so the output is a map the same size as the input with holes where the check failed.

That second input is the awkward part. cost is the raw matching cost buffer, and in normal use it belongs to the SGBM object that produced the map. Which is exactly why you'd reach for this node so rarely in a first workflow: if you're running the pack's curated stereo nodes, CV Stereo Disparity (SGBM) already exposes disp12MaxDiff and does the check in the one place it's cheap. Use this wrapper when the map you want to validate didn't come from SGBM - a quasi-dense stereo pass, a hand-rolled matcher, an experiment.

Inputs and outputs

  • disparity - the map to validate. Accepts NPARRAY, IMAGE or MASK (frame 0 of a batch).
  • cost - the matching cost volume. Same accepted types.
  • minDisparity and numberOfDisparities - must be the numbers the matcher ran with. Set them wrong and you won't get an error, you'll get a map that's all-valid or all-invalid, which is worse.
  • disp12MaxDisp (optional, default 1) - the tolerance in pixels. Loosen it to 2 if you're seeing speckle at sharp depth edges. OpenCV's own default of 1 is a tight, honest threshold - bumping it trades hole-free maps for false matches, and you should be able to say which one you wanted.

One NPARRAY comes back: the validated map. Wire it to Preview CV Array in heatmap mode to look at it, CV Array → Image if a downstream node wants an image, or straight into a hole-filling step. The pack has a proper refinement chain for that - CV Disparity Filter (WLS) and CV Disparity Interpolate (Edge-Aware) - and looking at its 59_disparity_refinement example workflow is the fastest way to see where a validated-but-holey map sits in a real pipeline.

The wider point

In the depth world, the ComfyUI question is usually "how do I get a depth map", and the answer is a depth estimator - Depth Anything, Marigold and friends - fed to a ControlNet preprocessor or a parallax effect. Stereo matching is the other road: two real views, real geometry, real metric disparity, and a pile of consistency checks instead of a neural network's confidence. This node is one of those checks. If your output is going into a point cloud, a measurement, or a rig you'll re-shoot, the checks are not optional.

Installing it

Ships inside ComfyUI CV. Manager → search ComfyUI CV, or:

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

You need Python ≥3.12 and a recent V3-API ComfyUI. The example stereo workflows additionally rely on a few third-party packs (Inspire-Pack, Custom-Scripts, Basic Data Handling), but this single node doesn't.

What goes wrong

  • Size mismatch. disparity and cost have to line up; cv2 raises a bare (-215) assertion if they don't, and the pack wraps those as a RuntimeError naming the function and the input shapes, so read the message rather than the traceback.
  • Wrong disparity window. minDisparity/numberOfDisparities copied from the wrong matcher is the classic silent failure. If suddenly everything is invalid, this is why.
  • Assuming it modifies your map in place. cv2 writes into its in-out argument, but the pack copies arrays on the way in and out, so the node returns a new value and leaves the input node's cached output alone. Nothing to guard against, just don't expect to see the damage upstream.
  • The invalid-band gotcha. SGBM's valid region is narrower than the image (the left band has no matches by construction). If you evaluate or filter over the whole frame you're mixing real disparity with garbage. The curated CV Disparity Filter (WLS) node even has an auto (skip the invalid left band) ROI option for exactly this.
Categoryimage/CV/low-level/cv2 V

Inputs (5)

NameTypeDefaultDescription
disparityNPARRAY,IMAGE,MASK - - - 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.
costNPARRAY,IMAGE,MASK - - - 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.
minDisparityINT0-2147483648–2147483647 - - -
numberOfDisparitiesINT0-2147483648–2147483647 - - -
disp12MaxDispoptINT1-2147483648–2147483647 - - - Preset to the OpenCV default (1).

Outputs (1)

NameTypeDescription
nparrayNPARRAY—