LucidFlux DiffBIR (SwinIR)
Feed the Diffusion a Pre-Cleaned Image So It Stops Inventing Detail
- image
- ldr
- lq
Every restoration pipeline has the same opening problem: the model has to be told what it's looking at, and a blurry, noisy, compressed photo is a bad way to tell it. Give a diffusion restorer raw damage and it spends its early steps guessing - and guessing is where invented eyes and mystery textures come from.
LucidFlux's answer is to run a cheap non-generative restorer first and hand the diffusion model two images: the raw downscaled input (so it knows what it's fixing) and a SwinIR-cleaned version (so it knows what "fixed" roughly looks like). LucidFluxDiffBIR produces both, and it's the first node in the shipped example workflow.
Why a GAN-style restorer in a diffusion pipeline
SwinIR is old, small, and unfashionable. It also doesn't hallucinate - it's a transformer trained to invert degradation, and at low-to-moderate damage it cleans up compression blocking, mild blur and sensor noise without inventing a nose that wasn't there. That's the ideal prior for a diffusion stage: not a finished image, just a version of your input with the damage statistically removed.
This mirrors how DiffBIR (the original paper this node is named for) thinks about the problem, and it's the same instinct that made SUPIR the restoration answer of 2024 - see the upscaling notes on why "restoration" and "super-resolution" are different jobs. Where SUPIR runs an SDXL backbone and captions the image with LLaVA, LucidFlux keeps the prior dumb and small and lets the Flux DiT do the semantic work.
Worth saying plainly: only the SwinIR stage of DiffBIR is implemented here. You're not running the full DiffBIR SD pipeline, you're running its first hop.
What the node does
This is a native-scale restorer, not an upscaler. The SwinIR checkpoint (general_swinir_v1.ckpt, sf=8 with unshuffle_scale=8) runs at whatever size you feed it and returns that same size. So the node does the resizing itself, and width/height are the target size of the SwinIR pass - which is half your final image. For a 2K result, fill 1024. For 4K, 2048. Setting either to 0 keeps the original dimensions.
Two outputs, and both matter:
ldr- the SwinIR restoration. This goes intoLucidFluxCondition'sldrinput, and also (usually) intoLucidFluxColorAlign'sreference.lq- the plain resized input, undamaged by any model. This goes intoLucidFluxCondition'slqinput.
Don't skip lq. The condition node builds two separate control branches from these; feeding the same image to both collapses half the model's job.
The one input that isn't obvious is swinir, a dropdown of files in ComfyUI/models/LucidFlux/ whose names contain "swinir". That filter is literal, so general_swinir_v1.ckpt shows up and a renamed copy that lost the word won't.
How to install this properly
Same pack, so:
cd ComfyUI/custom_nodes
git clone https://github.com/smthemex/ComfyUI_LucidFlux
then grab the one file this node needs and put it in ComfyUI/models/LucidFlux/:
# from https://huggingface.co/lxq007/DiffBIR/blob/main/general_swinir_v1.ckpt
ComfyUI/models/LucidFlux/general_swinir_v1.ckpt
That models/LucidFlux/ path is not a suggestion - the node only looks there, and the pack creates the folder on first startup so it'll be waiting for you.
Common issues
You downloaded one of the other DiffBIR checkpoints. The repo ships v2.1 variants and turbo models that are a different pipeline entirely; the pack loads general_swinir_v1.ckpt with strict=True against a fixed architecture, so anything else either won't appear in the dropdown or will fail to load with a wall of missing-key errors. This has confused enough people that it's the top complaint in the only public thread about the pack - someone asking which DiffBIR version is compatible, with no documentation to answer it.
You set width/height to your final resolution. Then SwinIR runs at full target size, and the control branch tries to align a 2048px hint against a latent that expects 1024. The condition node will silently resize it to match latent × 8, so you don't get a crash - you get a round trip through a bilinear resize that undid exactly the sharpening you paid for. Half the target. Always.
It's slow on the first run. The checkpoint gets loaded and cached, then moved to the GPU per execution and parked back on CPU afterwards with a cache flush. That's deliberate to leave VRAM for the sampler, but on a small card it means you'll see the SwinIR pass appear as a distinct hitch rather than blending into the run.
You expected a 4× upscale. Not this node. LucidFlux gets its scale from the empty latent size and, at the end, from LucidFluxPiDDecode. This node's contribution is quality, not pixels.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| swinir | COMBO | 1 options: none | |
| width | INT | 10240–8192 | — |
| height | INT | 10240–8192 | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| ldr | IMAGE | — |
| lq | IMAGE | — |