Nodes/ComfyUI CV/cv2.countNonZero
ComfyUI Node

cv2.countNonZero

The mask-coverage meter you'll use more than you expect

By bmad4ever·Created 4 months ago·Updated 14 days ago· 1
cv2.countNonZero
  • src
  • int

cv2.countNonZero returns one integer: how many elements of an array are not zero. That's it. And yet it's one of the most quietly load-bearing nodes in a CV pipeline, because "how many pixels are set" is the question behind a dozen things you'd otherwise eyeball - is the mask mostly empty, are these two masks actually the same region, did the threshold do anything.

The ComfyUI CV node cv2.countNonZero is a raw wrapper (category image/CV/low-level/cv2 C), takes one input, and puts a plain INT out. It's the smallest possible node in bmad4ever's pack, which is also why it's a good example of the pack's philosophy: ~470 auto-generated cv2.* wrappers plus curated high-level nodes, everything deterministic, no model involved.

How it works

It walks the array and counts elements whose value isn't zero. No threshold, no interpretation - non-zero means "counted". On a binary 0/255 uint8 mask that's obviously the area of the mask. On a ComfyUI MASK, which arrives as float 0–1, the pack converts to uint8 for the cv2 call (the same conversion cv2.copyTo and friends use), so a soft, anti-aliased edge still counts: a pixel at 0.4 becomes 102, which is non-zero and gets counted. If you need the count of solidly covered pixels, threshold the mask first and count that.

The output is a single INT for the whole array. There's no per-channel breakdown and no position information - this is a magnitude, not a location.

The inputs and outputs that matter

src is the only input, and the OpenCV documentation the pack quotes says "single-channel array". A MASK or a gray NPARRAY is the intended food. A 3-channel IMAGE is not what the function documents, and since this wrapper isn't in the pack's auto-grayscale list, whatever you wire in is what cv2 receives - so feed it a mask, or convert with Mask → CV Array / cv2.cvtColor first.

The output is named int and is a plain ComfyUI INT, which is the good part: it wires into any INT socket in the graph. Convert a widget on a downstream node to an input (right-click → Convert widget to input, then wire) and you can drive a threshold, a step count, or a branch condition off a measured quantity.

What you'd actually do with it

Mask IoU, by hand. cv2.bitwise_and two masks → count → cv2.bitwise_or the same two → count → divide. That's the standard overlap measure for comparing a predicted mask against a ground truth, and it's three nodes and a calculator.

Coverage as a gate. Count the mask, divide by width×height (CV Array Shape gives you the dimensions) and you have a fraction. Under 1%? The detector found noise. Over 60%? It probably grabbed the background. That number is the difference between a graph that fails quietly and one that tells you the segmentation went wrong - the recurring failure in the mask layer is a plausible-looking mask of the wrong thing.

Cheap change detection. Count a difference image after cv2.absdiff; zero means nothing moved, which is a genuinely useful check when you're debugging a video pipeline and suspect two frames are identical.

Its cousins in the same pack are worth knowing: cv2.hasNonZero returns a boolean ("is there anything at all"), cv2.findNonZero returns the positions of the set pixels as an array - which, passed to cv2.boundingRect, gives you the tight box of whatever is non-zero. Note that CV Masks to BBoxes already does the tidy version of that job if a bounding box is all you're after.

Installing it

ComfyUI Manager → search the pack title (ComfyUI CV) → install → restart. Or:

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

Python ≥ 3.12, V3-API ComfyUI, and the contrib wheel. One install caveat, since it's the one people actually trip over: this pack's pin pulls numpy 2.x, and the long-running conflict in this ecosystem is insightface 0.7.3 (numpy 1.x) versus any recent opencv wheel (numpy 2.x). If your FaceID/InstantID nodes broke after an install, that's why - and per the thread that keeps resurfacing, plenty of people have a silently broken insightface install and never realise.

Common issues and troubleshooting

It errors out or gives a nonsense count on a colour IMAGE. The function documents a single-channel array, and this node is not in the pack's auto-grayscale list, so nothing converts a 3-channel input for you. Feed it the mask, or a gray NPARRAY - not the picture.

The count is bigger than the mask's pixel area. You counted a soft mask: every anti-aliased edge pixel counts as set. Threshold first if "how many pixels are fully inside" is the question.

A count of zero on a mask you can see. The mask is all-black where you're looking at the wrong frame - remember an IMAGE/MASK batch is a batch, and this wrapper reports a single number, so check what frame 0 actually contains.

Categoryimage/CV/low-level/cv2 C

Inputs (1)

NameTypeDefaultDescription
srcNPARRAY,IMAGE,MASKsingle-channel array. 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.

Outputs (1)

NameTypeDescription
intINT—