CV Labels to Masks (full size)
Turn a label map into masks ComfyUI understands
- labels
- masks
- label_values
- count
A label map is one of the most useful and least friendly data types in computer vision: an H×W integer image where each pixel holds a region id. Superpixels give you one. Hierarchical segmentation (HFS) gives you one. Connected-component analysis gives you one. k-means clustering gives you one. They're all the same idea - a partition of the image - and they're all useless in ComfyUI, because ComfyUI masking nodes want a MASK, and every downstream masking node wants one mask per region.
CV Labels to Masks (full size) is the bridge. Every distinct id present in the map becomes one full-size mask, and the batch comes back aligned with label_values so you know which mask is which region.
Inputs and outputs
labels is the (H,W) integer map - a batch of maps uses frame 0. sort orders the output: by label value, or by area (largest first). The area sort is the one you'll use most, because it matches how you think about segmentation results: subject first, dust last.
background is the tooltip-heavy widget worth reading. Its default, keep every label, means label 0 gets its own region - and since connected-components-style maps use 0 for background, that's usually one enormous full-frame mask you didn't want. Switch it to drop label 0 and that region is skipped. There's a caveat baked into the design that trips people up: labels need not be contiguous. Nothing requires ids to run 0, 1, 2, 3; your map can hold 0, 7 and 40, and you'll get three masks.
Outputs: masks (a MASK batch, one full-size mask per distinct label, aligned with label_values), label_values ((B,) int32 - the id of each mask), and count (B, the number of distinct labels). Empty input gives an empty MASK batch and count 0, and never raises - which matters, because an image with a single flat region is a perfectly normal frame.
Why it's a genuinely useful pattern
This is the missing half of the "detect → crop → re-render → paste" loop that ComfyUI masking workflows are built on. Anything that produces a region partition on the classic-CV side - segment by colour, cluster by k-means, split a mask into blobs, run superpixels - lands here and comes out as the MASK batch that Crop by Masks, inpainting, and the Impact-Pack-style detailers all consume. Without the label→mask step, all of that work dead-ends in NPARRAY land.
It also composes nicely with sort, since "largest region first" is exactly the order a paste-back step should run in, and with label_values, since that's the thread back to which region you're looking at when you preview mask 3 of 12.
Install
ComfyUI Manager: search the pack title comfyui_cv (bmad4ever/comfyui_cv). Or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
Restart ComfyUI afterwards. It needs Python ≥ 3.12 and a recent ComfyUI built on the V3 node API - the whole pack is io.ComfyNode classes with io.Schema definitions and has no NODE_CLASS_MAPPINGS, so an old install fails silently (no nodes, no error). One dependency:
pip install "opencv-contrib-python-headless~=5.0.0.93"
Pinned because the pack is curated against it, and it must be the contrib wheel: every OpenCV distribution shares one site-packages/cv2 folder, so a non-contrib install over a contrib one silently blanks the contrib submodules, and contrib nodes then vanish from the menu with nothing logged. The pack's tools/repair_opencv_contrib.py --check / --apply handles that.
Common problems
An extra full-frame mask showed up at the front of your batch. That's label 0. Set background to drop label 0.
Mask count jumped between runs. Your label map's distinct ids change with the input - that's your segmentation, not the node. Use count and label_values instead of assuming an index.
A batch of maps only produced masks for the first frame. Documented behaviour: frame 0 is what gets split.
Pack-wide note, from the README itself: heavy LLM assistance in development, example pipelines that were tuned test-first against specific sample data, no planned updates, and an explicit warning against production use without independent review. This node is short and its behaviour is documented well enough to check by eye - do that rather than taking my word for it.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| labels | NPARRAY | (H, W) integer label map; each distinct id becomes one full-size mask. A batch of maps uses frame 0. | |
| sort | COMBO | by label value | Order of the output masks: by label id, or by area (largest first). |
| backgroundopt | COMBO | keep every label | 'drop label 0' skips the background region, which is what a connectedComponents-style map (or any map built with 'CV Take By Index' over one) uses 0 for - without it every such map emits an extra, usually full-frame, background mask. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| masks | MASK | One full-size mask per distinct label, as a MASK batch (aligned with 'label_values'). |
| label_values | NPARRAY | (B,) int32: the label id of each mask. |
| count | INT | B, the number of distinct labels. |