Instructions to use ndtran0101/pisa-sr-diffusers with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use ndtran0101/pisa-sr-diffusers with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline from diffusers.utils import load_image # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("ndtran0101/pisa-sr-diffusers", dtype=torch.bfloat16, device_map="cuda") prompt = "Turn this cat into a dog" input_image = load_image("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/cat.png") image = pipe(image=input_image, prompt=prompt).images[0] - Notebooks
- Google Colab
- Kaggle
File size: 6,947 Bytes
735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 5761977 735d0c0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | ---
license: other
license_name: creativeml-openrail-m-plus-plus
license_link: https://huggingface.co/stabilityai/stable-diffusion-2-1-base/blob/main/LICENSE
base_model: stabilityai/stable-diffusion-2-1-base
tags:
- super-resolution
- image-to-image
- diffusers
- one-step-diffusion
- real-isr
library_name: diffusers
pipeline_tag: image-to-image
---
# PiSA-SR — diffusers-native repack
A format conversion of the official [PiSA-SR](https://github.com/csslc/PiSA-SR) (CVPR 2025)
weights into standard 🤗 diffusers format, so inference needs no vendored model code and
no PEFT/LoRA plumbing. **No new training** — all credit belongs to the original authors.
Upstream ships a raw PEFT state dict with six sub-adapters whose keys embed adapter names,
so `load_lora_weights()` doesn't work on it. Here the deltas are pre-fused into two
ready-to-load UNets.
## Contents
| Path | What | Size |
|---|---|---|
| `unet_full/` | pix + sem fused — default 1-step mode | 1.7 GB |
| `unet_pix/` | pix only — second model for adjustable mode | 1.7 GB |
| `lora/pisa_{pix,sem}.safetensors` | adapter-only deltas (+ config json) | 16 MB ea |
| `example_inference.py` | runnable default / adjustable inference | — |
VAE, text encoder and tokenizer are **unmodified SD 2.1-base** — load them from the base
model. The upstream checkpoint contains no VAE or text-encoder weights.
## Usage
Not a standard diffusion pipeline: no scheduler, no noise, no sampling loop. The LQ image
is upsampled first, encoded, and the UNet runs a single pass at `t=1` on an empty prompt.
Its output is a **residual subtracted** from the input latent.
`example_inference.py` in this repo is a runnable version of everything below.
```python
import torch, PIL.Image as Image
import torchvision.transforms.functional as TF
from torchvision import transforms
from diffusers import AutoencoderKL, UNet2DConditionModel
from transformers import AutoTokenizer, CLIPTextModel
BASE, REPO = "stabilityai/stable-diffusion-2-1-base", "ndtran0101/pisa-sr-diffusers"
dev, dt = "cuda", torch.float16
tok = AutoTokenizer.from_pretrained(BASE, subfolder="tokenizer")
te = CLIPTextModel.from_pretrained(BASE, subfolder="text_encoder").to(dev, dt).eval()
vae = AutoencoderKL.from_pretrained(BASE, subfolder="vae").to(dev, dt).eval()
unet = UNet2DConditionModel.from_pretrained(REPO, subfolder="unet_full").to(dev, dt).eval()
img = Image.open("lq.png").convert("RGB")
img = img.resize((img.width * 4, img.height * 4))
img = img.resize((img.width - img.width % 8, img.height - img.height % 8), Image.LANCZOS)
with torch.no_grad():
x = TF.to_tensor(img).unsqueeze(0).to(dev, dt) * 2 - 1
ids = tok("", max_length=tok.model_max_length, padding="max_length",
truncation=True, return_tensors="pt").input_ids.to(dev)
emb = te(ids)[0].to(dt)
t = torch.tensor([1], device=dev).long()
z = vae.encode(x).latent_dist.sample() * vae.config.scaling_factor
pred = unet(z, t, encoder_hidden_states=emb).sample
out = vae.decode((z - pred) / vae.config.scaling_factor).sample.clamp(-1, 1)
transforms.ToPILImage()((out * 0.5 + 0.5).clamp(0, 1)[0].float().cpu()).save("sr.png")
```
### Adjustable mode
Load `unet_pix` as well and combine the two predictions. Higher `lambda_pix` removes
noise and compression artifacts (too high → over-smoothed); higher `lambda_sem` adds
semantic detail (too high → artifacts). Both are 1.0 in the default mode above.
```python
unet_pix = UNet2DConditionModel.from_pretrained(REPO, subfolder="unet_pix").to(dev, dt).eval()
with torch.no_grad():
pred_sem = unet(z, t, encoder_hidden_states=emb).sample
pred_pix = unet_pix(z, t, encoder_hidden_states=emb).sample
pred = lambda_pix * pred_pix + lambda_sem * (pred_sem - pred_pix)
out = vae.decode((z - pred) / vae.config.scaling_factor).sample.clamp(-1, 1)
```
### Colour fix
The upstream pipeline applies an AdaIN colour transfer from the upsampled input to the
decoded output. It sits outside the network and skipping it shifts colour noticeably.
```python
def adain(target, source):
t = TF.to_tensor(target).unsqueeze(0)
s = TF.to_tensor(source).unsqueeze(0)
t_mean, t_std = t.mean([2, 3], keepdim=True), t.std([2, 3], keepdim=True)
s_mean, s_std = s.mean([2, 3], keepdim=True), s.std([2, 3], keepdim=True)
return transforms.ToPILImage()(
(((t - t_mean) / (t_std + 1e-5)) * s_std + s_mean).clamp(0, 1)[0])
```
### Also easy to get wrong
- **Pre-upsampling** — ×4 happens *before* the UNet, so compute scales with **output**
pixels, not input. A 128² input at ×4 costs the same as a 512² input at ×1.
- **Large outputs** — no tiling is shipped here. Use `vae.enable_tiling()` and tile the
image yourself above ~768².
- **Seeds** — inference is deterministic apart from `latent_dist.sample()`; use `.mode()`
for bit-reproducible output.
## Verification
Checked against the official implementation (RealSR crops, RTX 4090, fp16):
| | PSNR | mean abs err (0–255) |
|---|---|---|
| Reference vs itself (noise floor) | 63.1 dB | 0.03 |
| This repack vs reference | **56.7 dB** | 0.13 |
Visually and metrically indistinguishable. Upstream metrics reproduce to within 0.39%
relative across 27 values (StableSR protocol, ×4, 1 step):
| Dataset | PSNR(Y) | SSIM(Y) | LPIPS | DISTS | FID | MUSIQ | CLIPIQA |
|---|---|---|---|---|---|---|---|
| RealSR (100) | 25.50 | 0.7418 | 0.2672 | 0.2044 | 124.13 | 70.15 | 0.6697 |
| DRealSR (93) | 28.32 | 0.7804 | 0.2960 | 0.2169 | 130.45 | 66.11 | 0.6971 |
| DIV2K-Val (3000) | 23.87 | 0.6058 | 0.2823 | 0.1934 | 25.09 | 69.68 | 0.6928 |
PSNR/SSIM are **Y-channel (YCbCr)**; MANIQA (not shown) needs the **PIPAL** weights —
RGB PSNR or KonIQ MANIQA will not reproduce the paper.
Speed, single RTX 4090 fp16, 1 step: 512² 0.07 s / 5.0 GB · 1024² 0.47 s / 7.4 GB ·
2048² 17.9 s / 15.4 GB. Larger outputs work with a reduced VAE-decoder tile (8192² in
8.3 GB) — the limit is time, not VRAM.
## License
Two licenses apply:
- `unet_pix/`, `unet_full/` are derivatives of Stable Diffusion 2.1-base →
[CreativeML Open RAIL++-M](https://huggingface.co/stabilityai/stable-diffusion-2-1-base/blob/main/LICENSE),
including its use-based restrictions, which you must pass on downstream.
- `lora/*.safetensors` contain only PiSA-SR-trained parameters →
[Apache 2.0](https://github.com/csslc/PiSA-SR/blob/main/LICENSE).
## Citation
```bibtex
@inproceedings{sun2025pisasr,
title = {Pixel-level and Semantic-level Adjustable Super-resolution: A Dual-LoRA Approach},
author = {Sun, Lingchen and Wu, Rongyuan and Ma, Zhiyuan and Liu, Shuaizheng and Yi, Qiaosi and Zhang, Lei},
booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)},
year = {2025},
eprint = {2412.03017},
archivePrefix = {arXiv},
url = {https://arxiv.org/abs/2412.03017}
}
```
|