Latent Normalized Lanczos Resize(LNLR)
A latent upscale that doesn't blur — decode, Lanczos, re-encode, match moments
- latent
- vae
- LATENT
Here's the classic hi-res fix problem. You upscale a latent, run a second KSampler pass at low denoise, and the image comes out soft. If you used the plain latent upscale nodes, that's why - a nearest-neighbor upscale in latent space is a blur factory, and "latent upscale blurs my image" is a recurring complaint in r/comfyui for a reason.
LatentNormalizedLanczosResize (LNLR) is the fix that people hand-roll anyway, in one node: decode to pixels, upscale with a real Lanczos interpolator in image space, re-encode, and - the part most hand-rolled versions skip - force the new latent's statistics to match the original. It lives in the image/upscaling category and outputs a LATENT, so it plugs in exactly where "Upscale Latent" used to sit, ahead of a second KSampler.
How it works
The internal order is spelled out in the source and it's worth knowing because it tells you why it behaves:
- Soft outlier clamp (on by default) - uses a
huberize_quantilestep to compress extreme latent values instead of hard-clipping them, so decode doesn't amplify noise spikes. - Decode - latent to image through your VAE.
- Lanczos upscale - image upscaled by
size_multiplier. - Re-encode - back to latent space.
- Optional blend - if
add_latent_upscale_with_weightis above 0, the re-encoded latent is blended toward a direct nearest-exact latent upscale of the original. - Moment matching - the core trick: per-channel mean and variance of the upscaled latent are matched to the original latent. This is what keeps it from looking washed out or contrast-shifted the way a naive decode→encode round trip does.
- Optional correlated Gaussian noise - smoothed noise added back, for variation.
Inputs that matter
latentandvae- your latents and the VAE that decoded them.size_multiplier(default 2, range 0.1–4) - the resize factor. 2x is the sweet spot; 4x means a much bigger second pass and more VRAM.soft_clamp_outliers(default on) - leave it on;outlier_quantile(0.01) andoutlier_clamp_slope(0.1) tune how aggressive the clamp is.add_latent_upscale_with_weight(default 0) - 0–1, blend toward a plain nearest latent upscale. If you want a bit of that latent-space character back, nudge it up.add_latent_noise(default off) withlatent_noise_std(1.0),latent_noise_scale(0.1) andnoise_seed- correlated noise to inject detail variation when the second pass is about adding texture, not just resolution.
The output is a single LATENT. Wire it into a second KSampler at low denoise (0.3–0.5 is the classic hi-res fix range).
Where it sits in the upscaling ladder
Be clear about what this isn't. LNLR is the "more pixels" rung, not the "more detail" rung - Lanczos adds no content and can't hallucinate, which is a feature when you just want a sharp, faithful base for re-diffusion. If you need genuine detail restoration, you're in SeedVR2/SUPIR territory, which is a different job. This is the cheap, predictable, milliseconds-fast option in the middle of a hi-res fix workflow.
Install and gotchas
ComfyUI Manager, search Comfy Latent Tools, install, restart. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/JTriggerFish/ComfyLatentTools
Then restart. No model downloads; dependencies are just torch, torchvision, pillow and numpy.
Three things to know before you trust it in a production workflow. It costs a VAE decode plus an encode every run, so it's slower than a pure latent upscale - that's the price of the sharper base. The pack is openly experimental (README: "MANY MISSING NODES HERE - WILL BE UPDATED"), so pin the version you're happy with. And the noise is off by default for a reason: it's the knob that introduces variation, but crank latent_noise_scale too high and the second pass starts inventing artifacts. Start with it off, add it only if your refinement pass comes out too sterile.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| latent | LATENT | — | |
| vae | VAE | — | |
| size_multiplier | FLOAT | 2.00.1–4 | — |
| soft_clamp_outliers | BOOLEAN | true | — |
| outlier_quantile | FLOAT | 0.010–1 | — |
| outlier_clamp_slope | FLOAT | 0.10–1 | — |
| add_latent_noise | BOOLEAN | false | — |
| latent_noise_std | FLOAT | 1.000–10 | — |
| latent_noise_scale | FLOAT | 0.100.01–10 | — |
| add_latent_upscale_with_weight | FLOAT | 0.000–1 | — |
| noise_seed | INT | 00–18446744073709550000 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| LATENT | LATENT | — |