| import os |
| import time |
| import gradio as gr |
| import torch |
| from PIL import Image |
|
|
| from huggingface_hub import snapshot_download |
| from diffusers import StableVideoDiffusionPipeline |
| import imageio.v2 as imageio |
|
|
| REPO_ID = "stabilityai/stable-video-diffusion-img2vid-xt" |
|
|
| |
| ROOT = "/data" if os.path.isdir("/data") else "/home/user" |
| MODEL_DIR = os.path.join(ROOT, "models", "svd-xt") |
| OUT_DIR = "/tmp/outputs" |
| os.makedirs(OUT_DIR, exist_ok=True) |
|
|
| pipe = None |
|
|
|
|
| def ensure_model(): |
| os.makedirs(MODEL_DIR, exist_ok=True) |
|
|
| |
| if any(os.path.exists(os.path.join(MODEL_DIR, f)) for f in ["model_index.json", "config.json"]): |
| print("✅ Model already present in:", MODEL_DIR) |
| return |
|
|
| print("⬇️ Downloading model to:", MODEL_DIR) |
| snapshot_download( |
| repo_id=REPO_ID, |
| local_dir=MODEL_DIR, |
| local_dir_use_symlinks=False, |
| resume_download=True, |
| max_workers=4, |
| token=os.environ.get("HF_TOKEN"), |
| ) |
| print("✅ Download finished. Top files:", os.listdir(MODEL_DIR)[:30]) |
|
|
|
|
| def get_pipe(): |
| global pipe |
| if pipe is not None: |
| return pipe |
|
|
| ensure_model() |
|
|
| dtype = torch.float16 if torch.cuda.is_available() else torch.float32 |
| pipe = StableVideoDiffusionPipeline.from_pretrained( |
| MODEL_DIR, |
| torch_dtype=dtype, |
| local_files_only=True, |
| ) |
|
|
| if torch.cuda.is_available(): |
| pipe = pipe.to("cuda") |
| try: |
| pipe.enable_xformers_memory_efficient_attention() |
| except Exception: |
| pass |
|
|
| return pipe |
|
|
|
|
| def run(image: Image.Image, motion: int, fps: int, frames: int, steps: int, seed: int): |
| if image is None: |
| raise gr.Error("Upload an image first.") |
|
|
| if seed < 0: |
| seed = int(time.time()) % 10_000_000 |
|
|
| generator = torch.Generator(device="cuda" if torch.cuda.is_available() else "cpu").manual_seed(int(seed)) |
|
|
| pipe = get_pipe() |
| img = image.convert("RGB") |
|
|
| out = pipe( |
| image=img, |
| num_frames=int(frames), |
| fps=int(fps), |
| motion_bucket_id=int(motion), |
| num_inference_steps=int(steps), |
| generator=generator, |
| ) |
|
|
| video_frames = out.frames[0] |
| out_path = os.path.join(OUT_DIR, f"svd_{int(time.time())}.mp4") |
| imageio.mimsave(out_path, video_frames, fps=int(fps)) |
| return out_path |
|
|
|
|
| with gr.Blocks(title="SVD img2vid XT") as demo: |
| gr.Markdown("## Stable Video Diffusion (img2vid-xt)\nModel downloads once at startup (resume enabled).") |
|
|
| with gr.Row(): |
| inp = gr.Image(type="pil", label="Input image") |
| out = gr.Video(label="Output video") |
|
|
| with gr.Accordion("Settings", open=False): |
| motion = gr.Slider(1, 255, value=100, step=1, label="motion_bucket_id (lower = calmer)") |
| fps = gr.Slider(6, 30, value=12, step=1, label="fps") |
| frames = gr.Slider(8, 30, value=14, step=1, label="num_frames") |
| steps = gr.Slider(10, 50, value=25, step=1, label="steps") |
| seed = gr.Number(value=-1, precision=0, label="seed (-1 random)") |
|
|
| btn = gr.Button("Generate", variant="primary") |
| btn.click(run, [inp, motion, fps, frames, steps, seed], out) |
|
|
| demo.queue().launch() |