'Is it possible to both save and show matplotlib animation without running each frame twice?

It is possible to save, and then show, a matplotlib animation, but it takes twice as long as it could, in all examples that I have found. I wonder if the same task could be done faster.

In more detail: What I have noticed is that the init function is run twice, and each frame function is run twice, one time for show, the other time for save. Obviously, it looks suboptimal. Is there a way to have both save, and show, without running each frame twice, and without writing a lot of extra code?

A simple example of an animation where each frame is run twice is below. Source: jakevdp.github.io. Output is also below.

"""
Matplotlib Animation Example

author: Jake Vanderplas
email: [email protected]
website: http://jakevdp.github.com
license: BSD
Please feel free to use and modify this, but keep the above information. Thanks!
"""

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

# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)

# initialization function: plot the background of each frame
def init():
    print("init")
    line.set_data([], [])
    return line,

# animation function.  This is called sequentially
def animate(i):
    print(i)
    x = np.linspace(0, 2, 1000)
    y = np.sin(2 * np.pi * (x - 0.01 * i))
    line.set_data(x, y)
    return line,

# call the animator.  blit=True means only re-draw the parts that have changed.
anim = animation.FuncAnimation(fig, animate, init_func=init,
                               frames=5, interval=20, blit=True, repeat=False)

# save the animation as an mp4.  This requires ffmpeg or mencoder to be
# installed.  The extra_args ensure that the x264 codec is used, so that
# the video can be embedded in html5.  You may need to adjust this for
# your system: for more information, see
# http://matplotlib.sourceforge.net/api/animation_api.html
anim.save('basic_animation.mp4', fps=30, extra_args=['-vcodec', 'libx264'])

plt.show()

enter image description here



Solution 1:[1]

I had a similar problem. I wanted to show my matplotlib animation on screen and then save that exact copy of the animation to an mp4 file. My main reason was that I wanted to provide input on the go and affect the plots as the plots were being created.

When I showed the plots on screen, I was able to manipulate them succesfully while looking at them. But the run that had been displayed, was not the one that was saved since showing and saving the plots are two seperate functions that are not performed at the same time. When you call FuncAnimation.save, the animation is being performed in the background and is not being shown on screen. I could give input while the animation was saving but I could not see what I was doing. The results of what had actually happened were only visible when I opened the mp4 file afterwards.

Based on the solution I ended up with to my problem, you could do the following for your code:

"""
Matplotlib Animation Example

author: Jake Vanderplas
email: [email protected]
website: http://jakevdp.github.com
license: BSD
Please feel free to use and modify this, but keep the above information. Thanks!
"""

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

# First set up the figure, the axis, and the plot element we want to animate
fig = plt.figure()
ax = plt.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)

# Create the x array. 
# It does not need to be assigned every time in the loop 
# as it remains the same in your example.
# This way it makes your code slighty more efficient.
x = np.linspace(0, 2, 1000)
# Set the number of frames to run the animation for
nr_of_frames = 50
# Set the frames per second to save it with
fps = 30
# Set the file path to save the mp4 file to
file_path = r"basic_animation.mp4"

# Create the ffmpeg writer
FFMpegWriter = animation.writers['ffmpeg']
writer = FFMpegWriter(fps=fps, extra_args=['-vcodec', 'libx264'])

# Open the mp4 file or create a new one if it does not exist
with writer.saving(fig, file_path, dpi=300):
    # Show the plot and set blocking to False
    plt.show(block=False)
    # Display and save each frame
    for i in range(nr_of_frames):
        print(i)
        y = np.sin(2 * np.pi * (x - 0.01 * i))
        line.set_data(x, y)

        fig.canvas.flush_events()
        writer.grab_frame()

Keep in mind that I used Anaconda with the ffmpeg package installed to run this piece of code. It might not always work in a straightforward way with other python distributions although I believe there are workarounds.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Aaron Bracke