CV Connected Components (Split Mask)
One mask in, one mask per blob out
- mask
- region_masks
- bboxes
- count
What this is for
A single binary mask with four objects in it is a problem the moment you want to treat those objects differently - one inpaint per region, one crop per face, one colour per blob. The detailing loop the community runs (detect a region, crop it, re-render it, paste it back) is structurally per-region, and this is the node that gets you from "one mask" to "four masks" without writing a loop.
It's the plain-CV counterpart to the Impact Pack's SEGS container: same idea - a set of regions as data - different transport.
How it works
cv2.connectedComponentsWithStats labels the blobs, then the node filters, sorts and slices them. Each surviving label becomes its own full-size mask (the whole frame is present, only that region is white), and the outputs are assembled as batches so downstream nodes iterate over them naturally.
The filters exist because raw connected components on a real mask is mostly noise: min_area drops specks below a pixel count (16 by default - raise it on a 4K frame, or you'll get hundreds of mask entries for JPEG mush), and max_regions caps the count after sorting so a pathological mask doesn't spawn 12,000 outputs.
sort decides which regions survive that cap and what order they come out in: largest first (the useful default), left to right, or top to bottom. Order is not cosmetic here - if you're pairing region i with a label, a crop, or a per-region prompt from a list, the sort order is the correspondence.
One batch subtlety: if the input mask is itself a batch, the frames are merged (bitwise OR) before splitting. You get the union, not per-frame components.
Empty is a normal outcome: no components, or all of them below min_area, yields empty batches and count = 0 instead of an exception, so a workflow that fans out over regions simply does nothing that run.
Inputs and outputs
mask- the mask to split; a batch is unioned first.connectivity(default8) - 8 also joins diagonal pixels, so a thin diagonal stroke is one component; 4 splits it. Anti-aliased edges tend to need 8.min_area(default 16),max_regions(default 64),sort- as above.
Three outputs:
region_masks- a MASK batch, one full-size mask per region.bboxes- coreBOUNDING_BOXdata: per-frame lists of{x, y, width, height}dicts, compatible withDraw BBoxes,Crop By Bounding Boxes,Image Cropand friends. This is the important one for staying inside normal ComfyUI.count- how many regions came out.
The canonical next step is CV Crop by Masks, which turns the batch into per-region crops you can process individually - one inpainting pass per region, say. For visual sanity-checking, OpenCV Overlay Masks (this pack) or the core Draw BBoxes node.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
# restart ComfyUI
Manager users: ComfyUI CV (bmad4ever). Requires Python ≥ 3.12, a V3-node-API ComfyUI, and opencv-contrib-python-headless~=5.0.0.93 (installed from the pack's requirements). No models.
Common issues
- Count is absurdly high. Speckle. Raise
min_area, and consider whether connectivity 4 is fragmenting regions that are visually one blob - the two effects roughly cancel out and both look like "it found the wrong things". - A region went missing.
max_regions(64) withlargest firstwill drop small ones once you're past the cap. Checkcountagainst what you expected to see. region_masksare all full-size and mostly black. That's by design - the frame geometry is preserved so each mask lines up with the original image and with the bboxes. If you wanted tight crops,CV Crop by Masksis the next node.- Contrib-backed nodes vanished pack-wide. A non-contrib OpenCV wheel clobbered the shared
site-packages/cv2;python tools/repair_opencv_contrib.py --check, then--apply.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| mask | MASK | Mask to split. A batch is merged (union) before splitting. | |
| connectivity | COMBO | 8 | 8 also connects diagonal pixels; 4 only direct neighbors. |
| min_area | INT | 161–2147483647 | Regions smaller than this many pixels are dropped (removes noise specks). |
| max_regions | INT | 641–1024 | Keep at most this many regions (after sorting). |
| sort | COMBO | largest first | Order of the output region masks/boxes: by area (largest first), left-to-right, or top-to-bottom. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| region_masks | MASK | One full-size mask per region, as a MASK batch. |
| bboxes | BOUNDING_BOX | Core BOUNDING_BOX data: per-frame lists of {x, y, width, height} dicts - compatible with Draw BBoxes, Crop By Bounding Boxes, Image Crop, etc. |
| count | INT | — |