A Forge Neo extension for NVIDIA PiD — a diffusion decoder that turns a finished latent into an image four times larger than the generation that produced it.
This is a standalone, more configurable alternative to the built-in PiD Integrated extension. Compared to that one it adds an output-scale selector (keep all 4x, or take the 4x render back down to 2x / 1.5x / 1.25x / 1x), a choice of downscaling filter, and a small set of advanced sampling controls — and it shows up in the UI whether or not you have a PiD checkpoint installed yet.
Extensions → Install from URL:
https://github.com/aoleg/Neo-PiD
Or clone into your Forge Neo extensions directory and restart:
git clone https://github.com/aoleg/Neo-PiDNo extra dependencies.
PiD is a diffusion model in its own right, not a VAE — it goes in
models/Stable-diffusion (a PiD/ subfolder keeps it out of the way), not in
models/VAE. Alongside it you need the VAE and the Gemma2 text encoder for the latent
space you are decoding, both in the usual models/VAE and models/text_encoder folders.
| Checkpoint | Conditions on | Use with |
|---|---|---|
pid_1.5_flux1_… |
16-channel Flux.1 latents | Flux.1, Chroma |
pid_1.5_qwenimage_… |
16-channel Wan 2.1 latents | Qwen-Image, Krea 2, Wan 2.1 / 2.2 |
pid_1.5_flux2_… |
128-channel Flux.2 latents | Flux.2 |
PiD does not care which model generated the latent, only which latent space it is in — the Qwen build decodes Krea 2 and Wan just as well, because all three share the Wan 2.1 latent space.
Open the PiD (Neo) accordion, enable it, and pick a PiD checkpoint plus its VAE and text encoder. Your normal generation runs as usual; PiD then re-renders each image from its latent and adds the result to the gallery alongside the original.
| Control | Default | Description |
|---|---|---|
| Prompt | (empty) | Prompt for the PiD pass. Empty means reuse the generation's prompt. |
| PiD | first PiD-looking checkpoint | The decoder. 🔄 rescans checkpoints, VAEs and text encoders. |
| VAE | first available | The source model's VAE — PiD needs it to read the latent. |
| Gemma2 2B IT ELM | first available | PiD's own text encoder. |
| Degrade Sigma | 0 |
How much freedom PiD has to change the image. 0 reproduces it; higher values let it reinterpret detail. |
| Color Correction | on | Match the result's histogram to the original decode. Fixes PiD's tendency to shift colors. |
| Output scale | 4x |
Size of the saved image, relative to the generation. |
| Downscale filter | Lanczos |
Which filter to resample with. Unused at 4x. |
PiD always renders at its native scale — 4x for every published checkpoint — and anything
below that is a downscale of that render, not a cheaper render. A 1024x1024 generation with
Output scale set to:
- 4x → saved at 4096x4096, exactly what PiD produced
- 2x → 2048x2048
- 1.5x → 1536x1536
- 1.25x → 1280x1280
- 1x → 1024x1024, the same size you asked for
The lower settings are not a fallback. Downsampling a 4x render averages away the
decoder's residual noise, so 1x gives you a normally sized image that is noticeably
cleaner and more detailed than a plain VAE decode of the same latent — at the cost of a full
PiD pass.
Lanczos, Bicubic, Hamming, Bilinear, Area (Pillow's box filter) and Nearest.
Lanczos is the default and the right answer almost always; Area is a plain box average
and is the gentlest on noise; Bicubic sits between them.
Resizing goes through Pillow with reducing_gap=3.0. That parameter lets Pillow do a fast
integer reduce() first and finish with the real filter, and Pillow documents >= 3.0 as
visually indistinguishable from a single full-quality pass. Worth knowing: it only engages
when the image is being shrunk by at least 2 x reducing_gap, i.e. 6x or more, so at the
ratios here (4.0 at the very most, for 4x → 1x) it never actually triggers — every
resize this extension does is already a single full-quality pass. It is passed anyway so the
code stays correct if a decoder with a larger native scale turns up.
| Control | Default | Description |
|---|---|---|
| Steps | 4 |
Published PiD checkpoints are distilled for 4 steps. |
| Shift | 3.5 |
Timestep shift for the PiD pass. |
| Sampler | LCM |
|
| Schedule type | Simple |
|
| Decoder scale | Auto |
What the checkpoint renders at. Auto reads it off the filename (1024_to_4096 → 4x) and falls back to 4x. |
| Save to disk | on | Write the PiD image out, not just show it. |
- Both images are kept. The ordinary decode is still the main output; the PiD render is
added as an extra image with its own infotext, and saved with a
-pidsuffix. - Hires. Fix and inpainting are not supported and are refused with a message in the console rather than failing halfway through.
- Mismatched decoders are caught before anything runs. The extension reads the PiD checkpoint's safetensors header (a few kilobytes — no weights are loaded) and compares the latent channel count it conditions on against what the loaded checkpoint produces. A Flux.2 decoder on a Flux.1 model is refused outright. Flux.1 and Wan 2.1 latents are both 16-channel and so cannot be told apart that way; there the filename is used for a warning instead, and the pass still runs.
- The checkpoint dropdown lists everything, with PiD-looking names first, so a renamed
or hand-merged decoder is still selectable.
pidis matched as a whole word —rapidandcupiddon't count. - The generation's
Size:in the infotext is the generation size, not the size of the PiD image.pid_scalerecords the actual factor. - Nothing in
sd-webui-forge-classicis modified.
Forge Neo already supports PiD as a first-class model type — it is a pixel-space DiT, so
its "latent" is the image itself and its conditioning is the other model's latent
(backend/diffusion_engine/pid.py, backend/nn/pixeldit/pid.py). Core wires that up
whenever the loaded checkpoint's repo is nvidia/PiD.
This extension hooks post_sample, which fires after sampling but before the VAE decode:
- it decodes the batch itself, so it can build a color-correction reference from the original 1x image, and hands the decoded result back so the main pipeline doesn't decode twice;
- for each image it runs a nested
img2imgwith the PiD checkpoint overridden in, passing the sampled latent straight through asinit_latentand asking for a render at 4x the generation size; - it downscales the render to the requested output scale, applies color correction to that (so the histogram matches the image that actually gets saved), and appends it to the gallery.