cv2.ximgproc.computeBadPixelPercent
Put a number on how wrong your depth map is
- GT
- src
- float
This is the stereo-benchmark metric, in a node. Give it a ground-truth disparity map and your estimate, and it returns the percentage of pixels whose error exceeds a threshold - the "bad pixels" number that every stereo paper since 2002 reports. If you're tuning a depth pipeline by eye, this is the node that stops you.
That last sentence is the actual pitch. Depth maps are lovely to look at and easy to fool yourself with: a smeared map can look smoother and feel better while being objectively worse. A single float out of this node settles it.
Inputs
GT- the ground-truth map.NPARRAY,IMAGEorMASK; frame 0 of a batch.src- the map you're evaluating. Same accepted types.ROI_x,ROI_y,ROI_w,ROI_h- the region you're scoring, as four separate ints. All four default to 0, which is an empty rectangle, so this is not a "leave it alone" default. You need to give it a real region, and here's the reason the pack gives ROI its own inputs at all: a stereo disparity map has a band along one edge where there are no matches by construction, and scoring that garbage against ground truth tells you nothing. The curatedCV Disparity Filter (WLS)node in the same pack has anauto (skip the invalid left band)ROI option for exactly this reason - the pack treats ROI as a first-class part of the stereo workflow.thresh(default 24) - the error tolerance. A pixel counts as bad when the absolute difference between GT and your map exceeds it. 24 is OpenCV's default and it's in disparity units, so it means "24 pixels of disparity", which is a lot - tighten it if you're comparing subtly different refinement settings.
Output: one FLOAT. To actually read it, wire it into Inspect CV Data, which reports value statistics for any input; a bare float has no preview of its own.
Using it as a regression test
The workflow that makes this node pay for itself: pick one stereo frame with ground truth, then run every hypothesis you have through the pair of nodes and compare the number.
Load GT + Load estimate
→ computeBadPixelPercent (with the same ROI for every run)
→ Inspect CV Data → note the number
Swap in a WLS-filtered map, an interpolated map, a different matcher, a neural depth estimator, and you have a leaderboard instead of a vibe. The pack's 59_disparity_refinement example is basically this exercise: one raw SGBM map cleaned up six different ways, previewed side by side.
Two rules for it to mean anything: the same ROI for every candidate, and the same units. Disparity in pixels is not disparity normalised to 0–1, and comparing a normalised map against a pixel-scale GT is a way to generate a meaningless constant.
Installing it
Contrib module, 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
Python ≥3.12 and a recent, V3-API ComfyUI. The example stereo workflows feed this kind of data from a rectified image pair; if you want the bundled sample media to show up in Load Image, run workflows/01_install_example_inputs.json once and reload the page - the Load* dropdowns are built when the page loads.
What goes wrong
- The ROI defaults bit you. Four zeros is an empty region. Set them.
- A surprisingly good score. You're scoring an invalid band, or GT and the estimate aren't aligned. Check by eyeballing both maps in
Preview CV Array(heatmap mode shows disparity sensibly) before you trust the number. - Comparing across different ROI sizes. The percentage is per-pixel within the ROI; a smaller ROI with all the hard pixels excluded will flatter anything.
- Missing node.
ximgprocis contrib-only, and all fouropencv-python*distributions share onesite-packages/cv2directory - install a non-contrib wheel and the submodule becomes an empty stub with no error.tools/repair_opencv_contrib.py --checkin the pack repo diagnoses it. - Nothing to compare against. A monocular depth estimator like Depth Anything gives you relative depth, not metric disparity. There is no ground truth to score against unless you captured one - this node is for stereo rigs, rendered scenes and synthetic datasets, not for "is my ControlNet depth map pretty".
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| GT | NPARRAY,IMAGE,MASK | ground truth disparity map 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. | |
| src | NPARRAY,IMAGE,MASK | disparity map to evaluate 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. | |
| ROI_x | INT | 0-2147483648–2147483647 | Rectangle top-left corner X in pixels. |
| ROI_y | INT | 0-2147483648–2147483647 | Rectangle top-left corner Y in pixels. |
| ROI_w | INT | 00–2147483647 | Rectangle width in pixels (>= 0). |
| ROI_h | INT | 00–2147483647 | Rectangle height in pixels (>= 0). |
| threshopt | INT | 24-2147483648–2147483647 | threshold used to determine "bad" pixels @result returns mean square error between GT and src Preset to the OpenCV default (24). |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| float | FLOAT | — |