MONK3YSPAC333 / app.py
spac333's picture
Update app.py
5993191 verified
"""
DynamiCrafter Image Animation
Anima immagini con interpolazione intelligente
"""
import gradio as gr
import torch
from diffusers import DiffusionPipeline
from diffusers.utils import export_to_video
from PIL import Image
import numpy as np
import tempfile
import os
print("πŸ”§ Initializing DynamiCrafter pipeline...")
# Configurazione
MODEL_ID = "Doubiiu/DynamiCrafter_512_Interp"
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
# Carica pipeline
print(f"πŸ“¦ Loading model from {MODEL_ID}...")
try:
pipe = DiffusionPipeline.from_pretrained(
MODEL_ID,
torch_dtype=torch.float16 if DEVICE == "cuda" else torch.float32,
custom_pipeline="dynamicrafter_interpolation"
)
pipe.to(DEVICE)
# Ottimizzazioni
if DEVICE == "cuda":
pipe.enable_model_cpu_offload()
pipe.enable_vae_slicing()
print("βœ… GPU optimizations enabled")
print(f"βœ… Pipeline loaded successfully on {DEVICE}")
except Exception as e:
print(f"❌ Error loading pipeline: {e}")
print("⚠️ Trying alternative loading method...")
# Fallback a loading standard
from diffusers import StableVideoDiffusionPipeline
pipe = StableVideoDiffusionPipeline.from_pretrained(
"stabilityai/stable-video-diffusion-img2vid",
torch_dtype=torch.float16 if DEVICE == "cuda" else torch.float32
)
pipe.to(DEVICE)
print("βœ… Loaded fallback model (SVD)")
def preprocess_image(image):
"""
Preprocessa l'immagine per DynamiCrafter
"""
if image is None:
raise ValueError("No image provided")
# Converti in PIL se necessario
if isinstance(image, np.ndarray):
image = Image.fromarray(image)
# Converti in RGB
if image.mode != "RGB":
image = image.convert("RGB")
# Ridimensiona a 512x512 (ottimale per DynamiCrafter)
original_size = image.size
image = image.resize((512, 512), Image.LANCZOS)
return image, original_size
def create_seamless_loop(frames, blend_frames=3):
"""
Crea un loop perfetto con blending
Args:
frames: Lista di frame
blend_frames: Numero di frame da usare per il blend
"""
if len(frames) < blend_frames * 2:
# Se troppo pochi frame, usa metodo semplice
return list(frames) + list(frames[-2:0:-1])
# Crea loop con blending avanzato
forward = list(frames)
reverse = list(frames[-2:0:-1])
# Blend tra ultimo frame forward e primo frame reverse
blended = []
for i in range(blend_frames):
alpha = i / blend_frames
frame1 = np.array(forward[-1 - i])
frame2 = np.array(reverse[i])
blended_frame = (frame1 * (1 - alpha) + frame2 * alpha).astype(np.uint8)
blended.append(Image.fromarray(blended_frame))
# Combina tutto
loop = forward[:-blend_frames] + blended + reverse[blend_frames:]
return loop
def animate_image(
image,
num_frames=16,
num_inference_steps=25,
motion_strength=127,
fps=8,
use_loop=True,
seed=-1,
progress=gr.Progress()
):
"""
Anima un'immagine con DynamiCrafter
Args:
image: Input image
num_frames: Numero di frame da generare (8-32)
num_inference_steps: Step di qualitΓ  (10-50)
motion_strength: IntensitΓ  movimento (1-255)
fps: Frame per secondo
use_loop: Crea loop perfetto
seed: Random seed (-1 per random)
progress: Progress tracker
"""
if image is None:
return None, "❌ Carica un'immagine prima!"
try:
progress(0, desc="πŸ–ΌοΈ Processing image...")
# Preprocessa immagine
processed_image, original_size = preprocess_image(image)
print(f"πŸ“Έ Image processed: {original_size} -> 512x512")
progress(0.2, desc="🎬 Generating animation...")
# Imposta seed se specificato
if seed != -1:
torch.manual_seed(seed)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(seed)
else:
seed = torch.randint(0, 1000000, (1,)).item()
print(f"🎲 Using seed: {seed}")
print(f"🎞️ Generating {num_frames} frames...")
# Genera frames
with torch.no_grad():
output = pipe(
processed_image,
num_frames=num_frames,
num_inference_steps=num_inference_steps,
decode_chunk_size=4,
motion_bucket_id=motion_strength,
fps=fps,
height=512,
width=512
)
frames = output.frames[0]
progress(0.7, desc="πŸ”„ Creating loop...")
# Crea loop se richiesto
if use_loop:
frames = create_seamless_loop(frames, blend_frames=3)
progress(0.9, desc="πŸ’Ύ Saving video...")
# Salva video
output_path = tempfile.NamedTemporaryFile(
suffix=".mp4",
delete=False
).name
export_to_video(frames, output_path, fps=fps)
progress(1.0, desc="βœ… Complete!")
# Info
total_frames = len(frames)
duration = total_frames / fps
info = f"""
βœ… **Animazione creata con successo!**
πŸ“Š **Dettagli:**
- Frame generati: {total_frames}
- FPS: {fps}
- Durata: ~{duration:.1f} secondi
- Loop: {'SΓ¬ βœ…' if use_loop else 'No ❌'}
- Motion strength: {motion_strength}
- Seed: {seed}
- Risoluzione: 512x512
- Device: {DEVICE.upper()}
- Inference steps: {num_inference_steps}
πŸ’‘ **Tip:** Salva il seed per ricreare animazioni simili!
"""
return output_path, info
except Exception as e:
error_msg = f"""
❌ **Errore durante la generazione:**
{str(e)}
πŸ’‘ **Possibili soluzioni:**
- Riduci il numero di frame
- Riduci gli inference steps
- Prova con un'altra immagine
- Verifica che l'immagine sia valida
"""
print(f"Error: {e}")
import traceback
traceback.print_exc()
return None, error_msg
# Esempi predefiniti (placeholder - aggiungi immagini vere)
EXAMPLES = [
["examples/landscape.jpg", 16, 25, 127, 8, True, 42],
["examples/portrait.jpg", 16, 20, 100, 8, True, 123],
["examples/abstract.jpg", 24, 25, 150, 8, True, 456],
]
# Interfaccia Gradio
with gr.Blocks(
title="🎬 DynamiCrafter Image Animator",
theme=gr.themes.Soft(
primary_hue="indigo",
secondary_hue="purple"
),
css="""
.gradio-container {max-width: 1200px !important}
.output-class {height: 500px !important}
"""
) as demo:
gr.Markdown("""
# 🎬 DynamiCrafter Image Animator
### Transform Static Images into Smooth Animations
Powered by **DynamiCrafter** - State-of-the-art image interpolation for fluid animations
πŸ’‘ **Best results with:**
- Clear, well-lit images
- Subjects with potential for natural movement
- Landscapes, portraits, or objects
""")
with gr.Row():
# Colonna sinistra - Input
with gr.Column(scale=1):
image_input = gr.Image(
label="πŸ“Έ Upload Image",
type="pil",
sources=["upload", "webcam", "clipboard"],
height=400
)
gr.Markdown("### βš™οΈ Animation Settings")
num_frames = gr.Slider(
minimum=8,
maximum=32,
value=16,
step=1,
label="🎞️ Number of Frames",
info="More frames = longer animation (but slower)"
)
motion_strength = gr.Slider(
minimum=1,
maximum=255,
value=127,
step=1,
label="πŸ’¨ Motion Strength",
info="Higher = more movement (127 is balanced)"
)
with gr.Accordion("🎨 Advanced Options", open=False):
num_inference_steps = gr.Slider(
minimum=10,
maximum=50,
value=25,
step=5,
label="🎨 Quality (Inference Steps)",
info="Higher = better quality but slower"
)
fps = gr.Slider(
minimum=4,
maximum=30,
value=8,
step=1,
label="πŸŽ₯ FPS (Frames per Second)",
info="Playback speed"
)
use_loop = gr.Checkbox(
value=True,
label="πŸ”„ Create Seamless Loop",
info="Enable for repeating animations"
)
seed = gr.Number(
value=-1,
label="🎲 Seed (-1 for random)",
info="Use same seed for consistent results",
precision=0
)
generate_btn = gr.Button(
"🎬 Animate Image",
variant="primary",
size="lg"
)
gr.Markdown("""
### πŸ“Š Performance Guide
**CPU (Free tier):**
- Frames: 8-12
- Steps: 15-20
- Time: ~2-3 min
**GPU T4 ($0.60/h):**
- Frames: 16-24
- Steps: 25-30
- Time: ~30-60 sec
""")
# Colonna destra - Output
with gr.Column(scale=1):
video_output = gr.Video(
label="🎬 Animated Result",
autoplay=True,
loop=True,
height=400
)
info_output = gr.Markdown(
value="πŸ‘† Upload an image and click 'Animate' to start!",
label="ℹ️ Generation Info"
)
gr.Markdown("""
### πŸ’‘ Tips for Best Results
- **Landscapes**: Natural scenes with clouds, water work great
- **Portraits**: Clear face shots animate smoothly
- **Objects**: Items with potential movement (flags, hair, etc.)
- **Lighting**: Well-lit images produce better results
- **Resolution**: 512x512 is optimal (auto-resized)
### 🎨 Motion Strength Guide
- **50-100**: Subtle movement (breathing, gentle sway)
- **100-150**: Medium movement (clouds, water)
- **150-200**: Strong movement (wind, dynamic action)
- **200+**: Extreme movement (experimental)
""")
# Event handler
generate_btn.click(
fn=animate_image,
inputs=[
image_input,
num_frames,
num_inference_steps,
motion_strength,
fps,
use_loop,
seed
],
outputs=[video_output, info_output],
)
# Footer
gr.Markdown("""
---
### πŸ”§ Technical Details
- **Model**: DynamiCrafter 512 Interpolation
- **Method**: Diffusion-based frame interpolation
- **Resolution**: 512x512 (optimized)
- **Device**: {device}
### πŸ“š Resources
- [DynamiCrafter Paper](https://arxiv.org/abs/2310.12190)
- [Model on HuggingFace](https://huggingface.co/Doubiiu/DynamiCrafter_512_Interp)
- [GitHub Repository](https://github.com/Doubiiu/DynamiCrafter)
---
**Made with ❀️ using HuggingFace Diffusers**
""".replace("{device}", DEVICE.upper()))
# Launch
if __name__ == "__main__":
demo.queue(max_size=10)
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False
)