Generating movie from python without saving individual frames to files
This functionality is now (at least as of 1.2.0, maybe 1.1) baked into matplotlib via the MovieWriter
class and it's sub-classes in the animation
module. You also need to install ffmpeg
in advance.
import matplotlib.animation as animationimport numpy as npfrom pylab import *dpi = 100def ani_frame(): fig = plt.figure() ax = fig.add_subplot(111) ax.set_aspect('equal') ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) im = ax.imshow(rand(300,300),cmap='gray',interpolation='nearest') im.set_clim([0,1]) fig.set_size_inches([5,5]) tight_layout() def update_img(n): tmp = rand(300,300) im.set_data(tmp) return im #legend(loc=0) ani = animation.FuncAnimation(fig,update_img,300,interval=30) writer = animation.writers['ffmpeg'](fps=30) ani.save('demo.mp4',writer=writer,dpi=dpi) return ani
After patching ffmpeg (see Joe Kington comments to my question), I was able to get piping png's to ffmpeg as follows:
import subprocessimport numpy as npimport matplotlibmatplotlib.use('Agg')import matplotlib.pyplot as pltoutf = 'test.avi'rate = 1cmdstring = ('local/bin/ffmpeg', '-r', '%d' % rate, '-f','image2pipe', '-vcodec', 'png', '-i', 'pipe:', outf )p = subprocess.Popen(cmdstring, stdin=subprocess.PIPE)plt.figure()frames = 10for i in range(frames): plt.imshow(np.random.randn(100,100)) plt.savefig(p.stdin, format='png')
It would not work without the patch, which trivially modifies two files and adds libavcodec/png_parser.c
. I had to manually apply the patch to libavcodec/Makefile
. Lastly, I removed '-number' from Makefile
to get the man pages to build. With compile options,
FFmpeg version 0.6.1, Copyright (c) 2000-2010 the FFmpeg developers built on Nov 30 2010 20:42:02 with gcc 4.2.1 (Apple Inc. build 5664) configuration: --prefix=/Users/paul/local_test --enable-gpl --enable-postproc --enable-swscale --enable-libxvid --enable-libx264 --enable-nonfree --mandir=/Users/paul/local_test/share/man --enable-shared --enable-pthreads --disable-indevs --cc=/usr/bin/gcc-4.2 --arch=x86_64 --extra-cflags=-I/opt/local/include --extra-ldflags=-L/opt/local/lib libavutil 50.15. 1 / 50.15. 1 libavcodec 52.72. 2 / 52.72. 2 libavformat 52.64. 2 / 52.64. 2 libavdevice 52. 2. 0 / 52. 2. 0 libswscale 0.11. 0 / 0.11. 0 libpostproc 51. 2. 0 / 51. 2. 0
Converting to image formats is quite slow and adds dependencies. After looking at these page and other I got it working using raw uncoded buffers using mencoder (ffmpeg solution still wanted).
Details at: http://vokicodder.blogspot.com/2011/02/numpy-arrays-to-video.html
import subprocessimport numpy as npclass VideoSink(object) : def __init__( self, size, filename="output", rate=10, byteorder="bgra" ) : self.size = size cmdstring = ('mencoder', '/dev/stdin', '-demuxer', 'rawvideo', '-rawvideo', 'w=%i:h=%i'%size[::-1]+":fps=%i:format=%s"%(rate,byteorder), '-o', filename+'.avi', '-ovc', 'lavc', ) self.p = subprocess.Popen(cmdstring, stdin=subprocess.PIPE, shell=False) def run(self, image) : assert image.shape == self.size self.p.stdin.write(image.tostring()) def close(self) : self.p.stdin.close()
I got some nice speedups.