The world’s leading publication for data science, AI, and ML professionals.

The simplest way of making GIFs and math videos with Python

How to create amazing animations in seconds using Celluloid

Image by Author - Decision boundary evolution of the SVM Classifier (made with Celluloid)
Image by Author – Decision boundary evolution of the SVM Classifier (made with Celluloid)

"Celluloid, Easy Matplotlib Animations"

I really enjoy working with Data Visualization and I always wonder what’s the best way to provide more direct and intuitive visual interactions when I have to explain some result or complex model.

Lately, I’ve been growing to use GIFs and quick videos. Even if this makes the coding part harder and more complex, the result generally is much more efficient in communicating my findings and process.

But in Python, there’s always an easier and simpler way and to simplify the animating process, Celluloid was born.

Using only 50 lines of code to deal with Matplotlib Artists and ArtistAnimations Celluloid creates an animation from the series of images you want to plot into the Camera abstraction.

Let’s start by installing the library with

$ pip install celluloid

Now let’s get to it!

Four usage examples

  1. The simplest one
  2. Evolution plot
  3. Working with images
  4. Using dynamic labels and titles

The simplest one

Let’s create a simple plot just to demonstrate the basic usage of how to run the code in a Jupyter Notebook, but we could also use the method save(‘filename.gif_or_mp4’)

from celluloid import Camera # getting the camera
import matplotlib.pyplot as plt
import numpy as np
from IPython.display import HTML # to show the animation in Jupyter
fig, ax = plt.subplots() # creating my fig
camera = Camera(fig)# the camera gets the fig we'll plot
for i in range(10):
    ax.plot([i] * 5, c='black') # 5 element array from 0 to 9
    camera.snap() # the camera takes a snapshot of the plot
animation = camera.animate() # animation ready
HTML(animation.to_html5_video()) # displaying the animation
Image by Author
Image by Author

Evolution Plot

This is a fun kind of plot, but not a very useful one. The idea is to plot the dataset evolving little by little.

from celluloid import Camera # getting the camera
import matplotlib.pyplot as plt
import numpy as np
from IPython.display import HTML
fig, ax = plt.subplots(figsize=(10, 6)) # let's make it bigger
camera = Camera(fig)# the camera gets our figure
x = np.linspace(0, np.pi*2, 60) # a sine circle
y = np.sin(x) # sine
for i in range(len(x)):
    x_t = x[:i] # current dataset
    y_t = y[:i] # current sine
    ax.plot(x_t, y_t, c='blue')
    camera.snap()
animation = camera.animate()
HTML(animation.to_html5_video())
Image by Author
Image by Author

A fun little trick that makes the plot look like hand drawn

x_t = x[:i] + 1.3*np.random.random(size=i)
Image by Author
Image by Author

Working with Images

Images may help a lot to present ideas, and algorithms that use images are mainly well explained by videos

In this example, I animated a Neural Style Transfer between an image of my cat and Munch’s The Scream. I trained the model for 200 epochs and saved the result each 10 epochs (I’ll explain more about the algorithm in another article)

In my case, I saved the 20 photos during training to make the GIF using the pattern "./NST/epochs/epoch_k.jpg"

from celluloid import Camera # getting the camera
import matplotlib.pyplot as plt
import numpy as np
from IPython.display import HTML
import os
fig, ax = plt.subplots() # make it bigger
camera = Camera(fig)# the camera gets our figure
for img in os.listdir("NST/epochs"):
    img_obj = plt.imread(os.path.join("NST/epochs"), img) # reading
    ax.imshow(img_obj) # plotting
    camera.snap()
animation = camera.animate()
HTML(animation.to_html5_video())
Image by Author
Image by Author

Using dynamic labels and titles

This is one of the most interesting aspects of celluloid, here we have the capacity to make the plot very dynamic.

In this example I plotted two normal distributions with distinct means but the same standard deviation and then I changed this standard deviation to evaluate the impact it has on each curve.

I put the current standard deviation on the title and the real mean as the label of each curve. I had to pass the calculated label as a tuple because I used two curves, if it was only one I could’ve used just a like plt.legend([calculated_label]), which is simple

from celluloid import Camera # getting the camera
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
from IPython.display import HTML
std = 3 # start std 
Am = 15. # mean of first normal
Bm = 12.5 # mean of second normal
fig, ax = plt.subplots(figsize=(9,6)) # empty fig
camera = Camera(fig)
for j in range(10):
    plt.ylim((0, 0.2)) # setting up the limits (or else it will auto ajust
    plt.xlim((-50, 50))

    A = np.random.normal(Am, std, size=(1000)) # creating the 1000-sized normals
    B = np.random.normal(Bm, std, size=(1000))    
    A_plot = sns.distplot(A, color='red') 
    B_plot = sns.distplot(B, color='blue')
    plt.legend(( 
        'Real Mean A: {:.2f}'.format(np.mean(A)),
        'Real Mean B: {:.2f}'.format(np.mean(B))
    ))

    ax.text(0.5, 1.01, "Standard Deviation = "+str(std), transform=ax.transAxes) # making the dynamic title
    camera.snap() # camera snapshot

    std += 1 # incrementing the std
anim = camera.animate() # animating the plots
HTML(anim.to_html5_video())
Image by Author
Image by Author

All done?

This were just my basic ideas of how to use celluloid, but truth be told the possibilities are infinite and I’ve used in several applications, even to demonstrate how some Machine Learning Algorithms learn check it out.

Decision Tree

Image by Author - Decision Tree Classifier boundary plot (Available here)
Image by Author – Decision Tree Classifier boundary plot (Available here)

Support Vector Machine Classifier

Image by Author - SVM Classifier boundary plot (Available here)
Image by Author – SVM Classifier boundary plot (Available here)

If you liked this Tutorial, used and it worked (or if it didn’t) please contact me using the links in my profile


Related Articles