cv2.connectedComponentsWithStats
Blob count, boxes, areas and centroids in one pass
- image
- retval
- labels
- stats
- centroids
This is the useful one of the four
If you have a mask with several blobs and you want to know how many, how big, where, and how far apart them, this is the node. cv2.connectedComponents gives you labels and a count; connectedComponentsWithStats gives you the same labels plus a tidy per-blob table: bounding box, pixel area, and centroid. That table is what you actually make decisions on - drop anything under 200 pixels, keep the largest three, crop each one, draw a dot at each centre.
It's the workhorse behind the whole "find the regions, then do something per region" loop that masking-detection-detailing.md describes, minus the model. No detector, no SEGS, just geometry on a mask you already have.
It's a raw wrapper from ComfyUI CV (bmad4ever/comfyui_cv), category image/CV/low-level/cv2 C.
How it works
Same labelling as the siblings: 8-bit single-channel input, non-zero means foreground, connected runs merged, background is label 0. Threshold first - a greyscale photo is almost entirely non-zero and collapses into one huge component. Connectivity decides whether diagonal contact counts as touching: 8-way (the default) says yes, 4-way says no.
Then the extra pass: for every label, OpenCV accumulates the tight bounding box, the pixel count, and the centroid. The centroid is the mean of the label's pixel coordinates, so for an L-shaped or ring-shaped blob it can land outside the shape - it's a centre of mass, not a centre of the object. That matters if you're drawing a marker with it.
Inputs and outputs that matter
- image - required. Your 8-bit single-channel mask. IMAGE/MASK/NPARRAY.
- connectivity - optional INT, default
8, advanced input. - ltype - optional COMBO, default
CV_32S; OpenCV supports onlyCV_32SandCV_16Ufor the label image.
Outputs - and here the naming is friendlier than the WithAlgorithm variants, which is a nice accident:
- retval - the label count, including background, so five blobs report
6. Drop the first row ofstatsand you're consistent again. - labels - the label map, NPARRAY. Not an echo of your input; it's data. View it with Preview CV Array in heatmap mode, or split it with CV Labels to Masks (full size).
- stats - an N×5 table, one row per label: left, top, width, height, area. Row 0 is the background.
- centroids - an N×2 table of
(cx, cy)per label, same row order.
What to do with stats and centroids in this pack: CV Array Statistic and CV Reduce Array By Label for per-label reductions, CV Masks to BBoxes / CV BBoxes to Masks if you'd rather work in box space, CV Crop by Masks to cut each region out, and CV Array To BBoxes / CV BBoxes To Array for moving between this pack's BOUNDING_BOX socket and plain arrays. For drawing, centroids feeds cv2.circle... or better, CV Draw Circles, which takes (x, y, r) points directly and is what you actually want for marking a dozen blobs.
Installing the pack
Manager → search ComfyUI CV, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
Restart. Requirements: Python ≥ 3.12, a ComfyUI with the V3 node API, and opencv-contrib-python-headless~=5.0.0.93.
Where people get burned
- The background row.
retval,stats[0]andcentroids[0]all describe background. Anything that averages per-blob areas without skipping row 0 will be quietly wrong, and the bigger the frame, the bigger the distortion. - Grey input instead of binary. Still the #1 mistake in this family: one giant blob, a huge
statsrow, and no error. - Filtering on the count instead of the area. "Is this mask clean?" is an area question. A single pixel of speckle adds a whole row.
- Uint8 rounding in OpenCV 5's newer CCL paths and similar details are exactly the kind of thing the README warns about - it explicitly flags that the pack was developed with heavy LLM assistance and may contain overfitted or missed cases, and that it is "not recommended in production" without your own verification. No community corpus exists for this pack (a Reddit search for it returns nothing), so verify surprising numbers yourself against a canvas with known blobs - three squares should give six labels, three sane rows of stats after row 0.
- And the generic OpenCV trap, which is the most likely thing to break this node on a working machine: the
cv2namespace is shared by all four OpenCV wheels, so anopencv-pythoninstall over a contrib build silently empties contrib functionality, and Windows portable installs are famous forDLL load failed while importing cv2.tools/repair_opencv_contrib.py --checkin the repo is the first stop.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| image | NPARRAY,IMAGE,MASK | the 8-bit single-channel image to be labeled 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. | |
| connectivityopt | INT | 8-2147483648–2147483647 | 8 or 4 for 8-way or 4-way connectivity respectively Preset to the OpenCV default (8). |
| ltypeopt | COMBO | CV_32S | output image label type. Currently CV_32S and CV_16U are supported. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| retval | INT | — |
| labels | NPARRAY | — |
| stats | NPARRAY | — |
| centroids | NPARRAY | — |