Created
April 9, 2020 21:48
-
-
Save tdhooper/d024da3d80834685c395c795820628a1 to your computer and use it in GitHub Desktop.
Optimize a gif to a specific file size
This file contains 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
#!/usr/bin/env python3 | |
import sys | |
import os | |
import ffmpeg | |
from pygifsicle import optimize | |
frames_path = sys.argv[1] | |
output_name = sys.argv[2] | |
palette_filename = 'palette.png' | |
def generate_gif(colors, suffix, **kwargs): | |
print('.', end='', flush=True) | |
output_filename = '{}_{}.gif'.format(output_name, suffix) | |
# Generate palette | |
( | |
ffmpeg | |
.input('{}/*.png'.format(frames_path), pattern_type='glob') | |
.filter('palettegen', stats_mode='diff', max_colors=colors, reserve_transparent=False) | |
.output(palette_filename) | |
.overwrite_output() | |
.run(quiet=True) | |
) | |
# Generate gif | |
palette = ( | |
ffmpeg | |
.input(palette_filename) | |
) | |
frames = ( | |
ffmpeg | |
.input('{}/*.png'.format(frames_path), pattern_type='glob') | |
) | |
( | |
ffmpeg | |
.filter((frames, palette), 'paletteuse', **kwargs) | |
.output(output_filename) | |
.overwrite_output() | |
.run(quiet=True) | |
) | |
optimize(output_filename, options=['--optimize=03', '--delay=6', '--no-warnings']) | |
size = os.stat(output_filename).st_size | |
return size | |
def binary_search(func, low, high, target): | |
while low <= high: | |
mid = low + (high - low) // 2 | |
candidate = func(mid) | |
# Check if x is present at mid | |
if candidate == target: | |
return (candidate, mid) | |
# If candidate is greater, ignore left half | |
elif candidate < target: | |
low = mid + 1 | |
# If candidate is smaller, ignore right half | |
else: | |
high = mid - 1 | |
if candidate > target: | |
candidate = func(mid - 1) | |
return (candidate, mid) | |
def configure_generate_gif(**kwargs): | |
suffix = '-'.join(str(v) for v in kwargs.values()) | |
def func(colors): | |
return generate_gif(colors, suffix, **kwargs) | |
return (suffix, func) | |
configurations = [ | |
configure_generate_gif(dither='bayer', bayer_scale=1), | |
configure_generate_gif(dither='bayer', bayer_scale=2), | |
configure_generate_gif(dither='bayer', bayer_scale=3), | |
configure_generate_gif(dither='floyd_steinberg'), | |
configure_generate_gif(dither='sierra2'), | |
configure_generate_gif(dither='sierra2_4a'), | |
] | |
for suffix, func in configurations: | |
print(suffix, end='', flush=True) | |
final_size, final_colors = binary_search(func, 1, 256, 2000000) | |
print(' {} bytes, {} colours'.format(final_size, final_colors)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment