Spaces:
Sleeping
Sleeping
| """ | |
| CCTV frame restoration demo — Stable Diffusion x4 Upscaler. | |
| Self-contained: this is the only Python file the Space needs. | |
| """ | |
| import torch | |
| import spaces | |
| from diffusers import StableDiffusionUpscalePipeline | |
| import gradio as gr | |
| MODEL_ID = "stabilityai/stable-diffusion-x4-upscaler" | |
| MAX_INPUT_SIDE = 128 # model was trained on small inputs; larger is slow and doesn't help | |
| device = "cuda" if torch.cuda.is_available() else "cpu" | |
| print("Loading model, this happens once at startup...") | |
| pipe = StableDiffusionUpscalePipeline.from_pretrained(MODEL_ID, torch_dtype=torch.float32) | |
| pipe.to(device) | |
| print(f"Is CUDA available: {torch.cuda.is_available()}") | |
| print(f"CUDA device: {torch.cuda.get_device_name(torch.cuda.current_device())}") | |
| def restore(image, prompt, steps): | |
| """Callback for the Gradio UI. `image` arrives as a PIL Image.""" | |
| if image is None: | |
| return None | |
| image = image.convert("RGB") | |
| w, h = image.size | |
| scale = MAX_INPUT_SIDE / max(w, h) | |
| if scale < 1.0: | |
| image = image.resize((int(w * scale), int(h * scale))) | |
| result = pipe( | |
| prompt=prompt, | |
| negative_prompt="blurry, noisy, low quality, pixelated, artifacts", | |
| image=image, | |
| num_inference_steps=int(steps), | |
| guidance_scale=7.0, | |
| ).images[0] | |
| return result | |
| demo = gr.Interface( | |
| fn=restore, | |
| inputs=[ | |
| gr.Image(type="pil", label="Low-res / noisy CCTV frame"), | |
| gr.Textbox( | |
| value="a clear, sharp, well-lit security camera photograph, high detail", | |
| label="Restoration prompt", | |
| ), | |
| gr.Slider(10, 50, value=20, step=5, label="Diffusion steps (higher = slower, sharper)"), | |
| ], | |
| outputs=gr.Image(type="pil", label="Restored (4x upscaled)"), | |
| title="CCTV Frame Restoration with Stable Diffusion x4 Upscaler", | |
| description=( | |
| "Diffusion-based super-resolution for low-quality surveillance frames. " | |
| "The model was trained on 128x128 crops, so larger inputs are downscaled first. " | |
| ), | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |