Colorize Superpixels (FLANN)
Steal the palette from a reference image
- target
- reference
- image
- success
What this is for
A different flavour of colorization from the learned kind: you supply a reference image, and the node transfers its colour onto your grayscale target while keeping the target's own luminance structure. Point it at a summer photo of a garden and a grey scan of a garden, and the grey scan comes back summer-ish. It's the classic "colour transfer" problem, and this implementation does it through superpixel statistics + a FLANN index - the sort of pipeline you'd otherwise write in a notebook outside ComfyUI entirely.
Practical uses: tinting a depth or mask-derived render with a palette from a real photo, matching the grade of a reference when nothing generative is appropriate, and resurrecting a monochrome scan when "plausible" is enough and no prompt exists to guide a diffusion pass.
How it works
The pipeline is genuinely hand-rolled (this is one of the pack's NC nodes, meaning the algorithm is theirs and cv2 supplies primitives, not that it's a wrapper):
- Both images are reduced to stride-2 grids of 5×5 luminance patches.
- A PCA feature space is fitted on the reference, and both images are projected into it, giving each patch a compact descriptor.
- Both images are segmented into SLIC superpixels - roughly
n_segof them each. - Per-superpixel mean descriptors are computed on both sides, and the colour side's mean Lab a/b is averaged per segment.
- A FLANN KD-tree is built over the reference's superpixel descriptors, and each gray superpixel runs a kNN query against it.
- Chrominance is transferred by distance-weighted kernel regression over those neighbours - close matches dominate, distant ones contribute a little smoothing.
- The a/b maps are upsampled to full resolution and, if you asked for it, run through a guided filter with the luminance as guide, so colour stops wandering across edges.
Then L + transferred ab is converted back to BGR. Note the whole thing is NPARRAY-native with its own helpers - it does not lean on cv2.ximgproc superpixels, so it works on a build where those are missing.
Inputs and outputs
Required:
target- the grayscale image to colour. A colour image works too; it's converted internally, and you'd still expect the output to take the reference's palette rather than the target's own chroma.reference- whose a/b palette gets transferred. Chrominance only: the reference's luminance is ignored, which is why a bright reference doesn't wash out a dark target.n_seg(default 300, 10–5000) - superpixels. More = finer colour detail, slower.k(default 8) - kNN neighbours. Higher blends more and is smoother; lower is more literal and more prone to patchy matches.
Advanced: flann_checks (search effort), flann_trees (more trees = faster queries, more memory), guided_r (0 disables the guided filter) and guided_eps (higher = smoother, less edge-aware). If you're getting colour bleeding across an obvious edge, the guided filter is the knob; if the result looks flat and dead, guided_r=0 will show you what the raw transfer did.
Two outputs: image and success. success goes false when the pipeline can't produce a result (empty input, no valid segments, FLANN found no matches) - and the node returns a black placeholder image rather than raising, so a failed colorization doesn't halt a long workflow. Branch on it if you care.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
# restart ComfyUI
Or ComfyUI CV in ComfyUI Manager (bmad4ever). Needs Python ≥ 3.12, a V3-node-API ComfyUI and opencv-contrib-python-headless~=5.0.0.93. No models - the "reference" is just an image you already have. Runtime is CPU-bound numpy/cv2 work, so expect a second or three per frame at 300 segments rather than a GPU-speed operation.
Common issues
- Black output.
successis false. Usual causes: empty target, or a target/reference so flat that no superpixels survive. Check the boolean before blaming the palette. - Colour bleeds into everything. Raise
n_seg(finer segments), lowerk, and make sureguided_risn't 0. - Barely any colour at all. The reference and target just don't have matching structure at the patch level - the kernel regression is averaging distant matches into grey. A reference that resembles the target's composition works far better than a reference that merely has nice colours.
- Contrib-backed nodes from this pack missing. A non-contrib OpenCV wheel overwrote the shared
site-packages/cv2;tools/repair_opencv_contrib.py --check/--apply.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| target | IMAGE | Grayscale image to colorize (converted to grayscale internally, so a color image also works). | |
| reference | IMAGE | Color reference whose a/b palette is transferred. The target's own luminance structure is preserved. | |
| n_seg | INT | 30010–5000 | Number of SLIC superpixels. More segments = finer color detail but slower. |
| k | INT | 81–128 | kNN neighbors. Higher = smoother, more blending. |
| flann_checksopt | INT | 161–1024 | FLANN KDTree search checks. Higher = more accurate, slower. |
| flann_treesopt | INT | 51–50 | FLANN KDTree tree count. More trees = faster queries, more memory. |
| guided_ropt | INT | 120–100 | Guided filter radius (0 = disabled). Smooths chrominance while respecting luminance edges. |
| guided_epsopt | FLOAT | 0.040–10 | Guided filter epsilon squared. Higher = smoother, less edge-aware. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | Colorized result: target luminance + reference chrominance transferred via FLANN superpixel matching. |
| success | BOOLEAN | False when the pipeline could not produce a result (empty input, no valid segments, FLANN found no matches). |