N / clean.py
q6's picture
Preload GPU models at startup and reuse them for inference
5f549a0
Raw History Blame Contribute Delete
2.32 kB
import numpy as np
import torch
from pathlib import Path
from huggingface_hub.constants import HF_HUB_CACHE
from PIL import Image
from spandrel import ImageModelDescriptor, ModelLoader
CLEAN_BUCKET = "HyperHail/CB"
CLEAN_FILE = "upscale_models/3_1x-Archivist_Soft.pth"
TILE_SIZE = 256
TILE_PAD = 32
PIXEL_MAX = 255
def download_clean():
path = Path(HF_HUB_CACHE) / f"buckets--{CLEAN_BUCKET.replace('/', '--')}" / CLEAN_FILE
if not path.is_file():
path.parent.mkdir(parents=True, exist_ok=True)
url = f"https://huggingface.co/buckets/{CLEAN_BUCKET}/resolve/{CLEAN_FILE}"
torch.hub.download_url_to_file(url, str(path), progress=False)
return str(path)
def load_clean(device):
model = ModelLoader().load_from_file(download_clean())
if not isinstance(model, ImageModelDescriptor) or model.scale != 1:
raise ValueError("Clean after requires a 1x image model")
if model.input_channels != 3 or model.output_channels != 3:
raise ValueError("Clean after requires an RGB model")
return model.to(device).eval()
@torch.inference_mode()
def clean_image(model, input_path, output_path):
device = next(model.model.parameters()).device
with Image.open(input_path) as source:
image = source.convert("RGB")
alpha = source.convert("RGBA").getchannel("A") if "A" in source.getbands() or "transparency" in source.info else None
width, height = image.size
result = Image.new("RGB", image.size)
for y in range(0, height, TILE_SIZE):
for x in range(0, width, TILE_SIZE):
right, bottom = min(x + TILE_SIZE, width), min(y + TILE_SIZE, height)
left, top = max(0, x - TILE_PAD), max(0, y - TILE_PAD)
box = (left, top, min(width, right + TILE_PAD), min(height, bottom + TILE_PAD))
pixels = np.array(image.crop(box), dtype=np.float32) / PIXEL_MAX
tile = torch.from_numpy(pixels).permute(2, 0, 1).unsqueeze(0).to(device)
output = model(tile)[0].clamp(0, 1).mul(PIXEL_MAX).round().byte()
patch = Image.fromarray(output.permute(1, 2, 0).cpu().numpy())
result.paste(patch.crop((x - left, y - top, right - left, bottom - top)), (x, y))
if alpha is not None:
result.putalpha(alpha)
result.save(output_path, format="PNG")