The easiest and fastest way to make GIFs and math videos with Python | by Bruno Rodrigues | Towards Data Science

“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!
The simplest oneEvolution plotWorking with imagesUsing dynamic labels and titles
The simplest one
Evolution plot
Working with images
Using dynamic labels and titles
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 cameraimport matplotlib.pyplot as pltimport numpy as npfrom IPython.display import HTML # to show the animation in Jupyterfig, ax = plt.subplots() # creating my figcamera = Camera(fig)# the camera gets the fig we'll plotfor 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 plotanimation = camera.animate() # animation readyHTML(animation.to_html5_video()) # displaying the animation
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 cameraimport matplotlib.pyplot as pltimport numpy as npfrom IPython.display import HTMLfig, ax = plt.subplots(figsize=(10, 6)) # let's make it biggercamera = Camera(fig)# the camera gets our figurex = np.linspace(0, np.pi*2, 60) # a sine circley = np.sin(x) # sinefor 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())
A fun little trick that makes the plot look like hand drawn
x_t = x[:i] + 1.3*np.random.random(size=i)
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 cameraimport matplotlib.pyplot as pltimport numpy as npfrom IPython.display import HTMLimport osfig, ax = plt.subplots() # make it biggercamera = Camera(fig)# the camera gets our figurefor 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())
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 cameraimport matplotlib.pyplot as pltimport numpy as npimport seaborn as snsfrom IPython.display import HTMLstd = 3 # start std Am = 15. # mean of first normalBm = 12.5 # mean of second normalfig, ax = plt.subplots(figsize=(9,6)) # empty figcamera = 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 stdanim = camera.animate() # animating the plotsHTML(anim.to_html5_video())
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.
If you liked this tutorial, used and it worked (or if it didn’t) please contact me using the links in my profile