Spaces:
Build error
Build error
| import numpy as np | |
| import cv2 | |
| from PIL import Image, ImageEnhance | |
| import tempfile | |
| import os | |
| from moviepy.editor import ImageClip, AudioFileClip, CompositeVideoClip | |
| import librosa | |
| import soundfile as sf | |
| import subprocess | |
| def process_image_for_video(image, style="static"): | |
| """ | |
| Process image for video creation based on style | |
| Args: | |
| image: PIL Image | |
| style: Animation style | |
| Returns: | |
| str: Path to processed image | |
| """ | |
| # Convert to RGB if necessary | |
| if image.mode != 'RGB': | |
| image = image.convert('RGB') | |
| # Enhance image quality | |
| enhancer = ImageEnhance.Contrast(image) | |
| image = enhancer.enhance(1.2) | |
| enhancer = ImageEnhance.Sharpness(image) | |
| image = enhancer.enhance(1.1) | |
| # Save processed image | |
| temp_path = os.path.join(tempfile.gettempdir(), f"processed_{uuid.uuid4().hex}.jpg") | |
| image.save(temp_path, quality=95) | |
| return temp_path | |
| def extend_audio_to_duration(audio_path, target_duration): | |
| """ | |
| Extend audio to target duration by looping and fading | |
| Args: | |
| audio_path: Path to original audio file | |
| target_duration: Target duration in seconds | |
| Returns: | |
| str: Path to extended audio file | |
| """ | |
| # Load audio | |
| y, sr = librosa.load(audio_path) | |
| original_duration = len(y) / sr | |
| if original_duration >= target_duration: | |
| return audio_path | |
| # Calculate how many times to loop | |
| loops_needed = int(target_duration / original_duration) + 1 | |
| # Create extended audio | |
| extended_audio = np.tile(y, loops_needed) | |
| # Trim to exact duration | |
| target_samples = int(target_duration * sr) | |
| extended_audio = extended_audio[:target_samples] | |
| # Apply fade in/out | |
| fade_samples = int(0.5 * sr) # 0.5 second fade | |
| extended_audio[:fade_samples] *= np.linspace(0, 1, fade_samples) | |
| extended_audio[-fade_samples:] *= np.linspace(1, 0, fade_samples) | |
| # Save extended audio | |
| output_path = os.path.join(tempfile.gettempdir(), f"extended_{uuid.uuid4().hex}.wav") | |
| sf.write(output_path, extended_audio, sr) | |
| return output_path | |
| def create_video_with_audio(image_path, audio_path, duration): | |
| """ | |
| Create video by combining image and audio | |
| Args: | |
| image_path: Path to image file | |
| audio_path: Path to audio file | |
| duration: Video duration in seconds | |
| Returns: | |
| str: Path to output video file | |
| """ | |
| # Create video clip from image | |
| image_clip = ImageClip(image_path, duration=duration) | |
| # Create audio clip | |
| audio_clip = AudioFileClip(audio_path) | |
| # Set audio to video | |
| video_clip = image_clip.set_audio(audio_clip) | |
| # Set fps | |
| video_clip.fps = 24 | |
| # Output path | |
| output_path = os.path.join(tempfile.gettempdir(), f"video_{uuid.uuid4().hex}.mp4") | |
| # Write video file | |
| video_clip.write_videofile( | |
| output_path, | |
| codec='libx264', | |
| audio_codec='aac', | |
| temp_audiofile=os.path.join(tempfile.gettempdir(), f"temp_audio_{uuid.uuid4().hex}.m4a"), | |
| remove_temp=True | |
| ) | |
| # Close clips | |
| image_clip.close() | |
| audio_clip.close() | |
| video_clip.close() | |
| return output_path | |
| def cleanup_temp_files(file_paths): | |
| """Clean up temporary files""" | |
| for file_path in file_paths: | |
| try: | |
| if os.path.exists(file_path): | |
| os.remove(file_path) | |
| except Exception as e: | |
| print(f"Error removing {file_path}: {e}") | |
| def apply_video_effects(image_path, style, duration): | |
| """ | |
| Apply video effects to create animated video from static image | |
| Args: | |
| image_path: Path to image | |
| style: Animation style | |
| duration: Duration in seconds | |
| Returns: | |
| str: Path to animated video | |
| """ | |
| # Read image | |
| image = cv2.imread(image_path) | |
| height, width = image.shape[:2] | |
| # Create temporary video path | |
| temp_video = os.path.join(tempfile.gettempdir(), f"animated_{uuid.uuid4().hex}.mp4") | |
| # Setup video writer | |
| fourcc = cv2.VideoWriter_fourcc(*'mp4v') | |
| out = cv2.VideoWriter(temp_video, fourcc, 30.0, (width, height)) | |
| frames = int(duration * 30) | |
| for i in range(frames): | |
| progress = i / frames | |
| if style == "zoom_in": | |
| # Zoom in effect | |
| scale = 1.0 + (progress * 0.5) | |
| new_height = int(height / scale) | |
| new_width = int(width / scale) | |
| y_start = (height - new_height) // 2 | |
| x_start = (width - new_width) // 2 | |
| frame = image[y_start:y_start+new_height, x_start:x_start+new_width] | |
| frame = cv2.resize(frame, (width, height)) | |
| elif style == "zoom_out": | |
| # Zoom out effect | |
| scale = 1.5 - (progress * 0.5) | |
| new_height = int(height / scale) | |
| new_width = int(width / scale) | |
| y_start = (height - new_height) // 2 | |
| x_start = (width - new_width) // 2 | |
| frame = image[y_start:y_start+new_height, x_start:x_start+new_width] | |
| frame = cv2.resize(frame, (width, height)) | |
| elif style == "pan_left": | |
| # Pan left effect | |
| offset = int(progress * (width * 0.3)) | |
| frame = image[:, offset:offset+width] if offset+width <= width else image[:, -width:] | |
| elif style == "pan_right": | |
| # Pan right effect | |
| offset = int((1 - progress) * (width * 0.3)) | |
| frame = image[:, offset:offset+width] if offset+width <= width else image[:, -width:] | |
| else: # static | |
| frame = image | |
| out.write(frame) | |
| out.release() | |
| return temp_video |