Match Colors to Reference
The color-match node that fixes flicker, because it understands you're working with video
- image_target
- image_ref
- matched_frames
Every color-matching node can do the classic trick: take your image, take a reference, transfer the reference's color statistics onto yours, done. What most of them can't do is handle a sequence without turning it into a strobe light - each frame matched independently to a static reference will wobble, because frame-to-frame noise throws the statistics around. That's the problem this node exists to solve. It's a Reinhard-style Lab color transfer (match per-channel mean and standard deviation), but with a centered temporal window that pools statistics across neighboring frames, so your matched footage stays steady. And it can even run with no reference at all, pulling frames toward their own consensus to kill color drift.
This is squarely the "reach for the cheap deterministic primitive before the expensive generative one" territory - the KB's post-processing doc makes the case that a statistics transfer is instant and deterministic, while re-rolling a generation to fix "the colors are off" is neither. When an inpainted region, an upscaled tile, or a composited subject doesn't sit in its scene's light, this is the honest fix.
How it works
It converts frames to CIE Lab, computes each channel's mean and standard deviation, then maps the target's stats onto the reference's. Doing it per-channel in Lab is what preserves the frame's internal color relationships - it's a global shift and scale, not a per-pixel remap. The video-aware parts are where it gets clever:
frame_window(default 9) pools Lab stats over a centered window of frames (9 = this frame plus 4 behind and 4 ahead). The window wraps around the batch, so a looping sequence's last-to-first transition is stabilized like any other seam.- With
image_refconnected, the pooled stats are the source side: frame t's mapping uses the window consensus instead of its own noisy solo stats, so you get no-lag smoothing (better than an EMA, since it's centered). - Without
image_ref, the pooled stats are the destination: each frame gets pulled toward its neighbors' consensus. That's self-referential drift removal - handy for footage whose color wanders over time, with no reference needed. Set the window longer than the drift or it just re-anchors locally. - Alpha awareness: if your frames have an embedded alpha channel, it weights the statistics so transparent regions (say, a keyed-out background still lurking in RGB) don't skew the mapping. The target's alpha is passed through untouched.
Inputs and outputs
image_target(IMAGE) - the frames to fix.strength(0–1, default 1) - blend between original (0) and fully matched (1). Start at 0.6-0.8 for compositing; full strength can over-flatten a frame.frame_window(1–99, odd values, default 9) - the pooling window described above.image_ref(optional IMAGE) - the absolute reference. A batch of 1 applies to all targets; otherwise its batch size must match the target's. Leave unconnected for the self-consensus mode.
Output is matched_frames (IMAGE), same batch size as input.
Installing it
It's part of Link Comfy Nodes:
cd ComfyUI/custom_nodes
git clone https://github.com/Mister-Link/link-comfy-nodes
cd link-comfy-nodes
pip install -r requirements.txt
or ComfyUI Manager → "Link Comfy Nodes", then restart. No model downloads - this one is pure numpy/torch pixel math, which is exactly the point.
Common issues
- Ref batch size doesn't match target - a multi-frame reference must match the target's batch count; only a single-frame ref broadcasts to everything.
- No visible change with no reference - with
frame_windowat 1 and noimage_ref, each frame's consensus is itself, so it's a no-op by design. You need a window for the drift-removal mode to do anything. - Non-looping footage gets a weird first frame - the window wraps around the batch because the node assumes loops. For non-looping sequences, that wrap makes the first frames borrow statistics from the end of the clip. Keep that in mind before blaming the node.
strength1.0 looks washed out - Lab mean/std transfer flattens contrast by construction; back the strength off.
For matching one still to a reference, plenty of packs do the job. For matching a sequence - or de-drifting a loop without hunting for a reference - this is the one to reach for.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| image_target | IMAGE | — | |
| strength | FLOAT | 0.880–1 | — |
| max_fit_samples | INT | 600001000–500000 | — |
| per_bin_cap | INT | 3001–10000 | — |
| bin_L | FLOAT | 4.00.1–50 | — |
| bin_ab | FLOAT | 6.00.1–50 | — |
| trim_low | FLOAT | 0.50–49 | — |
| trim_high | FLOAT | 99.551–100 | — |
| seed | INT | 70–2147483647 | — |
| image_refopt | IMAGE | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| matched_frames | IMAGE | — |