Nodes/ComfyUI CV/cv2.getValidDisparityROI
ComfyUI Node

cv2.getValidDisparityROI

Crop your disparity map before you trust it

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
cv2.getValidDisparityROI
    • roi
    ◄roi1_x0►
    ◄roi1_y0►
    ◄roi1_w0►
    ◄roi1_h0►
    ◄roi2_x0►
    ◄roi2_y0►
    ◄roi2_w0►
    ◄roi2_h0►
    ◄minDisparity0►
    ◄numberOfDisparities0►
    ◄blockSize0►

    A stereo disparity map looks like it covers the whole frame. It doesn't. After rectification each camera has a region that maps cleanly, the block matcher needs a full neighbourhood around every pixel it reports, and a disparity search range means the left and right images can only agree over a band in the middle. Outside all of that, whatever numbers you're reading aren't depth - they're edge-of-search garbage. This node computes the rectangle where the values are actually valid, so you can crop to it instead of quietly feeding noise into a 3D reconstruction.

    How it works

    You give it two rectification ROIs - roi1_* for the first camera, roi2_* for the second, each as four separate integers _x, _y, _w, _h - plus the three parameters of your disparity search: minDisparity, numberOfDisparities and blockSize. It intersects the two regions, then trims the result for the disparity range and the matched block size, because a block matcher can't say anything about a pixel whose neighbourhood isn't entirely inside the rectified area.

    The single output, roi, is a BOUNDING_BOX - cv2's Rect expressed as core ComfyUI bbox data, {x, y, width, height}, nested one group per frame the way every bbox emitter in this pack does it. From there its tooltip names the consumers directly: CV Crop by BBoxes to crop the disparity map, Draw BBoxes to see where the valid region lands (surprisingly educational the first time - it's smaller than you expect), or CV Split Tuple for the four numbers as loose values.

    It isn't in any shipped workflow of this pack. The stereo examples use the curated CV Stereo Disparity (BM) / (SGBM) / (WLS filtered) nodes, which do their own framing; this one is for when you're building the pipeline by hand.

    The plumbing bit that'll trip you

    The eight ROI numbers are flat INT widgets, not a rect socket, so if your rectification came from the raw cv2.stereoRectify wrapper you'll have two BOUNDING_BOX outputs (that wrapper's last two returns are rects) and no matching input shape. CV Split Tuple is the bridge: feed it a BOUNDING_BOX and it gives you a/b/c/d plus a count, which is how you get x/y/w/h into these widgets. Do that twice and the eleven inputs fill themselves. Alternatively, type the numbers in if you flagged the calibration region by hand - for a fixed rig, that's a legitimate one-time setup.

    The parameters that matter most are numberOfDisparities (usually 16 × something, and it's the width of the band the matcher searches, so it directly shrinks the valid region) and blockSize (the SAD window; each doubling of the window eats more of the border). Both must match what you passed to the matcher. Mismatched numbers here are the classic mistake: the ROI comes out too generous, and the "valid" map still has a rim of junk in it.

    Install

    pip install "opencv-contrib-python-headless~=5.0.0.93"
    

    ComfyUI Manager → search ComfyUI CV, or:

    cd ComfyUI/custom_nodes
    git clone https://github.com/bmad4ever/comfyui_cv
    

    Restart after installing. Python ≥ 3.12 and a recent ComfyUI on the V3 node API are required; the pack is curated against OpenCV 5.0.0.93. The node itself is pure geometry - no models, no downloads, nothing from the pack's models/onnx folder.

    Practical notes

    • Crop, then measure. Statistics computed over the whole disparity map - mean, percentiles, point-cloud spread - are contaminated by the invalid border. Crop with roi first and the same numbers become meaningful. If you're checking them by hand, Inspect CV Data gives you min/max/mean for any array and even reports how many non-finite values it dropped.
    • The ROI is a crop box in image space, so it composes with the rest of the bbox lane: paste results back with CV Paste by BBox once you've transformed them, which is how you relate a cropped disparity to the full-frame colour image.
    • Contrib wheel trap: all four OpenCV PyPI distributions share one site-packages/cv2, so installing opencv-python over the contrib wheel strips the contrib submodules and some of this pack's nodes disappear from the menu. tools/repair_opencv_contrib.py --check → --apply.
    • Read the pack's disclaimers if you're shipping this. The README states the example workflows' stereo settings were tuned to a specific dataset (StereoGeo-CARLA) and are not production-grade, and that the pack was developed with heavy LLM assistance. Individual nodes like this one are honest wrappers around one cv2 call; the pipeline around them is where you should apply your own judgement.
    Categoryimage/CV/low-level/cv2 G

    Inputs (11)

    NameTypeDefaultDescription
    roi1_xINT0-2147483648–2147483647Rectangle top-left corner X in pixels.
    roi1_yINT0-2147483648–2147483647Rectangle top-left corner Y in pixels.
    roi1_wINT00–2147483647Rectangle width in pixels (>= 0).
    roi1_hINT00–2147483647Rectangle height in pixels (>= 0).
    roi2_xINT0-2147483648–2147483647Rectangle top-left corner X in pixels.
    roi2_yINT0-2147483648–2147483647Rectangle top-left corner Y in pixels.
    roi2_wINT00–2147483647Rectangle width in pixels (>= 0).
    roi2_hINT00–2147483647Rectangle height in pixels (>= 0).
    minDisparityINT0-2147483648–2147483647 - - -
    numberOfDisparitiesINT0-2147483648–2147483647 - - -
    blockSizeINT0-2147483648–2147483647 - - -

    Outputs (1)

    NameTypeDescription
    roiBOUNDING_BOX- - - A cv2 Rect as core BOUNDING_BOX data ({x, y, width, height}, nested one group per frame) - feed 'Crop By Bounding Boxes', 'Draw BBoxes', or 'CV Split Tuple' for x/y/w/h.