GIF frame extraction made easy (sci. vis. util)

TL;DR;Summary

Extract the single frames of a GIF to repurpose them (for example to create a video) with the below code. As always, the most up-to-date version can be found in the GitHub gist

def gif2imgs(file,
             save=False,
             outpath='same',
             outfilename='same',
             ):
    """
    Extract image array from a GIF file.

    Based on PIL, frames of a GIF file are extracted to a numpy array. If
    more than one frame is present in the GIF, a list of numpy arrays is 
    returned. It is possible to save the images directy, by default in the 
    same path as the GIF and the same basename.
    Parameters
    ----------
    file : str
        The GIF file containing one or more image frames.
    save : bool, optional
        Whether to save the image frames directy. The default is False, i.e.
        the function returns a list of the image frames as seperate numpy arrays
    outpath : str, optional
        Where to save the extracted image frames.
        The default is 'same', i.e. same folder
    outfilename : str, optional
        How to name the extracted image as files.
        The default is 'same', i.e. taking the basename and adding _frameX
        to it. If a string is passed, the same suffix is added but the basename
        is taken as the passed in string.

    Returns
    -------
    list of frames or two lists (of filenames of extracted image and images)
        The extracted frames from the GIF and if saving was turned on also 
        the filenames of written out frames.

    """

    import PIL
    import numpy as np
    import os
    import matplotlib.image as img

    pilIm = PIL.Image.open(file)
    pilIm.seek(0)

    # Read all images inside
    images = []
    try:
        while True:
            # Get image as numpy array
            tmp = pilIm.convert()  # Make without palette
            a = np.asarray(tmp)
            if len(a.shape) == 0:
                continue
            # Store, and next
            images.append(a)
            pilIm.seek(pilIm.tell()+1)
    except EOFError:
        pass

    if save:
        if outpath == 'same':
            outpath = os.path.dirname(file) + os.sep
        if outfilename == 'same':
            outfilename = os.path.basename(file)

            outfiles = []
        for frameno, frame in enumerate(images):
            _ = outpath+outfilename.rstrip('.gif') + f'_frame{frameno}.png'
            img.imsave(_, frame, vmin=0, vmax=255)
            outfiles.append(_)

        return outfiles, images
    else:
        return images
      

if __name__ == '__main__':
    import requests
    import matplotlib.pyplot as plt
    fileurl = 'https://gifdb.com/images/high/jumping-cat-typing-on-keyboard-2b15r60jnh8hn5sv.gif'
    response = requests.get(fileurl)
    filename = './example.gif'
    with open(filename, 'wb') as fo:
      fo.write(response.content)
    frames = gif2imgs(filename)
    plt.imshow(frames[0])

Recently I wanted to make a video for a talk. And that is where the trouble started.

Initially, I thought to place a video and GIF side by side. However, getting a synchronous playback was tricky because there are few to no control options for GIFs in most presentation software. As a result, I had to extract the frames from the GIF instead and had to convert them into a video. While I could do this with any of many online services I specifically needed a synchronised playback as the timing of the content needed to match. The code above does exactly these frame extractions and pairs up nicely with another utility, the img2vid (which converts image files to a video with a choosable frame rate.

Simply set the option to save the frames and then run the img2vid on the single images to make the video and you can use it with controls in your presentation software, be it PowerPoint, or anything else.

The above main part gets you an example GIF (it had to be a cat, right?) and shows you the first frame. Enjoy!