从python生成电影,而不保存个别帧到文件
我想从我在matplotlib中的python脚本中生成的帧创build一个h264或divx电影。 这部电影中大约有10万帧。
在networking上的例子[例如。 1],我只看到了将每个帧保存为png,然后在这些文件上运行mencoder或ffmpeg的方法。 就我而言,保存每一帧是不切实际的。 有没有办法从matplotlib生成的阴谋,并直接pipe道到ffmpeg,不生成中间文件?
用ffmpeg的C-API编程对我来说太困难了[例如。 2]。 此外,我需要一个具有良好的压缩,如x264的编码,因为电影文件否则将是一个后续步骤太大。 所以,坚持使用mencoder / ffmpeg / x264会很好。
有没有什么可以用pipe道做的[3]?
[1] http://matplotlib.sourceforge.net/examples/animation/movie_demo.html
[2] 如何使用x264 C API将一系列图像编码到H264中?
[3] http://www.ffmpeg.org/ffmpeg-doc.html#SEC41
这个function现在(至less1.2.0,也许是1.1)通过MovieWriter
类MovieWriter
入matplotlib中,并且它是animation
模块中的子类。
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
文件的animation
修补ffmpeg后(请参阅Joe Kington对我的问题的评论),我可以通过以下方式获取pipe道png的ffmpeg:
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')
如果没有修补程序 ,它将不起作用, 修补程序会修改两个文件并添加libavcodec/png_parser.c
。 我不得不手动将补丁应用到libavcodec/Makefile
。 最后,我从Makefile
删除了“-number”来获取手册页。 通过编译选项,
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
转换为图像格式非常慢,并增加了依赖性。 看了这些页面之后,我使用mencoder(ffmpeg解决scheme仍然需要)使用原始的未编码缓冲区。
详细信息: http : //vokicodder.blogspot.com/2011/02/numpy-arrays-to-video.html
import subprocess import numpy as np class 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()
我有一些不错的加速。
这很棒! 我也想这样做。 但是,我永远不能在Vista中使用MingW32 + MSYS + pr环境编译修补过的ffmpeg源文件(0.6.1)… png_parser.c在编译期间生成了Error1。
所以,我想出了一个使用PIL的jpeg解决scheme。 只需将您的ffmpeg.exe放在与此脚本相同的文件夹中即可。 这应该与ffmpeg没有在Windows下的补丁。 我必须使用stdin.write方法,而不是正式文档中有关subprocess推荐的通信方法。 请注意,第二个-vcodec选项指定了编码编解码器。 pipe道被p.stdin.close()closures。
import subprocess import numpy as np from PIL import Image rate = 1 outf = 'test.avi' cmdstring = ('ffmpeg.exe', '-y', '-r', '%d' % rate, '-f','image2pipe', '-vcodec', 'mjpeg', '-i', 'pipe:', '-vcodec', 'libxvid', outf ) p = subprocess.Popen(cmdstring, stdin=subprocess.PIPE, shell=False) for i in range(10): im = Image.fromarray(np.uint8(np.random.randn(100,100))) p.stdin.write(im.tostring('jpeg','L')) #p.communicate(im.tostring('jpeg','L')) p.stdin.close()
这些都是非常好的答案。 这是另一个build议。 @ user621442是正确的,瓶颈通常是图像的写入,所以如果你正在写png文件到你的video压缩器,它会很慢(即使你是通过pipe道发送它们而不是写入磁盘)。 我find了一个使用纯ffmpeg的解决scheme,我个人觉得比matplotlib.animation或mencoder更容易使用。
此外,在我的情况下,我想只保存在一个轴的图像,而不是保存所有的刻度标签,graphics标题,graphics背景等。基本上我想用matplotlib代码制作电影/animation,但没有它“看起来像一个图表”。 我已经在这里包含了这个代码 ,但是如果你愿意的话,你可以制作标准的graphics并将它们传递给ffmpeg。
import matplotlib.pyplot as plt import subprocess # create a figure window that is the exact size of the image # 400x500 pixels in my case # don't draw any axis stuff ... thanks to @Joe Kington for this trick # https://stackoverflow.com/questions/14908576/how-to-remove-frame-from-matplotlib-pyplot-figure-vs-matplotlib-figure-frame f = plt.figure(frameon=False, figsize=(4, 5), dpi=100) canvas_width, canvas_height = f.canvas.get_width_height() ax = f.add_axes([0, 0, 1, 1]) ax.axis('off') def update(frame): # your matplotlib code goes here # Open an ffmpeg process outf = 'ffmpeg.mp4' cmdstring = ('ffmpeg', '-y', '-r', '30', # overwrite, 30fps '-s', '%dx%d' % (canvas_width, canvas_height), # size of image string '-pix_fmt', 'argb', # format '-f', 'rawvideo', '-i', '-', # tell ffmpeg to expect raw video from the pipe '-vcodec', 'mpeg4', outf) # output encoding p = subprocess.Popen(cmdstring, stdin=subprocess.PIPE) # Draw 1000 frames and write to the pipe for frame in range(1000): # draw the frame update(frame) plt.draw() # extract the image as an ARGB string string = f.canvas.tostring_argb() # write to pipe p.stdin.write(string) # Finish up p.communicate()