Created
August 26, 2015 05:11
-
-
Save BrunoGrandePhD/577a6ddc1880182ea7b1 to your computer and use it in GitHub Desktop.
Automatically create animated GIFs from a batch of photos
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
#!/usr/bin/env python | |
""" | |
detect_gifs.py | |
============== | |
This script tries to automatically detect | |
GIF opportunities within a batch of photos. | |
""" | |
from __future__ import print_function | |
import argparse | |
import os | |
import glob | |
from PIL import Image | |
from datetime import datetime | |
import subprocess | |
def main(): | |
"""Detect GIFs""" | |
# Parse command-line arguments | |
args = parse_args() | |
# Ensure output_dir exists | |
if not os.path.exists(args.output_dir): | |
os.mkdir(args.output_dir) | |
# Obtain list of photo files | |
photos = [] | |
for ext in args.extensions: | |
pattern = "{}/*.{}".format(args.photo_dir, ext) | |
photos.extend(glob.glob(pattern)) | |
# Iterate over list of photos | |
all_animations = [] | |
animation = [] | |
prev_photo_dict = None | |
for photo in photos: | |
photo_dict = analyze_photo(photo) | |
# Compare with previous | |
if prev_photo_dict: | |
delta = (photo_dict["date"] - prev_photo_dict["date"]).seconds | |
if delta <= args.max_delta and photo_dict["orient"] == prev_photo_dict["orient"]: | |
animation.append(prev_photo_dict["photo"]) | |
else: | |
animation.append(prev_photo_dict["photo"]) | |
if len(animation) >= 3: | |
all_animations.append(animation) | |
animation = [] | |
# Store photo and date for future iterations | |
prev_photo_dict = photo_dict | |
# Create GIFs | |
# convert -resize 400 -layers optimize -coalesce -delay 30 -loop 0 photos/DSC09379.jpg photos/DSC09380.jpg photos/DSC09381.jpg ./gifs/animation_1.gif | |
for i, anim in enumerate(all_animations, start=1): | |
photo_dict = analyze_photo(anim[0]) | |
resize = str(args.resize) | |
if photo_dict["orient"] == "horizontal": | |
resize = "x" + resize | |
output = "{}/animation_{}.gif".format(args.output_dir, i) | |
cmd = ["convert", "-layers", "optimize", "-coalesce", "-resize", resize, "-delay", str(args.delay), "-loop", "0"] + anim + [output] | |
print(" ".join(cmd), end="\n\n") | |
subprocess.call(cmd) | |
def parse_args(): | |
"""Parse command-line arguments""" | |
parser = argparse.ArgumentParser() | |
parser.add_argument("photo_dir", help="Input directory containing photos") | |
parser.add_argument("--extensions", "-e", nargs="+", default=["jpeg", "jpg", "png"], help="File extensions for photos in input directory") | |
parser.add_argument("--max_delta", "-d", default=1, help="Number of seconds allowed between successive photos to constitute a GIF") | |
parser.add_argument("--delay", default=25, help="Milliseconds between GIF frames") | |
parser.add_argument("--resize", default=500, help="Max width or height of output GIFs") | |
parser.add_argument("--output_dir", default=".", help="Output directory for animated GIFs") | |
parse_args | |
args = parser.parse_args() | |
return args | |
def analyze_photo(path): | |
"""Return dict of attributes""" | |
photo = {"photo": path} | |
op_photo = Image.open(path) | |
exifdata = op_photo._getexif() | |
ctime = exifdata[0x9003] | |
photo["date"] = datetime.strptime(ctime, "%Y:%m:%d %H:%M:%S") | |
(width, height) = op_photo.size | |
if width > height: | |
photo["orient"] = "horizontal" | |
else: | |
photo["orient"] = "vertical" | |
return photo | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment