2016-11-15 Saving and displaying movies and dynamic figures

It is insanely useful to create movies to illustrate a talk, blog post or just to include in a notebook:

In [1]:
from IPython.display import HTML
HTML('<center><video controls autoplay loop src="../files/noise.mp4" width=61.8%/></center>')

For years I have used a custom made solution made around saving single frames and then calling ffmpeg to save that files to a movie file. That function (called anim_save had to be maintained accross different libraries to reflect new needs (going to WEBM and MP4 formats for instance). That made the code longer than necessary and had not its place in a scientific library.

Here, I show how to use the animation library from matplotlib to replace that

There quite a few pages on the web describing a possible alternative:

In [2]:
import numpy as np
image = np.random.rand(64, 16, 128)
from IPython.display import display, clear_output, HTML, Image
In [3]:
import sys
import matplotlib.pyplot as plt
from matplotlib import animation, rc
animation.rcParams['animation.writer'] = 'ffmpeg'

# First set up the figure, the axis, and the plot element we want to animate
fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(8, 2))
ax.xlim = (0, image.shape[1])
ax.ylim = (0, image.shape[2])
img = ax.imshow(image[:, :, 0].T, cmap='gray')
def animate(i): 
    #img = ax.imshow(image[:, :, i].T, cmap='gray')
    img.set_data(image[:, :, i].T)
    print ('It: %i'%i)
    return (img,)
# call the animator. blit=True means only re-draw the parts that have changed.
# *interval* draws a new frame every *interval* milliseconds.
anim = animation.FuncAnimation(fig, animate, frames=image.shape[-1], interval=50, blit=True)
It: 127

The same anim object can be used to save the movie as a file:

In [4]:
Help on method save in module matplotlib.animation:

save(filename, writer=None, fps=None, dpi=None, codec=None, bitrate=None, extra_args=None, metadata=None, extra_anim=None, savefig_kwargs=None) method of matplotlib.animation.FuncAnimation instance
    Saves a movie file by drawing every frame.
    *filename* is the output filename, e.g., :file:`mymovie.mp4`
    *writer* is either an instance of :class:`MovieWriter` or a string
    key that identifies a class to use, such as 'ffmpeg' or 'mencoder'.
    If nothing is passed, the value of the rcparam `animation.writer` is
    *dpi* controls the dots per inch for the movie frames. This combined
    with the figure's size in inches controls the size of the movie.
    *savefig_kwargs* is a dictionary containing keyword arguments to be
    passed on to the 'savefig' command which is called repeatedly to save
    the individual frames. This can be used to set tight bounding boxes,
    for example.
    *extra_anim* is a list of additional `Animation` objects that should
    be included in the saved movie file. These need to be from the same
    `matplotlib.Figure` instance. Also, animation frames will just be
    simply combined, so there should be a 1:1 correspondence between
    the frames from the different animations.
    These remaining arguments are used to construct a :class:`MovieWriter`
    instance when necessary and are only considered valid if *writer* is
    not a :class:`MovieWriter` instance.
    *fps* is the frames per second in the movie. Defaults to None,
    which will use the animation's specified interval to set the frames
    per second.
    *codec* is the video codec to be used. Not all codecs are supported
    by a given :class:`MovieWriter`. If none is given, this defaults to the
    value specified by the rcparam `animation.codec`.
    *bitrate* specifies the amount of bits used per second in the
    compressed movie, in kilobits per second. A higher number means a
    higher quality movie, but at the cost of increased file size. If no
    value is given, this defaults to the value given by the rcparam
    *extra_args* is a list of extra string arguments to be passed t