multimodalart's picture
multimodalart HF Staff
Update app.py
8385548 verified
Raw
History Blame Contribute Delete
4.67 kB
import spaces # MUST come before torch / any CUDA-touching import
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"
# Load at module scope, .to("cuda") eagerly. ShellDInference auto-detects cuda
# (the spaces hijack patches torch.cuda.is_available() in the main process).
# Pass the text encoder explicitly — the config.json's text_encoder_name is a
# local path ("./all-MiniLM-L6-v2") which doesn't exist in the Space.
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())