CV Take By Index
Paint a Label Map With a Lookup Table (No 256-Colour Limit)
- table
- index
- values
CV Superpixels hands you an int32 label map - one integer per pixel saying which region it belongs to. What you want next is a picture: region 7 gets its own colour, region 8 gets another. That's a table lookup, and this node is the lookup.
Concretely: element i of the index array gets replaced by row i of the table. Feed it the palette from CV Reduce Array By Label (one mean colour per region) plus the label map that produced it, and every region comes back painted. That's the CV Superpixel Paint recipe, and it's the same operation as a colour LUT - with one difference that matters.
Why not just use a LUT
cv2.LUT caps out at 256 entries and wants specific dtypes. This doesn't: any number of table rows, any integer index dtype, any shape. So the same node maps per-component areas or scores from cv2.connectedComponentsWithStats into a heat map, remaps a watershed label image, or applies an arbitrary many-to-one palette - things a 256-entry LUT can only do if you're lucky.
Mechanically it's a numpy gather, so shape beats naivety: an [H,W] index over a (K,3) table gives [H,W,3], and the output keeps the table's dtype. That's why the tooltip tells you to cast it to uint8 with CV Cast Array before previewing - a float table gives float values, and Preview CV Array will happily normalize them for you but a save-to-IMAGE path wants integers.
The inputs that matter
table - (K,) or (K, C...). Row i is what index i stands for. Its dtype is preserved.
index - an integer array of any shape, typically [H,W]. If you hand it a float array with non-integer values it raises, with a message telling you to cast to int32 first. That's a good error, not an annoying one.
out_of_range - what happens when an index falls outside 0..K-1. clamp (the default) snaps to the nearest row, wrap goes modulo K, and fill writes fill_value instead.
fill_value (default 0, advanced) - only used by fill, cast to the table's dtype. Set it thoughtfully: it's your "unknown region" marker, and a 0 in a per-region score table reads as "definitely zero score" rather than "no data".
Output is a single values array. One edge case worth knowing because it's handled rather than thrown: an empty table (zero detections, zero regions) fills the output instead of raising. An empty result is a real outcome, not a wiring mistake.
Install
ComfyUI Manager → search ComfyUI CV (publisher bmad4ever), or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Restart after. Python ≥ 3.12 and a recent ComfyUI on the V3 node API. The contrib wheel matters for the rest of the pack - a plain opencv-python install shares site-packages/cv2 and silently empties the contrib submodules. tools/repair_opencv_contrib.py --check / --apply.
25_saliency_superpixels.json and 16_npr_filters_playground.json are the reference graphs for the paint-back chain. Sample images come in via 01_install_example_inputs.json (run it, then reload the page).
Where it bites
Row order is everything and nothing checks it. If your palette rows aren't in the same index order as your label map - because you sorted the regions, or filtered some out, or ran a k that dropped a cluster - you get a beautifully painted image with the colours assigned to the wrong regions. Repainting a static palette over a new segmentation is the classic version of this bug.
Also worth knowing if you're chasing a preview that looks washed out: the output dtype follows the table, not the visual range. A table of mean colours in float 0–1 casts straight to black on an integer image path. CV Cast Array first, then look.
Standing caveat, author's words not mine: personal project, heavy LLM assistance, no support planned, curated against one OpenCV build. This node is a small, careful numpy operation with better error messages than most of the ecosystem ships, so it's one of the pack's safer pieces.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| table | NPARRAY | Lookup table, (K,) or (K,C...): row i is the value that index i stands for. Its dtype is preserved. | |
| index | NPARRAY | Integer array of any shape - typically an [H,W] label map. Each element is replaced by the corresponding table row. | |
| out_of_range | COMBO | clamp | What to do with an index outside 0..K-1: clamp to the nearest row, wrap around (modulo K, for a repeating palette), or write 'fill_value'. |
| fill_valueopt | FLOAT | 0.00-1000000000000–1000000000000 | Value written for out-of-range indices when out_of_range is 'fill' (cast to the table's dtype). |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| values | NPARRAY | The gathered array: the shape of 'index' plus the table's trailing axes, in the table's dtype. An [H,W] index over a (K,3) table gives [H,W,3] - cast it to uint8 with 'CV Cast Array' to preview it. |