Nodes/ComfyUI_Swwan/Color Match (Swwan)
ComfyUI Node

Color Match (Swwan)

Make this image wear that image's palette

By aining2022·Created 10 months ago·Updated about 18 hours ago· 33
Color Match (Swwan)
  • image_ref
  • image_target
  • reference_mask
  • image
◄methodmkl►
◄strength1.00►
◄multithreadtrue►
◄match_modecolor_matcher►
◄color_space▾►
◄factor1.00►
◄device▾►
◄batch_size0►

The cheap deterministic fix for "the colours are off"

Color Match does colour transfer: give it a reference image and a target image, and it shifts the target's colour statistics toward the reference. Not a filter, not a re-gen - a statistics transfer, which means it's instant, it's deterministic, and running it twice gives the same answer.

Three jobs this is genuinely the right tool for, all of which people otherwise fix by re-rolling the seed: making an outpainted or inpainted region agree with the rest of the frame, making upscaled tiles match their neighbours, and making a composited subject sit in a new background's light. The wider rule from the post-processing layer of this ecosystem is worth repeating - reach for the cheap deterministic primitive before the expensive generative one. A re-gen to fix colour is neither cheap nor deterministic.

It's KJNodes' ColorMatch (itself riffing on the Essentials colour nodes) re-registered under a Swwan ID.

Which one is the reference?

This is the trap, so here it is up front. image_ref is the image whose look you want to copy. image_target is the image being changed. The output is image_target, recoloured. Read the names the way they're written and you're fine; read them as "image 1 and image 2" and you'll protect the wrong one.

The inputs

  • method - mkl, hm, reinhard, mvgd, hm-mvgd-hm, hm-mkl-hm. mkl (Monge-Kantorovich linearisation) is the default and the mildest-feeling of the set. hm is plain histogram matching, the most aggressive and the one most likely to produce banding on gradients.
  • strength - 0 to 10, default 1. It scales the difference, so 0 is an early return (target passes through untouched) and 1 is the full transfer. Above 1 overshoots, which is occasionally exactly what you want on a washed-out frame.
  • match_mode - color_matcher uses the color-matcher package; mean_std swaps in the pack's own mean/std algorithm (Essentials-derived, via kornia) and unlocks color_space, factor, batch_size, device and reference_mask.
  • reference_mask - restricts which pixels of the reference count toward the statistics. Useful when your reference has a big distracting background.
  • color_space - LAB by default; the mean/std path also offers YCbCr, RGB, LUV, YUV, XYZ.
  • multithread - default on, one worker per frame up to your CPU count.

Output is a single image. It clamps to 0–1 at the end, so no downstream surprises.

Batches

Frames are paired by index. If image_ref holds exactly one image it's reused for every target frame, which is the common case; otherwise frame n of the target is matched to frame n of the reference, and mismatched counts blow up with an index error. If a single frame fails mid-batch, the node prints the error and silently passes that frame through unchanged - so a batch that's 90% matched with two odd frames is a symptom, not a mystery.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/aining2022/ComfyUI_Swwan
cd ComfyUI_Swwan
python -m pip install -r requirements.txt
python -m pip install color-matcher     # only needed for match_mode = color_matcher

Manager users: install ComfyUI Swwan from the node search, and note that color-matcher is not in the pack's requirements.txt - the README calls it out as a separate install because it's an optional dependency. The mean_std mode doesn't need it at all; it uses kornia, which ships with ComfyUI.

When it errors

The familiar one is a hard failure on the first run: Can't import color-matcher, did you install requirements.txt? Manual install: pip install color-matcher. That means exactly what it says, and the fix is in the same Python environment ComfyUI runs in. Installing it into a different venv and wondering why nothing changed is the classic variant.

Second: if mean_std is your mode and kornia is somehow missing, you get a RuntimeError mentioning kornia rather than a crash - the pack catches the import and re-raises with context, which is more than most nodes bother with.

Third, expectations. Match is statistics, not semantics: it will happily make a sunset match a snowy street's cool cast, which is right when you're compositing and wrong when you wanted the sunset. That's why strength exists. Nudge it to 0.3–0.5 and see if the seam disappears before you commit to the full transfer.

CategorySwwan/Advanced/Image

Inputs (11)

NameTypeDefaultDescription
image_refIMAGE—
image_targetIMAGE—
methodCOMBOmkl6 options: mkl, hm, reinhard, mvgd, hm-mvgd-hm, hm-mkl-hm
strengthoptFLOAT1.000–10—
multithreadoptBOOLEANtrue—
match_modeoptCOMBOcolor_matcher2 options: color_matcher, mean_std
reference_maskoptMASK—
color_spaceoptCOMBO6 options: LAB, YCbCr, RGB, LUV, YUV, XYZ
factoroptFLOAT1.000–1—
deviceoptCOMBO3 options: auto, cpu, gpu
batch_sizeoptINT00–1024—

Outputs (1)

NameTypeDescription
imageIMAGE—