Explain Codes LogoExplain Codes Logo

Programmatically generate video or animated GIF in Python?

python
image-processing
gif-animation
imageio
Alex KataevbyAlex Kataev·Oct 31, 2024
TLDR

The way to rapidly create animated GIFs/videos in Python is by using the imageio library. Install it with pip install imageio. Here's a quick way to generate a GIF from a sequence of images:

import imageio images = ['image1.png', 'image2.png', ...] # Image sequence with imageio.get_writer('animation.gif', mode='I') as writer: for img in images: writer.append_data(imageio.imread(img)) # It's like 'Add image. And another one. And another one...'

To generate a video, simply replace 'animation.gif' with 'video.mp4'. You can also specify fps (frames per second) in get_writer. Don't forget to install FFmpeg (pip install imageio[ffmpeg]) to handle video processing tasks.

Going beyond the basics

Tackling large image sequences

In cases where you're dealing with a boatload of images, it's crucial to optimize your memory usage and processing time. One neat way to combat this is by using lazy-loading techniques, such as PIL's Image.open.

from PIL import Image images = ['image1.png', 'image2.png', ...] # Image sequence with imageio.get_writer('animation.gif', mode='I', fps=20) as writer: # 20fps, because why not! for img in images: with Image.open(img) as im: writer.append_data(np.array(im)) # "Get in the line please, one by one!"

Keeping your frames in order:

Story telling is an art, and disorderly frames can ruin your whole plot! glob.glob coupled with sorting is your knight in shining armor when dealing with images that include numbers:

import glob image_paths = sorted(glob.glob('images/*.png')) #Thanks to sorting, fairy '100.png' doesn't cut in front of poor '20.png' anymore!

Holding frames for the right time:

Not all scenes in your GIF need the same screentime. You can control the duration of individual scenes using the duration parameter of the append_data method.

frame_durations = [0.2, 0.1, ...] # Durations from your director's cut with imageio.get_writer('animation.gif', mode='I') as writer: for img, duration in zip(images, frame_durations): image = imageio.imread(img) writer.append_data(image, duration=duration) # Bye-bye Mr. Frame, see you after 'duration' seconds!

Dressing up your GIFs

Prioritize quality

While GIFs are great, bad ones can be worse than no GIFs. Your image quality takes the front seat when creating GIFs. Using high-quality images is absolutely recommended.

from PIL import ImageSequence, Image with Image.open('source.gif') as im: frames = [frame.copy() for frame in ImageSequence.Iterator(im)] frames = [np.array(frame.convert('RGBA')) for frame in frames] imageio.mimsave('high_quality.gif', frames, 'GIF', fps=5) #The name says it all!

Fight the byte!

If sharing your GIF on the interwebs or keeping your server costs down is your thing, you better consider your GIF file sizes. A good way to tone them down is by resizing your images before creating the GIF:

from PIL import Image # Resize images to width 640px while maintaining aspect ratio resized_images = [] for img in images: with Image.open(img) as im: im.thumbnail((640, im.size[1])) # Vanishing spell! resized_images.append(im) # Convert to numpy arrays and use imageio to create a light-weight GIF frames = [np.array(img.convert('RGBA')) for img in resized_images] imageio.mimsave('resized_animation.gif', frames, 'GIF', fps=5) # Skinny GIF, wow!

Going the extra mile

Command Line Magic!

ImageMagick's convert command is like a magic wand for those who love their terminal windows. This powerful tool lets you create and customize GIFs right from your command line:

convert -delay 20 -loop 0 frame0.png frame1.png frame2.png animation.gif

Just ensure that ImageMagick is installed on your systems.

Pillow brings gifts

The Python Pillow library treats you with properties like loop count and dithering, letting you have more control over your GIF cinema:

from PIL import ImageSequence, GifImagePlugin with Image.open('source.gif') as im: im.info['loop'] = 0 # Infinite loop. Energizer bunny approves! im.save('looping.gif', 'GIF', save_all=True, dither="None") # A magician never repeats his tricks!