diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ec759f2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.12-slim-bookworm + +WORKDIR /app + +COPY video_to_gif.py /app/video_to_gif.py + +RUN apt update && \ + apt upgrade && \ + apt install ffmpeg -y + +ENTRYPOINT ["python", "video_to_gif.py"] \ No newline at end of file diff --git a/README.md b/README.md index ad8f0ad..a02fb4b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # video-to-gif +## Usage ``` -usage: video_to_gif.py [-h] -i INPUT [-w WIDTH] [-r FRAMERATE] [-t TAG] +usage: video_to_gif.py [-h] -i INPUT [-w WIDTH] [-r FRAMERATE] + [-s {fast_bilinear,bilinear,bicubic,experimental,neighbor,area,bicublin,gauss,sinc,lanczos,spline}] + [-interpolate {none,dup,blend,mci}] [-t TAG] Use ffmpeg to make GIFs from videos @@ -9,10 +12,20 @@ options: -i INPUT, --input INPUT Input pattern like '/users/MyName/Videos/*.mov' -w WIDTH, --width WIDTH - Width of the GIF in pixels, respecting aspect ratio + Width of the GIF in pixels, respecting aspect ratio (default: 960) -r FRAMERATE, --framerate FRAMERATE - Framerate of GIF + Framerate of GIF (default: 12) + -s {fast_bilinear,bilinear,bicubic,experimental,neighbor,area,bicublin,gauss,sinc,lanczos,spline}, --scaler {fast_bilinear,bilinear,bicubic,experimental,neighbor,area,bicublin,gauss,sinc,lanczos,spline} + Scaling algorithm to use (default: lanczos) + -interpolate {none,dup,blend,mci}, --interpolate {none,dup,blend,mci} + Interpolation method to use (default: none) -t TAG, --tag TAG Optional tag included in file name — Be gay and do crime +``` + +## Docker +```shell +docker build -t video_to_gif:latest . +docker run video_to_gif --help ``` \ No newline at end of file diff --git a/video_to_gif.py b/video_to_gif.py index dd57a64..7a87338 100644 --- a/video_to_gif.py +++ b/video_to_gif.py @@ -16,11 +16,13 @@ def get_args() -> Dict[str, Any]: parser.add_argument('-i', '--input', type=str, required=True, help="Input pattern like '/users/MyName/Videos/*.mov'") parser.add_argument('-w', '--width', type=str, default='960', - help='Width of the GIF in pixels, respecting aspect ratio') + help='Width of the GIF in pixels, respecting aspect ratio (default: 960)') parser.add_argument('-r', '--framerate', type=str, - default='12', help='Framerate of GIF') + default='12', help='Framerate of GIF (default: 12)') parser.add_argument('-s', '--scaler', type=str, choices=get_scaling_algorithms(), - default='lanczos', help='Scaling algorithm to use') + default='lanczos', help='Scaling algorithm to use (default: lanczos)') + parser.add_argument('-interpolate', '--interpolate', type=str, choices=get_interpolation_methods(), + default='none', help='Interpolation method to use (default: none)') parser.add_argument('-t', '--tag', type=str, default=get_tag(), help='Optional tag included in file name') return vars(parser.parse_args()) @@ -31,6 +33,10 @@ def get_scaling_algorithms() -> List[str]: 'area', 'bicublin', 'gauss', 'sinc', 'lanczos', 'spline'] +def get_interpolation_methods() -> List[str]: + return ['none', 'dup', 'blend', 'mci'] + + # TODO: Figure out how to specify both scaling and dithering algorithms. def get_dithering_algorithms() -> List[str]: return ['none', 'auto', 'bayer', 'ed', 'a_dither', 'x_dither'] @@ -54,14 +60,20 @@ def generate_gif_files(inputs: List[str], args: Dict[str, Any]) -> Dict[str, str width = args['width'] framerate = args['framerate'] scaler = args['scaler'] + interpolate = args['interpolate'] + + interpolate_cmd = '' + if interpolate != 'none': + interpolate_cmd = f"minterpolate=fps={ \ + framerate}:mi_mode={interpolate}," output_map = {} for input in inputs: file_name = f"{Path(input).stem}_{tag}.gif" full_path = f"{Path(input).parent}/{file_name}" - call_ffmpeg(f"-i {input} -f gif -r {framerate} -filter_complex scale={ \ - width}:-1:flags={scaler},split[v1][v2];[v1]palettegen[plt];[v2][plt]paletteuse { \ - full_path}") + call_ffmpeg(f"-i {input} -f gif -r {framerate} -filter_complex {interpolate_cmd}scale={ \ + width}:-1:flags={scaler},split[v1][v2];[v1]palettegen[plt];[v2][plt]paletteuse { \ + full_path}") output_map[input] = full_path return output_map