Created
October 14, 2024 09:34
-
-
Save drorhilman/6f92e0d6dd24e4876298347d6cf7c94c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import subprocess | |
from moviepy.editor import VideoFileClip | |
from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip | |
from tqdm import tqdm | |
import imageio | |
from rich.progress import track, SpinnerColumn, Progress | |
from rich import print | |
def convert_video_to_gif(input_file='input.mp4', output_file='output.gif', fps=10, scale=0.75, start_time=None, end_time=None, quality=5): | |
""" | |
Convert an MP4 video to a GIF with options for quality, resolution, and length. | |
Args: | |
input_file (str): Path to the input MP4 file. | |
output_file (str): Path to the output GIF file. | |
fps (int): Frames per second for the output GIF. | |
scale (float): Scale factor for resizing (e.g., 0.5 for half size). | |
start_time (float): Start time in seconds for trimming the video. | |
end_time (float): End time in seconds for trimming the video. | |
quality (int): Quality of the output GIF (1-10, higher is better quality). | |
""" | |
try: | |
with VideoFileClip(input_file) as video: | |
# Trim video if start_time and end_time are specified | |
if start_time is not None or end_time is not None: | |
video = video.subclip(start_time, end_time) | |
# Resize video to specified scale | |
video = video.resize(scale if scale is not None else 1.0) | |
frames = [] | |
# Calculate total frames and ensure it does not lead to an overly large number | |
total_frames = min(int(video.duration * fps), 10000) # Limit to 10,000 frames to avoid memory overload | |
# Use rich's track to display progress bar during frame extraction | |
for i, frame in track(enumerate(video.iter_frames(fps=fps, dtype='uint8')), total=total_frames, description="Processing frames"): | |
if i >= total_frames: | |
break | |
frames.append(frame) | |
# Use a spinner to indicate progress during GIF creation | |
with Progress(SpinnerColumn(), transient=True) as progress: | |
print(f"[cyan]Creating GIF...") | |
task = progress.add_task("[cyan]Creating GIF...", total=None) | |
# Write frames to GIF using imageio with further compression options | |
# Set palettesize to 32 for better compression and optimize with quantizer='median_cut' | |
imageio.mimsave(output_file, frames, fps=fps, palettesize=32, loop=0, quantizer='median_cut', quality=quality) | |
progress.update(task, completed=True) | |
# Further compress the GIF using gifsicle | |
optimized_output_file = f"optimized_{output_file}" | |
subprocess.run(["gifsicle", "--optimize=3", "--colors", "32", output_file, "-o", optimized_output_file]) | |
print(f"[green]Optimized GIF saved to {optimized_output_file}[/green]") | |
except FileNotFoundError: | |
print(f"[red]Error: File '{input_file}' not found. Please check the file path.[/red]") | |
except ValueError as e: | |
print(f"[red]Error: Invalid value encountered. Details: {e}[/red]") | |
except OSError as e: | |
print(f"[red]Error: Unable to open video file '{input_file}'. Please check the file format and path.\nDetails: {e}[/red]") | |
except Exception as e: | |
print(f"[red]An unexpected error occurred: {e}[/red]") | |
def main(): | |
input_file = 'input.mp4' | |
output_file = 'output.gif' | |
fps = 8 # Lower fps to reduce size | |
scale = 0.75 | |
start_time = None | |
end_time = None | |
quality = 4 # Lower quality for compression | |
if not os.path.isfile(input_file): | |
print(f"[red]Error: File '{input_file}' not found.[/red]") | |
return | |
convert_video_to_gif( | |
input_file=input_file, | |
output_file=output_file, | |
fps=fps, | |
scale=scale, | |
start_time=start_time, | |
end_time=end_time, | |
quality=quality | |
) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment