Nodes/ComfyUI-Recolor/🎨 Statistical Transfer
ComfyUI Node

🎨 Statistical Transfer

Recolor that keeps the shading, not just the color

By benjamin-bertramΒ·Created 6 months agoΒ·Updated 6 months agoΒ· 1
🎨 Statistical Transfer
  • image
  • mask
  • recolored
β—„target_r128β–Ί
β—„target_g128β–Ί
β—„target_b128β–Ί
β—„luminance_strength0.70β–Ί
β—„target_spread0.25β–Ί
β—„edge_feather2β–Ί

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.

CategoryAICG/Recolor

Inputs (8)

NameTypeDefaultDescription
imageIMAGEβ€”
maskMASKβ€”
target_rINT1280–255β€”
target_gINT1280–255β€”
target_bINT1280–255β€”
luminance_strengthFLOAT0.700–1β€”
target_spreadFLOAT0.250–1β€”
edge_featherINT20–50β€”

Outputs (1)

NameTypeDescription
recoloredIMAGEβ€”