Color Shift Fix (Swwan)
Fix the washed-out color cast that video inpainting leaves behind
- images
- reference_images
- mask
- image
You inpainted a watermark out of a video, frame by frame, and the whole clip came back washed out, brightened, slightly warm - and it shimmers every few frames. That's not your mask, and it's not your sampler. Video models like LTX regenerate the masked region with a slightly different global tone on each frame, and when you composite them back, the color drifts like it can't hold still. Color Shift Fix (Swwan) is the small node at the end of that pipeline that slaps the color back into alignment.
What it actually does
The idea is almost annoyingly simple. Your inpaint workflow has two versions of the same footage: the original frames and the regenerated frames. The regenerated ones only changed inside your mask - the area outside it should be identical to the original. So this node treats the unmasked region as ground truth and fits a per-channel transform to it.
For each frame and each RGB channel it solves corrected = gain * x + offset - an affine fit - using least squares on the pixels outside the mask, then blends the corrected frame with the original by strength. That's the whole mechanism, and why it's called "Shift" and not "Match": it's fixing a global gain/offset shift, not recoloring.
Two details make it work on video instead of per-frame images:
- Iterative trimmed least squares. It fits, computes residuals, drops the worst pixels (above
trim_percentile), refits - three rounds. The pixels it keeps dropping are exactly the ones whose content genuinely changed, like your erased watermark or bottom bar. That's what keeps the fit honest. - Temporal smoothing. The per-frame coefficients pass through a sliding-window average (
smooth_windowframes wide, edges replicated), so neighboring frames share similar gain/offset. That averaging is the anti-flicker step. A 9-frame window at 24fps is roughly 0.4 seconds - about right for a static scene, smaller if your cuts come fast.
It's built for the crop-based video-inpaint loop (crop the region, inpaint the crop, paste back - the same crop-zoom-paste loop image detailers use, applied to footage). The author's LTX watermark-removal workflow wires it: images from VAE Decode, reference_images from the original frames of the same crop, mask from ImageToMask, and the output straight into Restore Crop Box's croped_image input.
The inputs that matter
There are only two you'll think about, plus one trap:
images- the regenerated/inpainted frames you want to correct.reference_images- the original frames of the same crop region. This must line up withimages. The node forgives batch count (repeats modulo) and size (bilinear resize), but it cannot fix content that isn't aligned - feed it the inpainted frames as reference and the fit learns nothing.mask(optional) - the region that was inpainted; everything outside it is the trusted reference. Leave it empty and the node falls back to a whole-frame fit, fine when the inpaint touched only a small area.strength- 0 to 1 blend of the correction. Default 1.0 is full correction; dial it back if the fix overcorrects.smooth_window(default 9) andtrim_percentile(default 70) - the flicker and outlier controls above. Start at defaults; adjust the window only if the video has hard cuts.
Output is a single IMAGE - same frames, corrected color. Wire it into whatever pastes the crop back.
Installing it
It ships in the ComfyUI_Swwan pack. Easiest route is ComfyUI Manager - search "ComfyUI_Swwan" and install. Manual way:
cd ComfyUI/custom_nodes
git clone https://github.com/aining2022/ComfyUI_Swwan
pip install -r ComfyUI_Swwan/requirements.txt
Then restart ComfyUI. No model files to download for this node - it's pure torch math. Fair warning: the pack's requirements.txt is a kitchen-sink list (opencv, scipy, matplotlib, scikit-image, spandrel, color-matcher), most of it for other nodes. This one only really needs torch and torchvision, but you'll eat the whole list either way.
Gotchas
- ComfyUI Manager may flag a "conflict" with KJNodes or rgthree. This is real and semi-expected: the pack ships "KJ Alternative" variants with separate node types so it can sit next to KJNodes, but it also registers a node literally named
Seed (rgthree), which collides with the real rgthree pack. People have posted about the warning showing up even when they thought the pack wasn't installed. If you're not actually using both packs, it's usually safe to ignore. - Reference alignment is the #1 failure mode. Wrong reference frames → the fit matches noise → the output looks worse. Verify the crop region is the same in both inputs before you touch the settings.
- It only fixes color. Contrast compression, brightness, warmth - yes. Motion blur, geometry, a genuinely botched inpaint - no. Run it before you judge the inpaint itself.
For a one-trick color fixer in a personal node grab-bag, the whole thing is one readable function - proof of what it does is five minutes of reading away.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| reference_images | IMAGE | — | |
| strength | FLOAT | 1.000–1 | — |
| smooth_window | INT | 91–99 | — |
| trim_percentile | FLOAT | 7050–95 | — |
| device | COMBO | GPU | 2 options: CPU, GPU |
| maskopt | MASK | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |