Skip to content

Instantly share code, notes, and snippets.

@BrunoGrandePhD
Created August 26, 2015 05:11
Show Gist options
  • Save BrunoGrandePhD/577a6ddc1880182ea7b1 to your computer and use it in GitHub Desktop.
Save BrunoGrandePhD/577a6ddc1880182ea7b1 to your computer and use it in GitHub Desktop.
Automatically create animated GIFs from a batch of photos
#!/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