DiffusionMotion / src /animation_builder.py
yeruvadeekshithreddy-ops
Initial commit: DiffusionMotion AI animation app
cf1a3cd
Raw History Blame Contribute Delete
7.34 kB
"""
Animation Builder Module
Converts frame sequences into GIF/MP4 animations.
"""
from PIL import Image
from typing import List, Optional
import imageio
import os
from pathlib import Path
class AnimationBuilder:
"""
Builds animations (GIF/MP4) from frame sequences.
"""
def __init__(self, output_dir: str = "outputs"):
"""
Initialize animation builder.
Args:
output_dir: Directory to save animations
"""
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
print(f"Output directory: {self.output_dir.absolute()}")
def save_frames(
self,
frames: List[Image.Image],
prefix: str = "frame",
subfolder: Optional[str] = None
) -> List[Path]:
"""
Save individual frames as PNG files.
Args:
frames: List of PIL Images
prefix: Filename prefix
subfolder: Optional subfolder name
Returns:
List of saved file paths
"""
if subfolder:
save_dir = self.output_dir / subfolder
else:
save_dir = self.output_dir
save_dir.mkdir(parents=True, exist_ok=True)
saved_paths = []
for i, frame in enumerate(frames):
filename = f"{prefix}_{i:04d}.png"
filepath = save_dir / filename
frame.save(filepath)
saved_paths.append(filepath)
print(f"Saved {len(frames)} frames to {save_dir}")
return saved_paths
def create_gif(
self,
frames: List[Image.Image],
output_filename: str = "animation.gif",
fps: int = 8,
loop: int = 0,
optimize: bool = True
) -> Path:
"""
Create GIF animation from frames.
Args:
frames: List of PIL Images
output_filename: Output GIF filename
fps: Frames per second
loop: Number of loops (0 = infinite)
optimize: Optimize GIF size
Returns:
Path to saved GIF
"""
output_path = self.output_dir / output_filename
# Calculate duration per frame in milliseconds
duration_ms = int(1000 / fps)
# Save as GIF
frames[0].save(
output_path,
save_all=True,
append_images=frames[1:],
duration=duration_ms,
loop=loop,
optimize=optimize
)
file_size_mb = output_path.stat().st_size / (1024 * 1024)
print(f"GIF saved: {output_path} ({file_size_mb:.2f} MB)")
return output_path
def create_mp4(
self,
frames: List[Image.Image],
output_filename: str = "animation.mp4",
fps: int = 8,
quality: int = 8
) -> Path:
"""
Create MP4 video from frames.
Args:
frames: List of PIL Images
output_filename: Output MP4 filename
fps: Frames per second
quality: Video quality (1-10, 10 is best)
Returns:
Path to saved MP4
"""
output_path = self.output_dir / output_filename
# Convert PIL Images to numpy arrays
frame_arrays = [imageio.core.util.Array(frame) for frame in frames]
# Write video
imageio.mimsave(
output_path,
frame_arrays,
fps=fps,
quality=quality,
codec='libx264',
pixelformat='yuv420p'
)
file_size_mb = output_path.stat().st_size / (1024 * 1024)
print(f"MP4 saved: {output_path} ({file_size_mb:.2f} MB)")
return output_path
def create_side_by_side_comparison(
self,
frames_a: List[Image.Image],
frames_b: List[Image.Image],
output_filename: str = "comparison.gif",
fps: int = 8,
labels: Optional[tuple] = None
) -> Path:
"""
Create side-by-side comparison animation.
Args:
frames_a: First frame sequence
frames_b: Second frame sequence
output_filename: Output filename
fps: Frames per second
labels: Optional tuple of labels for each sequence
Returns:
Path to saved animation
"""
combined_frames = []
for frame_a, frame_b in zip(frames_a, frames_b):
# Ensure same height
height = max(frame_a.height, frame_b.height)
frame_a = frame_a.resize((int(frame_a.width * height / frame_a.height), height))
frame_b = frame_b.resize((int(frame_b.width * height / frame_b.height), height))
# Combine horizontally
combined = Image.new('RGB', (frame_a.width + frame_b.width, height))
combined.paste(frame_a, (0, 0))
combined.paste(frame_b, (frame_a.width, 0))
combined_frames.append(combined)
return self.create_gif(combined_frames, output_filename, fps)
def create_grid_animation(
self,
frame_sequences: List[List[Image.Image]],
output_filename: str = "grid.gif",
fps: int = 8,
grid_size: Optional[tuple] = None
) -> Path:
"""
Create grid animation showing multiple sequences.
Args:
frame_sequences: List of frame sequences
output_filename: Output filename
fps: Frames per second
grid_size: Optional (rows, cols) for grid layout
Returns:
Path to saved animation
"""
num_sequences = len(frame_sequences)
if grid_size is None:
# Auto-calculate grid size
cols = int(num_sequences ** 0.5)
rows = (num_sequences + cols - 1) // cols
else:
rows, cols = grid_size
# Get frame count (use minimum)
num_frames = min(len(seq) for seq in frame_sequences)
grid_frames = []
for frame_idx in range(num_frames):
# Get all frames at this index
current_frames = [seq[frame_idx] for seq in frame_sequences]
# Resize all to same size
target_size = (256, 256)
current_frames = [f.resize(target_size) for f in current_frames]
# Create grid
grid_width = cols * target_size[0]
grid_height = rows * target_size[1]
grid_image = Image.new('RGB', (grid_width, grid_height))
for i, frame in enumerate(current_frames):
row = i // cols
col = i % cols
x = col * target_size[0]
y = row * target_size[1]
grid_image.paste(frame, (x, y))
grid_frames.append(grid_image)
return self.create_gif(grid_frames, output_filename, fps)
if __name__ == "__main__":
print("Animation builder module loaded.")
print("Use to convert frame sequences into GIF/MP4 animations.")