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: 4,228 Bytes
5761977 | 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 | """Minimal PiSA-SR inference on the diffusers repack, in default or adjustable mode."""
import argparse, os
import torch
from PIL import Image
from torchvision import transforms
import torchvision.transforms.functional as TF
from diffusers import AutoencoderKL, UNet2DConditionModel
from transformers import AutoTokenizer, CLIPTextModel
BASE = "stabilityai/stable-diffusion-2-1-base"
REPO = "ndtran0101/pisa-sr-diffusers"
def prepare(img, upscale=4):
"""Upsample a low-quality image to the diffusion input size, aligned to a multiple of 8.
The upsample happens before the UNet, so cost scales with the output size.
"""
img = img.resize((img.width * upscale, img.height * upscale))
return img.resize((img.width - img.width % 8, img.height - img.height % 8), Image.LANCZOS)
def adain(target, source):
"""Match the target's per-channel mean and std to the source.
The upstream pipeline applies this colour transfer after decoding; skipping it
shifts colour noticeably.
"""
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)
out = ((t - t_mean) / (t_std + 1e-5)) * s_std + s_mean
return transforms.ToPILImage()(out.clamp(0, 1)[0])
@torch.no_grad()
def upscale(path, base=BASE, repo=REPO, lambda_pix=None, lambda_sem=None,
device="cuda", dtype=torch.float16, color_fix=True):
"""Upscale one image x4 and return it as a PIL image.
Runs one UNet pass at t=1 on an empty prompt and subtracts the predicted residual;
there is no scheduler and no noise. Passing both lambdas switches to the two-pass
adjustable mode, which additionally loads unet_pix.
path: low-quality input image
repo: HuggingFace id or local directory holding unet_full/ and unet_pix/
lambda_pix / lambda_sem: strength of the pixel and semantic branches
"""
adjustable = lambda_pix is not None and lambda_sem is not None
tok = AutoTokenizer.from_pretrained(base, subfolder="tokenizer")
te = CLIPTextModel.from_pretrained(base, subfolder="text_encoder").to(device, dtype).eval()
vae = AutoencoderKL.from_pretrained(base, subfolder="vae").to(device, dtype).eval()
unet = UNet2DConditionModel.from_pretrained(repo, subfolder="unet_full").to(device, dtype).eval()
src = Image.open(path).convert("RGB")
img = prepare(src)
x = TF.to_tensor(img).unsqueeze(0).to(device, dtype) * 2 - 1
ids = tok("", max_length=tok.model_max_length, padding="max_length",
truncation=True, return_tensors="pt").input_ids.to(device)
emb = te(ids)[0].to(dtype)
t = torch.tensor([1], device=device).long()
z = vae.encode(x).latent_dist.sample() * vae.config.scaling_factor
if adjustable:
unet_pix = UNet2DConditionModel.from_pretrained(
repo, subfolder="unet_pix").to(device, dtype).eval()
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)
else:
pred = unet(z, t, encoder_hidden_states=emb).sample
out = vae.decode((z - pred) / vae.config.scaling_factor).sample.clamp(-1, 1)
pil = transforms.ToPILImage()((out * 0.5 + 0.5).clamp(0, 1)[0].float().cpu())
return adain(pil, img) if color_fix else pil
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--input", required=True)
ap.add_argument("--output", default="sr.png")
ap.add_argument("--repo", default=REPO)
ap.add_argument("--base", default=BASE)
ap.add_argument("--lambda_pix", type=float, default=None)
ap.add_argument("--lambda_sem", type=float, default=None)
ap.add_argument("--no_color_fix", action="store_true")
a = ap.parse_args()
img = upscale(a.input, base=a.base, repo=a.repo,
lambda_pix=a.lambda_pix, lambda_sem=a.lambda_sem,
color_fix=not a.no_color_fix)
img.save(a.output)
print(f"{a.input} -> {a.output} {img.size[0]}x{img.size[1]}")
if __name__ == "__main__":
main()
|