pythonnumpymatplotlibanimationimshow

Matplotlib FuncAnimation Step-by-Step Animation Function


I am trying to use matplotlib's FuncAnimation to make an animated video. Each frame is just a boolean n x n array visualised as white/black squares. I can do this successfully by defining all the arrays in advance and then going through them one by one. This uses code similar to matplotlib's example.

My items are rather large and I want to run the simulation for a long time. I thus don't want to create the entire list of arrays then go through them one by one. Instead, I want to define the animate function to do each step. Let me explain with a minimal non-working example. My actual example includes far larger arrays!

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

def create_video(n):
    global X
    X = np.random.binomial(1, 0.3, size = (n,n))
    
    fig = plt.figure()
    im = plt.imshow(X, cmap = plt.cm.gray)
    
    def animate(t):
        global X
        X = np.roll(X, +1, axis = 0)
        im.set_array(X)
    
    anim = FuncAnimation(
        fig,
        animate,
        frames = 100,
        interval = 1000 / 30,
        blit = True
    )
    
    return anim

anim = create_video(10)

This initialises some random 10 x 10 set of 0/1s then just 'rolls' it at each step. I get an error.

RuntimeError: The animation function must return a sequence of Artist objects.

If I remove the return anim, replacing it with pass, and replacing anim = create_video(10) with create_video(10), then I get a warning.

UserWarning: Animation was deleted without rendering anything. This is most likely unintended. To prevent deletion, assign the Animation to a variable that exists for as long as you need the Animation.

Clearly, I don't understand well enough FuncAnimation. What I want to happen is for the function animate to update the array X, by 'rolling' it one step, as well as doing im.set_array(X).


Solution

  • As explained in this answer:

    As the error suggests, and as can be seen e.g. in the simple_animation example, but also from the FuncAnimation documentation, the init_func as well as the updating func are supposed to return an iterable of artists to animate.

    The documentation does not say that this is actually only needed when using blit=True, but since you are using blitting here, it is definitely needed.

    So you have two ways:

    Complete Code

    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.animation import FuncAnimation
    
    
    def create_video(n):
        global X
        X = np.random.binomial(1, 0.3, size = (n, n))
    
        fig = plt.figure()
        im = plt.imshow(X, cmap = plt.cm.gray)
    
        def animate(t):
            global X
            X = np.roll(X, +1, axis = 0)
            im.set_array(X)
            return im, 
    
        anim = FuncAnimation(
            fig,
            animate,
            frames = 100,
            interval = 1000/30,
            blit = True
        )
    
        plt.show()
    
        return anim
    
    anim = create_video(10)
    

    enter image description here