π¨ Statistical Transfer
Recolor that keeps the shading, not just the color
- image
- mask
- recolored
When a flat repaint isn't good enough
The direct-replace node gives you the exact target color, but exact is also its weakness: it can flatten a photo. This one is the alternative for images where the variation is the story - a jacket with fabric sheen, a knit with tonal shifts, a product shot with real lighting. Instead of setting every masked pixel to the same color, it does a statistical color transfer: it recenters the image's color distribution on your target and keeps the relative spread of the source. The result reads as the same garment, just in a different colorway.
It's a "Reinhard-style" transfer - the classic technique of matching mean and standard deviation between images. You don't need to know the name, just what it buys you: gradients, fold highlights and shadows get remapped rather than erased. For flat studio-on-white shots the direct replace is arguably better; for anything lit, this is the one you'll reach for.
How it works, in the shape of knobs
Under the hood the node converts to CIELAB (L* = lightness, a*/b* = color), computes the mean and standard deviation of the masked region in each channel, then shifts the source mean onto the target's mean and rescales the spread by a factor you control.
- target_r / target_g / target_b - your colorway spec, 0β255.
- target_spread - 0 to 1, default 0.25. At 0 the result collapses toward flat; at 1.0 you keep the full source variation. The default is deliberately subtle - crank it when the fabric's tonal range is the point.
- luminance_strength - 0β1, default 0.7. How much of the source's lightness distribution you keep vs. how far you chase the target's brightness.
- edge_feather - mask-edge softening in pixels, default 2. Bump to 5β10 for ragged masks.
Inputs are image, mask, and the above; output is a single recolored IMAGE. Same wiring as the rest of the pack - mask in, target color in, image out.
The catch (it's a soft one)
A statistical transfer matches distribution shape, so the result is never pixel-exact to your target RGB. If you're matching a Pantone spec where the buyer will measure the hex, use Direct Replace Recolor instead. If you're generating a colorway that has to look like photography, this usually wins. The two exist precisely because those are different jobs.
Also worth knowing: the transfer is computed per image, so specular highlights in your source become highlights in the same relative place - the highlight color migrates to the target too. That's usually desirable, but on a glossy product it can look washed if your source was overexposed.
Install
Same as every node in this pack:
cd ComfyUI/custom_nodes
git clone https://github.com/benjamin-bertram/ComfyUI-Recolor
cd ComfyUI-Recolor
pip install -r requirements.txt
Then restart ComfyUI (or use Manager and search "ComfyUI-Recolor"). No models to download - the pack is pure math on torch, opencv-python and scikit-image. The mask still comes from elsewhere, typically SAM via ComfyUI-Impact-Pack, or rembg for whole-object extraction.
One limitation shared with its siblings: only image[0] is processed, so feed single frames. And if your output is identical to the input, your mask is empty or doesn't overlap the frame - the node returns the source unchanged in that case, which is easy to mistake for a bug.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | β | |
| mask | MASK | β | |
| target_r | INT | 1280β255 | β |
| target_g | INT | 1280β255 | β |
| target_b | INT | 1280β255 | β |
| luminance_strength | FLOAT | 0.700β1 | β |
| target_spread | FLOAT | 0.250β1 | β |
| edge_feather | INT | 20β50 | β |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| recolored | IMAGE | β |