| import spaces |
|
|
| import random |
| import time |
|
|
| import torch |
| import gradio as gr |
| from inference import ShellDInference |
|
|
| MODEL_ID = "FlameF0X/ShellD" |
| TEXT_ENCODER = "sentence-transformers/all-MiniLM-L6-v2" |
|
|
| |
| |
| |
| |
| pipe = ShellDInference(MODEL_ID, device="cpu", text_encoder_path=TEXT_ENCODER) |
| pipe.model.eval() |
| print("ShellD model loaded.") |
|
|
|
|
| def _estimate_duration(prompt, seed, randomize_seed, num_steps, cfg_scale, *args, **kwargs): |
| """GPU-seconds reservation for ZeroGPU. Measured on ZeroGPU: ~0.01 s/step |
| for this 67M-param DiT (2 CFG forward passes per step on 64 patches). |
| Add ~5 s for cold-start weight streaming, floor at 10 s, ceiling at 60 s.""" |
| return min(60, max(10, int(num_steps * 0.015 + 5))) |
|
|
|
|
| @spaces.GPU(duration=_estimate_duration) |
| def generate( |
| prompt: str, |
| seed: int = 42, |
| randomize_seed: bool = False, |
| num_steps: int = 250, |
| cfg_scale: float = 3.0, |
| progress=gr.Progress(track_tqdm=False), |
| ): |
| """Generate a 256×256 image from a text prompt using ShellD. |
| |
| Args: |
| prompt: Text description of the image to generate. |
| seed: RNG seed for reproducibility (ignored if randomize_seed is True). |
| randomize_seed: If True, pick a random seed and write it back. |
| num_steps: Number of DDPM denoising steps (more = higher quality, slower). |
| cfg_scale: Classifier-free guidance scale (higher = more prompt adherence). |
| |
| Returns: |
| (image, seed) — a 256×256 PIL Image and the seed used. |
| """ |
| if prompt is None or str(prompt).strip() == "": |
| prompt = "a serene lake surrounded by mountains" |
| prompt = str(prompt).strip() |
|
|
| if randomize_seed: |
| seed = random.randint(0, 2**31 - 1) |
| seed = int(seed) |
|
|
| t0 = time.perf_counter() |
| img = pipe.generate( |
| prompt=prompt, |
| num_steps=int(num_steps), |
| cfg_scale=float(cfg_scale), |
| seed=seed, |
| ) |
| elapsed = time.perf_counter() - t0 |
| print(f"ShellD generated '{prompt[:40]}' in {elapsed:.2f}s ({num_steps} steps, seed={seed})") |
| return img, seed |
|
|
|
|
| CSS = """ |
| #col-container { max-width: 900px; margin: 0 auto; } |
| .dark .gradio-container { color: var(--body-text-color); } |
| """ |
|
|
| with gr.Blocks(css=CSS) as demo: |
| gr.Markdown( |
| """ |
| # 🐚 ShellD — Shell Diffusion Text-to-Image |
| A lightweight 67M-parameter Diffusion Transformer (DiT) that generates 256×256 images from text prompts. |
| [Model card](https://huggingface.co/FlameF0X/ShellD) |
| """ |
| ) |
|
|
| with gr.Column(elem_id="col-container"): |
| with gr.Row(): |
| prompt = gr.Textbox( |
| label="Prompt", |
| show_label=False, |
| placeholder="Describe the image you want to generate…", |
| container=False, |
| scale=4, |
| ) |
| run = gr.Button("Generate", variant="primary", scale=1) |
|
|
| output = gr.Image(label="Generated image", height=320, show_label=True) |
|
|
| with gr.Accordion("Advanced settings", open=False): |
| num_steps = gr.Slider( |
| label="Steps", minimum=10, maximum=1000, step=10, value=250 |
| ) |
| cfg_scale = gr.Slider( |
| label="CFG scale", minimum=1.0, maximum=10.0, step=0.5, value=3.0 |
| ) |
| randomize_seed = gr.Checkbox(label="Randomize seed", value=True) |
| seed = gr.Number(label="Seed", value=0, precision=0) |
|
|
| gr.Examples( |
| examples=[ |
| ["a serene lake surrounded by mountains"], |
| ["a futuristic city at night with neon lights"], |
| ["a cute cat sitting on a windowsill"], |
| ["a tropical beach with palm trees at sunset"], |
| ], |
| inputs=[prompt], |
| outputs=[output, seed], |
| fn=generate, |
| cache_examples=True, |
| cache_mode="lazy", |
| ) |
|
|
| run.click( |
| generate, |
| inputs=[prompt, seed, randomize_seed, num_steps, cfg_scale], |
| outputs=[output, seed], |
| api_name="generate", |
| ) |
| prompt.submit( |
| generate, |
| inputs=[prompt, seed, randomize_seed, num_steps, cfg_scale], |
| outputs=[output, seed], |
| api_name="generate_submit", |
| ) |
|
|
| if __name__ == "__main__": |
| demo.launch(mcp_server=True, theme=gr.themes.Citrus()) |