Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

matplotlib animation: write to png files without third party module

The animation module in matplotlib usually requires third party modules like FFmpeg, mencoder or imagemagik to be able to save the animation to a file (e.g. here: https://stackoverflow.com/a/25143651/5082048).

Even the MovieWriter class in matplotlib seems to be build in a way that third party modules will be incorporated (starting and closing processes, cummunicating via pipe): http://matplotlib.org/api/animation_api.html#matplotlib.animation.MovieWriter.

I am looking for a way, how I can save a matplotlib.animation.FuncAnimation object frame to frame to png - directly, within python. Afterwards, I want to show the .png files as animation in an iPython notebook using this approach: https://github.com/PBrockmann/ipython_animation_javascript_tool/

Therefore my questions are:

  • How can I save an matplotlib.animation.FuncAnimation object directly to .png files without the need to use third party modules?
  • Is there a writer class implemented for this usecase?
  • How can I get figure objects frame by frame out of the FuncAnimation object (so that I could save them myself)?

Edit: The matplotlib.animation.FuncAnimation object is given, the task is to save it's frames using pure Python. Unfortunately, I cannot change the underlying animation function as ImportanceOfBeingErnest suggested.

like image 535
Arco Bast Avatar asked Jan 04 '23 21:01

Arco Bast


2 Answers

Althoough this may seem a bit complicated, saving the frames of an animation may be easily done within the animation itself.

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

def animate(i):
    line.set_ydata(np.sin(2*np.pi*i / 50)*np.sin(x))
    #fig.canvas.draw() not needed see comment by @tacaswell
    plt.savefig(str(i)+".png")
    return line,

fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1,1)
x = np.linspace(0, 2*np.pi, 200)
line, = ax.plot(x, np.zeros_like(x))
plt.draw()

ani = matplotlib.animation.FuncAnimation(fig, animate, frames=5, repeat=False)
plt.show()

Mind the repeat = False argument, which will prevent the animation to run continuously and repeat writing the same files to disk.

Note that if you're willing to loosen the restriction of "no external package" you may use imagemagick to save the pngs

ani.save("anim.png", writer="imagemagick")

which will save files anim-1.png, anim-2.png etc.

Finally note that there are of course easier methods to show an animation in a jupyter notebook.

like image 59
ImportanceOfBeingErnest Avatar answered Jan 13 '23 14:01

ImportanceOfBeingErnest


You want to look at the FileMovieWriter sub-classes (See http://matplotlib.org/2.0.0rc2/api/animation_api.html#writer-classes) You probably want to sub-class FileMoveWriter, something like

import matplotlib.animation as ma


class BunchOFiles(ma.FileMovieWriter):
    def setup(self, fig, dpi, frame_prefix):
        super().setup(fig, dpi, frame_prefix, clear_temp=False)

    def _run(self):
        # Uses subprocess to call the program for assembling frames into a
        # movie file.  *args* returns the sequence of command line arguments
        # from a few configuration options.
        pass

    def grab_frame(self, **savefig_kwargs):
        '''
        Grab the image information from the figure and save as a movie frame.
        All keyword arguments in savefig_kwargs are passed on to the 'savefig'
        command that saves the figure.
        '''

        # Tell the figure to save its data to the sink, using the
        # frame format and dpi.
        with self._frame_sink() as myframesink:
            self.fig.savefig(myframesink, format=self.frame_format,
                             dpi=self.dpi, **savefig_kwargs)

    def cleanup(self):
        # explictily skip a step in the mro
        ma.MovieWriter.cleanup(self)

(this is not tested, might be better to just implement a class that implements saving, grab_frame, finished, and setup)

like image 27
tacaswell Avatar answered Jan 13 '23 14:01

tacaswell