Spaces:
Running
Running
| """ | |
| Adapted from: | |
| https://github.com/genmoai/mochi/blob/main/demos/fine_tuner/trim_and_crop_videos.py | |
| """ | |
| from pathlib import Path | |
| import shutil | |
| import click | |
| from moviepy.editor import VideoFileClip | |
| from tqdm import tqdm | |
| def truncate_videos(folder, output_folder, num_frames, resolution, force_upsample): | |
| """Truncate all MP4 and MOV files in FOLDER to specified number of frames and resolution""" | |
| input_path = Path(folder) | |
| output_path = Path(output_folder) | |
| output_path.mkdir(parents=True, exist_ok=True) | |
| # Parse target resolution | |
| target_height, target_width = map(int, resolution.split("x")) | |
| # Calculate duration | |
| duration = (num_frames / 30) + 0.09 | |
| # Find all MP4 and MOV files | |
| video_files = ( | |
| list(input_path.rglob("*.mp4")) | |
| + list(input_path.rglob("*.MOV")) | |
| + list(input_path.rglob("*.mov")) | |
| + list(input_path.rglob("*.MP4")) | |
| ) | |
| for file_path in tqdm(video_files): | |
| try: | |
| relative_path = file_path.relative_to(input_path) | |
| output_file = output_path / relative_path.with_suffix(".mp4") | |
| output_file.parent.mkdir(parents=True, exist_ok=True) | |
| click.echo(f"Processing: {file_path}") | |
| video = VideoFileClip(str(file_path)) | |
| # Skip if video is too short | |
| if video.duration < duration: | |
| click.echo(f"Skipping {file_path} as it is too short") | |
| continue | |
| # Skip if target resolution is larger than input | |
| if target_width > video.w or target_height > video.h: | |
| if force_upsample: | |
| click.echo( | |
| f"{file_path} as target resolution {resolution} is larger than input {video.w}x{video.h}. So, upsampling the video." | |
| ) | |
| video = video.resize(width=target_width, height=target_height) | |
| else: | |
| click.echo( | |
| f"Skipping {file_path} as target resolution {resolution} is larger than input {video.w}x{video.h}" | |
| ) | |
| continue | |
| # First truncate duration | |
| truncated = video.subclip(0, duration) | |
| # Calculate crop dimensions to maintain aspect ratio | |
| target_ratio = target_width / target_height | |
| current_ratio = truncated.w / truncated.h | |
| if current_ratio > target_ratio: | |
| # Video is wider than target ratio - crop width | |
| new_width = int(truncated.h * target_ratio) | |
| x1 = (truncated.w - new_width) // 2 | |
| final = truncated.crop(x1=x1, width=new_width).resize((target_width, target_height)) | |
| else: | |
| # Video is taller than target ratio - crop height | |
| new_height = int(truncated.w / target_ratio) | |
| y1 = (truncated.h - new_height) // 2 | |
| final = truncated.crop(y1=y1, height=new_height).resize((target_width, target_height)) | |
| # Set output parameters for consistent MP4 encoding | |
| output_params = { | |
| "codec": "libx264", | |
| "audio": False, # Disable audio | |
| "preset": "medium", # Balance between speed and quality | |
| "bitrate": "5000k", # Adjust as needed | |
| } | |
| # Set FPS to 30 | |
| final = final.set_fps(30) | |
| # Check for a corresponding .txt file | |
| txt_file_path = file_path.with_suffix(".txt") | |
| if txt_file_path.exists(): | |
| output_txt_file = output_path / relative_path.with_suffix(".txt") | |
| output_txt_file.parent.mkdir(parents=True, exist_ok=True) | |
| shutil.copy(txt_file_path, output_txt_file) | |
| click.echo(f"Copied {txt_file_path} to {output_txt_file}") | |
| else: | |
| # Print warning in bold yellow with a warning emoji | |
| click.echo( | |
| f"\033[1;33m⚠️ Warning: No caption found for {file_path}, using an empty caption. This may hurt fine-tuning quality.\033[0m" | |
| ) | |
| output_txt_file = output_path / relative_path.with_suffix(".txt") | |
| output_txt_file.parent.mkdir(parents=True, exist_ok=True) | |
| output_txt_file.touch() | |
| # Write the output file | |
| final.write_videofile(str(output_file), **output_params) | |
| # Clean up | |
| video.close() | |
| truncated.close() | |
| final.close() | |
| except Exception as e: | |
| click.echo(f"\033[1;31m Error processing {file_path}: {str(e)}\033[0m", err=True) | |
| raise | |
| if __name__ == "__main__": | |
| truncate_videos() | |