Wavelet Color Fix
Steal the color grade from one shot and keep your detail
- target_image
- source_image
- image
Color drift is the silent killer of otherwise-good image work. You upscale a frame, re-render a pass, or push something through a second model, and suddenly the colors aren't quite the reference anymore. WaveletColorFix exists to pull that back: it takes the color from one image and lays it onto another - while leaving the target's fine detail alone. If you've ever wished you could "make this look like that" without a LUT and without flattening the image, this is that node.
How it works
It's a color-transfer node with two different algorithms under one align_method dropdown, and the choice genuinely changes what happens:
adain(Adaptive Instance Normalization) - matches the global color statistics: it normalizes the target's per-channel mean and standard deviation to match the source's. Simple, global, one shot. Your target takes on the source's overall brightness, contrast, and tint.wavelet(default) - smarter about detail. It decomposes each frame into low-frequency (color/blur) and high-frequency (detail/edges) components using a 5-level pyramid of blurring convolutions, keeps the target's high-frequency detail, and swaps in the source's low-frequency color. Result: the target keeps its sharpness while inheriting the source's palette.
The wavelet path is the one you'll actually want for fixing upscaled images, because a plain color match tends to smear detail - the whole point of the split is that it doesn't. If you want a flat global re-grade instead, AdaIN is the blunt instrument for the job.
It processes frame-by-frame with a progress bar, and if you feed it a video where source and target have different frame counts, it loops the shorter one and logs a warning rather than failing. Inputs are target_image (the one whose detail you keep), source_image (the one lending its colors), and align_method. Output is image, ready for SaveImage or the next node. It needs RGB or RGBA input; if your target is RGBA, the alpha channel passes through intact.
Where this kind of node shows up
You'll recognize the lineage if you've done upscaling workflows: this is the same color-fix trick that StableSR and its ports bake in, where the upscaled output gets color-matched back to the original because pure upscalers quietly shift color. Here it's exposed as a standalone node, so you can drop it in anywhere the color drifted - a rediffused frame, a frame from a different model's pass, a video clip that needs to match a reference grade. It's the kind of small utility that quietly saves you fifteen minutes of curve fiddling in an editor.
Installing
Manager: search "ComfyUI-FrameUtilitys". Or:
cd ComfyUI/custom_nodes
git clone https://github.com/lum3on/ComfyUI-FrameUtilitys
Restart. Zero extra dependencies - it's pure PyTorch plus ComfyUI's own comfy.utils progress bar, so nothing to download.
Troubleshooting
- "Expected a 4D tensor" / "must be IMAGE tensors" - both inputs need to be IMAGE type in
[B, H, W, C]format, straight from LoadImage or a video loader. - Colors look flat or washed - you're probably on
adain, which is the global-statistics path. Switch towaveletto keep detail and get a subtler transfer. - Frame count mismatch warning - expected if your clips aren't the same length; the source just loops. Check the console if the result looks wrong.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| target_image | IMAGE | Image or frame sequence whose detail should be preserved | |
| source_image | IMAGE | Image or frame sequence providing the color statistics | |
| align_method | COMBO | wavelet | AdaIN matches global color statistics; wavelet transfers low-frequency color |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |