Spaces:
Sleeping
Sleeping
| """Gradio demo for the deepsky diffusion model β live 128px generation. | |
| Loads the trained EMA weights from the Hugging Face Hub and samples with DDIM. | |
| Runs on the Space's CPU by default (or GPU if the Space has one). | |
| """ | |
| import random | |
| import sys | |
| import gradio as gr | |
| import numpy as np | |
| import torch | |
| from huggingface_hub import hf_hub_download | |
| from PIL import Image | |
| from deepsky.config import ModelCfg | |
| from deepsky.diffusion.gaussian import GaussianDiffusion | |
| from deepsky.diffusion.samplers import ddim_sample | |
| from deepsky.diffusion.schedule import make_schedule | |
| from deepsky.models.unet import UNet | |
| REPO_ID = "jessholbrook/deepsky-128px" | |
| CKPT = "ckpt_0200000.pt" | |
| IMAGE_SIZE = 128 | |
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| # 128px architecture β matches configs/cloud-128px-full.yaml. | |
| cfg = ModelCfg( | |
| base_channels=128, | |
| channel_mults=(1, 2, 3, 4), | |
| num_res_blocks=2, | |
| attn_resolutions=(16, 8), | |
| time_emb_dim=512, | |
| ) | |
| # Lazy singletons β built on first generate() so the Gradio server binds its | |
| # port immediately and the Space reaches "Running" instead of timing out while | |
| # a 1.39 GB checkpoint downloads + loads on the free CPU tier. | |
| _model = None | |
| _diffusion = None | |
| def _load(): | |
| global _model, _diffusion | |
| if _model is not None: | |
| return _model, _diffusion | |
| print("Downloading checkpoint from the Hubβ¦", flush=True) | |
| ckpt_path = hf_hub_download(REPO_ID, CKPT) | |
| print(f"Loading weights from {ckpt_path} onto {device}β¦", flush=True) | |
| state = torch.load(ckpt_path, map_location=device) | |
| m = UNet(cfg, IMAGE_SIZE).to(device) | |
| m.load_state_dict(state["ema"]["shadow"]) # EMA weights = best quality | |
| m.eval() | |
| _model = m | |
| _diffusion = GaussianDiffusion(make_schedule("cosine", 1000).to(device)) | |
| print("Model ready.", flush=True) | |
| return _model, _diffusion | |
| def generate(steps, seed): | |
| model, diffusion = _load() | |
| if seed is None or int(seed) < 0: | |
| seed = random.randint(0, 2**31 - 1) | |
| torch.manual_seed(int(seed)) | |
| samples = ddim_sample( | |
| model, diffusion, (1, 3, IMAGE_SIZE, IMAGE_SIZE), | |
| device, steps=int(steps), progress=False, | |
| ) | |
| arr = ((samples[0].clamp(-1, 1) + 1) / 2 * 255).byte().permute(1, 2, 0).cpu().numpy() | |
| return Image.fromarray(np.ascontiguousarray(arr)), int(seed) | |
| with gr.Blocks(title="deepsky") as demo: | |
| gr.Markdown( | |
| "# π deepsky\n" | |
| "A **from-scratch** diffusion model generating deep-sky astronomical images β " | |
| "nebulae, galaxies, star clusters β trained on public ESA/Hubble, ESA/Webb, " | |
| "ESO, and NASA imagery. Every pixel is generated; nothing is retrieved.\n\n" | |
| "Model: [jessholbrook/deepsky-128px](https://huggingface.co/jessholbrook/deepsky-128px) Β· " | |
| "Code: [github.com/jessholbrook/deepsky](https://github.com/jessholbrook/deepsky)\n\n" | |
| "*The first generation also loads the model (one-time ~1β2 min). After that, " | |
| "each 128px image takes ~30β90s on the free CPU tier β lower the step count for speed.*" | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| steps = gr.Slider(20, 150, value=50, step=5, | |
| label="DDIM steps (more = sharper, slower)") | |
| seed = gr.Number(value=-1, label="Seed (β1 = random)", precision=0) | |
| btn = gr.Button("β¨ Generate", variant="primary") | |
| used = gr.Number(label="Seed used", interactive=False, precision=0) | |
| with gr.Column(scale=1): | |
| out = gr.Image(label="Generated 128px sample", type="pil", height=384) | |
| btn.click(generate, [steps, seed], [out, used]) | |
| demo.queue(max_size=8).launch() | |