I would like to create an h264 or divx movie from frames that I generate in a python script in matplotlib. There are about 100k frames in this movie.
In examples on the web [eg. 1], I have only seen the method of saving each frame as a png and then running mencoder or ffmpeg on these files. In my case, saving each frame is impractical. Is there a way to take a plot generated from matplotlib and pipe it directly to ffmpeg, generating no intermediate files?
Programming with ffmpeg's C-api is too difficult for me [eg. 2]. Also, I need an encoding that has good compression such as x264 as the movie file will otherwise be too large for a subsequent step. So it would be great to stick with mencoder/ffmpeg/x264.
Is there something that can be done with pipes ?
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 animation import numpy as np from pylab import * dpi = 100 def 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 subprocess import numpy as np import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt outf = 'test.avi' rate = 1 cmdstring = ('local/bin/ffmpeg', '-r', '%d' % rate, '-f','image2pipe', '-vcodec', 'png', '-i', 'pipe:', outf ) p = subprocess.Popen(cmdstring, stdin=subprocess.PIPE) plt.figure() frames = 10 for 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