LucidNFT Color Align
The Two-Node Color Fix That Saves You a Re-Roll
- image
- reference
- IMAGE
What it's for
LucidNFT restores real-world photos - soft phone shots, compressed JPEGs, scanned prints - by regenerating pixels at 1024 or 2K. Regenerating pixels has a bill attached: the colour and brightness drift. A touch warmer, a touch flatter, sometimes a visible cast that wasn't in the source. Nobody notices it in a vacuum; everybody notices it next to the original.
LucidNFT Color Align is the node that drags it back. You give it your decoded render and a reference, and it keeps the render's fine detail while borrowing the reference's low-frequency colour and luminance. The reference is usually the ldr output from LucidNFT DiffBIR - the SwinIR restoration of your original photo - which carries exactly the colour you're trying to keep.
This is the difference between "upgraded" and "same photo, better". If you're running the pack at all, you should be running this at the end.
How it works
It's a straight port of the official wavelet_reconstruction from LucidNFT, rewritten in pure torch - the pack's version dropped the PIL and torchvision imports so it needs nothing beyond what ComfyUI already ships.
The mechanism is frequency surgery. The image gets decomposed at five levels; at each level a small Gaussian-ish 3×3 kernel, with the radius doubling every pass, blurs it, and the difference between the image and the blur becomes the high-frequency band. Then the node throws away the render's low-frequency band, takes the reference's low-frequency band instead, and adds them back together. High frequency from your diffusion pass, low frequency from reality.
Nothing is trained and nothing is invented - it's the same family of operation as classic Reinhard-style colour transfer, and the standing advice applies: a statistics swap is instant and deterministic, so reach for it instead of burning another diffusion pass because the colours are off.
strength blends the result back toward the original in pixel space. At 1.0 (the default) you get the full low-frequency swap. At 0.5 you split the difference. At exactly 0 the node returns your input untouched - it's a literal early return, not a no-op blend - which makes A/B comparisons cheap.
The inputs that matter
Only three fields, all of them required:
- image - the VAE-decoded render. The thing with the detail you want to keep.
- reference - the
ldroutput from LucidNFT DiffBIR. If its dimensions don't matchimageit gets bilinearly resized to fit, and if the batch sizes differ the reference is repeated. No crash, no warning; it just quietly upscales. Which is fine, because low frequency doesn't care about resolution. - strength - 0 to 1, default 1.0, step 0.05.
One output: a plain IMAGE, straight into SaveImage or an ImageCompare next to the original. It lives between VAEDecode and SaveImage, and because it upscales the reference itself you don't need to pre-resize ldr to 2K by hand.
Installing it
Manager route: search LucidNFT in ComfyUI Manager, install the pack, restart. Manual route:
cd ComfyUI/custom_nodes
git clone https://github.com/smthemex/ComfyUI_LucidNFT
Then restart ComfyUI. The pack's requirements.txt is just torch and einops, both already in ComfyUI - no diffusers, no transformers, no peft, which is the main reason this one installs cleanly.
You do still need the pack's weights in ComfyUI/models/LucidFlux/, because this node is the last step of a pipeline whose earlier nodes will refuse to run without them:
ComfyUI/models/
├── diffusion_models/flux1-dev-fp8.safetensors
├── loras/LucidNFT_lora_dit.safetensors
├── clip_vision/siglip2-so400m-patch16-512.safetensors
└── LucidFlux/
├── general_swinir_v1.ckpt
├── lucidflux.pth
├── prompt_embeddings.pt
├── lucid_connector.pth
└── lora_condition/{adapter_config.json, adapter_model.safetensors}
Color Align itself loads no weights - but the ldr it wants to reference comes from the SwinIR stage, so there's no road to this node that skips the downloads.
Where people get burned
A too-strong swap looks like a bad upscale. Full strength assumes the reference really is your source image at low quality. If you point reference at a different photo - sometimes a neat colour-transfer trick, sometimes an accident - you're reassigning the entire palette of your output. Dial strength to 0.3–0.6 for that.
It's per-image, on the GPU. The node loops the batch, copies each frame to the card, processes, copies back. One image is microseconds; forty is noticeably not, though still nothing next to the sampler.
It's not a colour-managed transform. A wavelet low-pass is a spatial filter. If your render came out materially too dark, this smears the reference's brightness in rather than correcting it, and pushing strength to 1.0 just makes the mismatch more obvious.
That's the whole node. It's the least glamorous thing in the pack and the one most likely to be the difference between an image you keep and one you re-roll.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| reference | IMAGE | — | |
| strength | FLOAT | 1.000–1 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |