Color Match (Swwan)
Make this image wear that image's palette
- image_ref
- image_target
- reference_mask
- image
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.hmis 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, so0is an early return (target passes through untouched) and1is the full transfer. Above 1 overshoots, which is occasionally exactly what you want on a washed-out frame.match_mode-color_matcheruses thecolor-matcherpackage;mean_stdswaps in the pack's own mean/std algorithm (Essentials-derived, via kornia) and unlockscolor_space,factor,batch_size,deviceandreference_mask.reference_mask- restricts which pixels of the reference count toward the statistics. Useful when your reference has a big distracting background.color_space-LABby default; the mean/std path also offersYCbCr,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.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| image_ref | IMAGE | — | |
| image_target | IMAGE | — | |
| method | COMBO | mkl | 6 options: mkl, hm, reinhard, mvgd, hm-mvgd-hm, hm-mkl-hm |
| strengthopt | FLOAT | 1.000–10 | — |
| multithreadopt | BOOLEAN | true | — |
| match_modeopt | COMBO | color_matcher | 2 options: color_matcher, mean_std |
| reference_maskopt | MASK | — | |
| color_spaceopt | COMBO | 6 options: LAB, YCbCr, RGB, LUV, YUV, XYZ | |
| factoropt | FLOAT | 1.000–1 | — |
| deviceopt | COMBO | 3 options: auto, cpu, gpu | |
| batch_sizeopt | INT | 00–1024 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |