Spaces:
Paused
Paused
| import gradio as gr | |
| import torch | |
| import os | |
| import tempfile | |
| import shutil | |
| import imageio | |
| import logging | |
| from pathlib import Path | |
| # Import from our modules | |
| from model_loader import ModelLoader, MODELS_ROOT_DIR | |
| from video_processor import VideoProcessor | |
| from config import CAMERA_TRANSFORMATIONS, TEST_DATA_DIR | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # Global model loader instance | |
| model_loader = ModelLoader() | |
| video_processor = None | |
| def init_video_processor(): | |
| """Initialize video processor""" | |
| global video_processor | |
| if model_loader.is_loaded and video_processor is None: | |
| video_processor = VideoProcessor(model_loader.pipe) | |
| return video_processor is not None | |
| def extract_frames_from_video(video_path, output_dir, max_frames=81): | |
| """Extract frames from video and ensure we have at least 81 frames""" | |
| os.makedirs(output_dir, exist_ok=True) | |
| reader = imageio.get_reader(video_path) | |
| fps = reader.get_meta_data()['fps'] | |
| total_frames = reader.count_frames() | |
| frames = [] | |
| for i, frame in enumerate(reader): | |
| frames.append(frame) | |
| reader.close() | |
| # If we have fewer than required frames, repeat the last frame | |
| if len(frames) < max_frames: | |
| logger.info(f"Video has {len(frames)} frames, padding to {max_frames} frames") | |
| last_frame = frames[-1] | |
| while len(frames) < max_frames: | |
| frames.append(last_frame) | |
| # Save frames | |
| for i, frame in enumerate(frames[:max_frames]): | |
| frame_path = os.path.join(output_dir, f"frame_{i:04d}.png") | |
| imageio.imwrite(frame_path, frame) | |
| return len(frames[:max_frames]), fps | |
| def generate_recammaster_video( | |
| video_file, | |
| text_prompt, | |
| camera_type, | |
| progress=gr.Progress() | |
| ): | |
| """Main function to generate video with ReCamMaster""" | |
| if not model_loader.is_loaded: | |
| return None, "Error: Models not loaded! Please load models first." | |
| if not init_video_processor(): | |
| return None, "Error: Failed to initialize video processor." | |
| if video_file is None: | |
| return None, "Please upload a video file." | |
| try: | |
| # Create temporary directory for processing | |
| with tempfile.TemporaryDirectory() as temp_dir: | |
| progress(0.1, desc="Processing input video...") | |
| # Copy uploaded video to temp directory | |
| input_video_path = os.path.join(temp_dir, "input.mp4") | |
| shutil.copy(video_file, input_video_path) | |
| # Extract frames | |
| progress(0.2, desc="Extracting video frames...") | |
| num_frames, fps = extract_frames_from_video(input_video_path, os.path.join(temp_dir, "frames")) | |
| logger.info(f"Extracted {num_frames} frames at {fps} fps") | |
| # Process with ReCamMaster | |
| progress(0.3, desc="Processing with ReCamMaster...") | |
| output_video = video_processor.process_video( | |
| input_video_path, | |
| text_prompt, | |
| camera_type | |
| ) | |
| # Save output video | |
| progress(0.9, desc="Saving output video...") | |
| output_path = os.path.join(temp_dir, "output.mp4") | |
| from diffsynth import save_video | |
| save_video(output_video, output_path, fps=30, quality=5) | |
| # Copy to persistent location | |
| final_output_path = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False).name | |
| shutil.copy(output_path, final_output_path) | |
| progress(1.0, desc="Done!") | |
| transformation_name = CAMERA_TRANSFORMATIONS.get(str(camera_type), "Unknown") | |
| status_msg = f"Successfully generated video with '{transformation_name}' camera movement!" | |
| return final_output_path, status_msg | |
| except Exception as e: | |
| logger.error(f"Error generating video: {str(e)}") | |
| return None, f"Error: {str(e)}" | |
| # Create Gradio interface | |
| with gr.Blocks(title="ReCamMaster Demo") as demo: | |
| gr.Markdown(f""" | |
| # 🎥 ReCamMaster | |
| ReCamMaster allows you to re-capture videos with novel camera trajectories. | |
| Upload a video and select a camera transformation to see the magic! | |
| """) | |
| with gr.Row(): | |
| with gr.Column(): | |
| # Video input section | |
| with gr.Group(): | |
| gr.Markdown("### Step 1: Upload Video") | |
| video_input = gr.Video(label="Input Video") | |
| text_prompt = gr.Textbox( | |
| label="Text Prompt (describe your video)", | |
| placeholder="A person walking in the street", | |
| value="A dynamic scene" | |
| ) | |
| # Camera selection | |
| with gr.Group(): | |
| gr.Markdown("### Step 2: Select Camera Movement") | |
| camera_type = gr.Radio( | |
| choices=[(v, k) for k, v in CAMERA_TRANSFORMATIONS.items()], | |
| label="Camera Transformation", | |
| value="1" | |
| ) | |
| # Generate button | |
| generate_btn = gr.Button("Generate Video", variant="primary") | |
| with gr.Column(): | |
| # Output section | |
| output_video = gr.Video(label="Output Video") | |
| status_output = gr.Textbox(label="Generation Status", interactive=False) | |
| # Example videos | |
| gr.Markdown("### Example Videos") | |
| gr.Examples( | |
| examples=[ | |
| [f"{TEST_DATA_DIR}/videos/case0.mp4", "A person dancing", "1"], | |
| [f"{TEST_DATA_DIR}/videos/case1.mp4", "A scenic view", "5"], | |
| ], | |
| inputs=[video_input, text_prompt, camera_type], | |
| ) | |
| # Event handlers | |
| generate_btn.click( | |
| fn=generate_recammaster_video, | |
| inputs=[video_input, text_prompt, camera_type], | |
| outputs=[output_video, status_output] | |
| ) | |
| if __name__ == "__main__": | |
| model_loader.load_models() | |
| demo.launch(share=True) |