Spaces:
Sleeping
Sleeping
Download app.py from DjK66-reddy/DiffusionMotion: direct link, hf CLI and curl.
- Browser
- Download file 17.7 kB
-
https://huggingface.co/spaces/DjK66-reddy/DiffusionMotion/resolve/main/app.py
- Command line
-
hf download hf://spaces/DjK66-reddy/DiffusionMotion/app.py
-
curl -L -o app.py https://huggingface.co/spaces/DjK66-reddy/DiffusionMotion/resolve/main/app.py
17.7 kB
| """ | |
| Gradio Web Interface for DiffusionMotion | |
| AI-Assisted Animation Generator | |
| """ | |
| try: | |
| import spaces # ZeroGPU support on HF Spaces β must be imported before torch | |
| HF_SPACES = True | |
| except ImportError: | |
| spaces = None | |
| HF_SPACES = False | |
| import gradio as gr | |
| import torch | |
| from pathlib import Path | |
| import sys | |
| # Add src to path | |
| sys.path.insert(0, str(Path(__file__).parent)) | |
| from src.frame_generator import FrameGenerator | |
| from src.controlnet_pipeline import ControlNetFrameGenerator | |
| from src.interpolation import LatentInterpolator | |
| from src.animation_builder import AnimationBuilder | |
| MODE_BASIC = "Basic Img2Img" | |
| MODE_CONTROLNET = "ControlNet" | |
| MODE_INTERPOLATION = "Latent Interpolation" | |
| class AnimationApp: | |
| """ | |
| Main application class for the Gradio interface. | |
| Supports three generation modes: Basic, ControlNet, and Latent Interpolation. | |
| """ | |
| def __init__(self): | |
| self.frame_generator = None | |
| self.controlnet_generator = None | |
| self.interpolator = None | |
| self.animation_builder = AnimationBuilder(output_dir="outputs") | |
| self.device = ( | |
| "cuda" if torch.cuda.is_available() | |
| else "mps" if torch.backends.mps.is_available() | |
| else "cpu" | |
| ) | |
| # ------------------------------------------------------------------ | |
| # Lazy model loaders | |
| # ------------------------------------------------------------------ | |
| def _load_frame_generator(self): | |
| if self.frame_generator is None: | |
| print("Loading Stable Diffusion (txt2img / img2img)...") | |
| self.frame_generator = FrameGenerator(device=self.device) | |
| return self.frame_generator | |
| def _load_controlnet(self): | |
| if self.controlnet_generator is None: | |
| print("Loading ControlNet pipeline...") | |
| self.controlnet_generator = ControlNetFrameGenerator(device=self.device) | |
| return self.controlnet_generator | |
| def _load_interpolator(self): | |
| if self.interpolator is None: | |
| print("Loading VAE for latent interpolation...") | |
| self.interpolator = LatentInterpolator(device=self.device) | |
| return self.interpolator | |
| # ------------------------------------------------------------------ | |
| # Generation entry point | |
| # ------------------------------------------------------------------ | |
| def generate_animation( | |
| self, | |
| prompt: str, | |
| mode: str = MODE_BASIC, | |
| num_frames: int = 16, | |
| guidance_scale: float = 7.5, | |
| fps: int = 8, | |
| seed: int = -1, | |
| # Basic mode | |
| strength: float = 0.5, | |
| # ControlNet mode | |
| controlnet_scale: float = 0.8, | |
| edge_variation: float = 0.3, | |
| # Interpolation mode | |
| num_keyframes: int = 3, | |
| frames_between: int = 4, | |
| interpolation_type: str = "slerp", | |
| progress=gr.Progress() | |
| ): | |
| try: | |
| if not prompt or not prompt.strip(): | |
| return None, "Please enter a prompt." | |
| if num_frames < 2: | |
| return None, "Need at least 2 frames." | |
| actual_seed = seed if seed >= 0 else torch.randint(0, 2**32, (1,)).item() | |
| def cb(current, total, msg): | |
| progress(current / total, desc=msg) | |
| # ββ Basic Img2Img βββββββββββββββββββββββββββββββββββββββββ | |
| if mode == MODE_BASIC: | |
| progress(0, desc="Loading Stable Diffusion...") | |
| gen = self._load_frame_generator() | |
| progress(0.1, desc="Generating frames...") | |
| frames = gen.generate_frame_sequence( | |
| base_prompt=prompt, | |
| num_frames=num_frames, | |
| strength=strength, | |
| guidance_scale=guidance_scale, | |
| seed=actual_seed, | |
| progress_callback=cb | |
| ) | |
| mode_detail = f"Strength: {strength}" | |
| # ββ ControlNet ββββββββββββββββββββββββββββββββββββββββββββ | |
| elif mode == MODE_CONTROLNET: | |
| progress(0, desc="Loading Stable Diffusion + ControlNet...") | |
| base_gen = self._load_frame_generator() | |
| cn_gen = self._load_controlnet() | |
| progress(0.05, desc="Generating initial frame...") | |
| initial_frame = base_gen.generate_first_frame( | |
| prompt=prompt, | |
| guidance_scale=guidance_scale, | |
| seed=actual_seed | |
| ) | |
| progress(0.1, desc="Generating ControlNet sequence...") | |
| frames = cn_gen.generate_consistent_sequence( | |
| initial_frame=initial_frame, | |
| prompt=prompt, | |
| num_frames=num_frames, | |
| guidance_scale=guidance_scale, | |
| controlnet_scale=controlnet_scale, | |
| edge_variation=edge_variation, | |
| seed=actual_seed, | |
| progress_callback=cb | |
| ) | |
| mode_detail = f"ControlNet scale: {controlnet_scale} | Edge variation: {edge_variation}" | |
| # ββ Latent Interpolation ββββββββββββββββββββββββββββββββββ | |
| elif mode == MODE_INTERPOLATION: | |
| progress(0, desc="Loading Stable Diffusion + VAE...") | |
| base_gen = self._load_frame_generator() | |
| interp = self._load_interpolator() | |
| progress(0.05, desc=f"Generating {num_keyframes} keyframes...") | |
| keyframes = [] | |
| for k in range(num_keyframes): | |
| cb(k, num_keyframes, f"Keyframe {k + 1}/{num_keyframes}...") | |
| kf = base_gen.generate_first_frame( | |
| prompt=prompt, | |
| guidance_scale=guidance_scale, | |
| seed=actual_seed + k * 100 | |
| ) | |
| keyframes.append(kf) | |
| progress(0.6, desc="Interpolating in latent space...") | |
| frames = interp.generate_interpolated_sequence( | |
| keyframes=keyframes, | |
| frames_between=frames_between, | |
| interpolation_type=interpolation_type | |
| ) | |
| mode_detail = ( | |
| f"Keyframes: {num_keyframes} | " | |
| f"Frames between: {frames_between} | " | |
| f"Method: {interpolation_type}" | |
| ) | |
| else: | |
| return None, f"Unknown mode: {mode}" | |
| # ββ Export ββββββββββββββββββββββββββββββββββββββββββββββββ | |
| progress(0.95, desc="Exporting GIF...") | |
| output_filename = f"animation_{mode.replace(' ', '_')}_{actual_seed}.gif" | |
| gif_path = self.animation_builder.create_gif( | |
| frames=frames, | |
| output_filename=output_filename, | |
| fps=fps | |
| ) | |
| status_msg = f""" | |
| **Animation Generated!** | |
| **Mode:** {mode} | |
| **Frames:** {len(frames)} @ {fps} FPS | |
| **Seed:** {actual_seed} | |
| **Device:** {self.device.upper()} | |
| **Settings:** {mode_detail} | |
| Saved to: `{gif_path}` | |
| """ | |
| return str(gif_path), status_msg | |
| except Exception as e: | |
| import traceback | |
| traceback.print_exc() | |
| return None, f"**Error:** {str(e)}\n\nCheck the console for details." | |
| # Wrap generate_animation with @spaces.GPU on HF ZeroGPU, no-op locally | |
| if HF_SPACES: | |
| AnimationApp.generate_animation = spaces.GPU(duration=60)( | |
| AnimationApp.generate_animation | |
| ) | |
| # ------------------------------------------------------------------ | |
| # UI helpers | |
| # ------------------------------------------------------------------ | |
| def _toggle_mode_panels(mode): | |
| """Show/hide mode-specific setting panels based on selected mode.""" | |
| return ( | |
| gr.update(visible=(mode == MODE_BASIC)), | |
| gr.update(visible=(mode == MODE_CONTROLNET)), | |
| gr.update(visible=(mode == MODE_INTERPOLATION)), | |
| ) | |
| # ------------------------------------------------------------------ | |
| # Interface builder | |
| # ------------------------------------------------------------------ | |
| def create_interface(): | |
| app = AnimationApp() | |
| css = """ | |
| .output-image { max-height: 500px; } | |
| .mode-panel { border: 1px solid #e0e0e0; border-radius: 8px; padding: 8px; } | |
| """ | |
| with gr.Blocks(css=css, title="DiffusionMotion - AI Animation") as demo: | |
| gr.Markdown( | |
| """ | |
| # DiffusionMotion β AI-Assisted Animation | |
| Generate smooth animations from text prompts using Stable Diffusion. | |
| | Mode | How it works | Best for | | |
| |------|-------------|----------| | |
| | **Basic Img2Img** | Chains img2img from the previous frame | Fast, general use | | |
| | **ControlNet** | Canny-edge conditioning for structural consistency | Objects that should hold their shape | | |
| | **Latent Interpolation** | SLERP/linear between VAE keyframes | Smooth morphs, abstract motion | | |
| """ | |
| ) | |
| with gr.Row(): | |
| # ββ Left column: inputs βββββββββββββββββββββββββββββββββββ | |
| with gr.Column(scale=1): | |
| prompt_input = gr.Textbox( | |
| label="Animation Prompt", | |
| placeholder="A red ball bouncing up and down on a white background", | |
| lines=3 | |
| ) | |
| mode_radio = gr.Radio( | |
| choices=[MODE_BASIC, MODE_CONTROLNET, MODE_INTERPOLATION], | |
| value=MODE_BASIC, | |
| label="Generation Mode" | |
| ) | |
| # ββ Common settings βββββββββββββββββββββββββββββββββββ | |
| with gr.Accordion("Common Settings", open=True): | |
| num_frames_slider = gr.Slider( | |
| minimum=2, maximum=16, value=4, step=1, | |
| label="Number of Frames", | |
| info="Keep low (4-6) on ZeroGPU to stay within time limits" | |
| ) | |
| guidance_slider = gr.Slider( | |
| minimum=1.0, maximum=20.0, value=7.5, step=0.5, | |
| label="Guidance Scale", | |
| info="How closely to follow the prompt" | |
| ) | |
| fps_slider = gr.Slider( | |
| minimum=4, maximum=30, value=8, step=1, | |
| label="FPS", | |
| info="Playback speed" | |
| ) | |
| seed_input = gr.Number( | |
| label="Seed", value=-1, precision=0, | |
| info="-1 for random" | |
| ) | |
| # ββ Basic mode settings βββββββββββββββββββββββββββββββ | |
| with gr.Group(visible=True, elem_classes=["mode-panel"]) as basic_panel: | |
| gr.Markdown("**Basic Img2Img Settings**") | |
| strength_slider = gr.Slider( | |
| minimum=0.1, maximum=1.0, value=0.5, step=0.05, | |
| label="Frame Variation Strength", | |
| info="Higher = more change between frames" | |
| ) | |
| # ββ ControlNet mode settings ββββββββββββββββββββββββββ | |
| with gr.Group(visible=False, elem_classes=["mode-panel"]) as controlnet_panel: | |
| gr.Markdown("**ControlNet Settings**") | |
| controlnet_scale_slider = gr.Slider( | |
| minimum=0.1, maximum=1.5, value=0.8, step=0.05, | |
| label="ControlNet Conditioning Scale", | |
| info="Higher = stronger structural guidance" | |
| ) | |
| edge_variation_slider = gr.Slider( | |
| minimum=0.0, maximum=1.0, value=0.3, step=0.05, | |
| label="Edge Variation (Motion Amount)", | |
| info="0 = static structure, higher = more edge movement" | |
| ) | |
| # ββ Interpolation mode settings βββββββββββββββββββββββ | |
| with gr.Group(visible=False, elem_classes=["mode-panel"]) as interp_panel: | |
| gr.Markdown("**Latent Interpolation Settings**") | |
| num_keyframes_slider = gr.Slider( | |
| minimum=2, maximum=8, value=3, step=1, | |
| label="Number of Keyframes", | |
| info="Keyframes generated at different seeds, then interpolated" | |
| ) | |
| frames_between_slider = gr.Slider( | |
| minimum=2, maximum=16, value=4, step=1, | |
| label="Frames Between Keyframes", | |
| info="Interpolated frames inserted between each keyframe pair" | |
| ) | |
| interp_type_radio = gr.Radio( | |
| choices=["slerp", "linear"], | |
| value="slerp", | |
| label="Interpolation Method", | |
| info="SLERP is smoother for latent spaces" | |
| ) | |
| generate_btn = gr.Button( | |
| "Generate Animation", variant="primary", size="lg" | |
| ) | |
| # ββ Right column: output ββββββββββββββββββββββββββββββββββ | |
| with gr.Column(scale=1): | |
| output_image = gr.Image( | |
| label="Generated Animation", | |
| type="filepath", | |
| elem_classes=["output-image"] | |
| ) | |
| status_output = gr.Markdown( | |
| value="Ready. Enter a prompt and click Generate." | |
| ) | |
| # ββ Examples ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| gr.Examples( | |
| examples=[ | |
| ["A red ball bouncing up and down", MODE_BASIC, 16, 7.5, 8, 42, 0.4, 0.8, 0.3, 3, 4, "slerp"], | |
| ["A blue cube slowly rotating", MODE_BASIC, 16, 7.5, 8, 123, 0.5, 0.8, 0.3, 3, 4, "slerp"], | |
| ["A fire flickering in the dark", MODE_CONTROLNET, 16, 8.0, 8, 42, 0.5, 0.7, 0.4, 3, 4, "slerp"], | |
| ["A glowing orb pulsing", MODE_CONTROLNET, 12, 7.5, 10, 99, 0.5, 0.8, 0.2, 3, 4, "slerp"], | |
| ["Abstract shapes morphing", MODE_INTERPOLATION, 16, 7.5, 8, 42, 0.5, 0.8, 0.3, 3, 6, "slerp"], | |
| ["A face slowly smiling", MODE_INTERPOLATION, 16, 8.0, 8, 200, 0.5, 0.8, 0.3, 4, 4, "slerp"], | |
| ], | |
| inputs=[ | |
| prompt_input, mode_radio, | |
| num_frames_slider, guidance_slider, fps_slider, seed_input, | |
| strength_slider, | |
| controlnet_scale_slider, edge_variation_slider, | |
| num_keyframes_slider, frames_between_slider, interp_type_radio, | |
| ], | |
| label="Example Prompts" | |
| ) | |
| # ββ Footer βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| gr.Markdown( | |
| """ | |
| --- | |
| **System Info:** Device: {} | Model: Stable Diffusion 2.1 Base | |
| First generation is slower (model download + load). Subsequent runs are fast. | |
| """.format( | |
| "CUDA GPU" if torch.cuda.is_available() | |
| else "Apple MPS" if torch.backends.mps.is_available() | |
| else "CPU" | |
| ) | |
| ) | |
| # ββ Event wiring βββββββββββββββββββββββββββββββββββββββββββββββ | |
| mode_radio.change( | |
| fn=_toggle_mode_panels, | |
| inputs=[mode_radio], | |
| outputs=[basic_panel, controlnet_panel, interp_panel] | |
| ) | |
| generate_btn.click( | |
| fn=app.generate_animation, | |
| inputs=[ | |
| prompt_input, mode_radio, | |
| num_frames_slider, guidance_slider, fps_slider, seed_input, | |
| strength_slider, | |
| controlnet_scale_slider, edge_variation_slider, | |
| num_keyframes_slider, frames_between_slider, interp_type_radio, | |
| ], | |
| outputs=[output_image, status_output] | |
| ) | |
| return demo | |
| if __name__ == "__main__": | |
| print("=" * 60) | |
| print("DiffusionMotion - AI-Assisted Animation") | |
| print("=" * 60) | |
| device_label = ( | |
| "CUDA GPU" if torch.cuda.is_available() | |
| else "Apple MPS" if torch.backends.mps.is_available() | |
| else "CPU" | |
| ) | |
| print(f"Device: {device_label}") | |
| print("Starting Gradio interface...") | |
| print("=" * 60) | |
| demo = create_interface() | |
| demo.launch( | |
| share=False, | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| show_error=True, | |
| ssr_mode=False | |
| ) | |