2015-06-21 24 views
2

在Raspbian(树莓派2),下面的小例子,从我的脚本剥离正确生成的MP4文件:提速matplotlib动画视频文件

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

def anim_lift(x, y): 

    #set up the figure 
    fig = plt.figure(figsize=(15, 9)) 

    def animate(i): 
     # update plot 
     pointplot.set_data(x[i], y[i]) 

     return pointplot 

    # First frame 
    ax0 = plt.plot(x,y) 
    pointplot, = ax0.plot(x[0], y[0], 'or') 

    anim = animation.FuncAnimation(fig, animate, repeat = False, 
            frames=range(1,len(x)), 
            interval=200, 
            blit=True, repeat_delay=1000) 

    anim.save('out.mp4') 
    plt.close(fig) 

# Number of frames 
nframes = 200 

# Generate data 
x = np.linspace(0, 100, num=nframes) 
y = np.random.random_sample(np.size(x)) 

anim_lift(x, y) 

现在,生产以优良的品质和非常小的文件文件大小,但制作170帧电影需要15分钟,这对于我的应用程序来说是不可接受的。我正在寻找一个显着的加速,视频文件大小的增加不是问题。

我相信视频制作中的瓶颈是暂时保存png格式的帧。在处理过程中,我可以看到png文件在我的工作目录中,只有25%的CPU负载。

请提出一个解决方案,那也可能基于不同的包,而不是简单地matplotlib.animation,像OpenCVmoviepy(这无论如何已经在我的项目进口)。

版本中使用:

  • 蟒蛇2.7.3
  • matplotlib 1.1.1rc2
  • 的ffmpeg 0.8.17-6:0.8.17-1 + rpi1

回答

0

你应该能够使用其中一个将正确流入ffmpeg的作家,但是其他一些作品会出错。

import matplotlib.pyplot as plt 
from matplotlib import animation 


def anim_lift(x, y): 

    #set up the figure 
    fig, ax = plt.subplots(figsize=(15, 9)) 

    def animate(i): 
     # update plot 
     pointplot.set_data(x[i], y[i]) 

     return [pointplot, ] 

    # First frame 
    pointplot, = ax.plot(x[0], y[0], 'or') 
    ax.set_xlim([0, 200]) 
    ax.set_ylim([0, 200]) 
    anim = animation.FuncAnimation(fig, animate, repeat = False, 
            frames=range(1,len(x)), 
            interval=200, 
            blit=True, repeat_delay=1000) 

    anim.save('out.mp4') 
    plt.close(fig) 


x = list(range(170)) 
y = list(range(170)) 
anim_lift(x, y) 

保存此为test.py(这是我不认为你的代码的清理版本实际上运行,因为plt.plot回报的Line2D对象和列表的列表没有plot法)给出了:

(dd_py3k) ✔ /tmp 
14:45 $ time python test.py 

real 0m7.724s 
user 0m9.887s 
sys  0m0.547s 
+0

感谢您的纠正,实际上我的版本运行得益于'pointplot [0]',但您的版本更干净,我将相应地编辑原始版本。 – gaggio

+0

无论如何,速度的问题仍然存在......您是否注意到您的程序在工作目录中生成临时png文件?我认为你的版本也运行在raspberry pi 2,raspbian发行版上?也许这在我原来的问题中还不够清楚。 – gaggio

1

一个大大改进的解决方案是基于对this post的答案将时间缩短约10倍。

import numpy as np 
import matplotlib.pylab as plt 
import matplotlib.animation as animation 
import subprocess 

def testSubprocess(x, y): 

    #set up the figure 
    fig = plt.figure(figsize=(15, 9)) 
    canvas_width, canvas_height = fig.canvas.get_width_height() 

    # First frame 
    ax0 = plt.plot(x,y) 
    pointplot, = plt.plot(x[0], y[0], 'or') 

    def update(frame): 
     # your matplotlib code goes here 
     pointplot.set_data(x[frame],y[frame]) 

    # Open an ffmpeg process 
    outf = 'testSubprocess.mp4' 
    cmdstring = ('ffmpeg', 
       '-y', '-r', '1', # overwrite, 1fps 
       '-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 frames and write to the pipe 
    for frame in range(nframes): 
     # draw the frame 
     update(frame) 
     fig.canvas.draw() 

     # extract the image as an ARGB string 
     string = fig.canvas.tostring_argb() 

     # write to pipe 
     p.stdin.write(string) 

    # Finish up 
    p.communicate() 

# Number of frames 
nframes = 200 

# Generate data 
x = np.linspace(0, 100, num=nframes) 
y = np.random.random_sample(np.size(x)) 

testSubprocess(x, y) 

我怀疑进一步加速可能类似地通过管道将原始图像数据到gstreamer的这是现在能够从在树莓裨硬件编码受益来获得,见this discussion

+0

这非常有帮助。 writer类使用'fig.savefig()'保存到非常慢的管道。 – Wang

1

将动画保存到文件的瓶颈在于使用figure.savefig()。这是matplotlib的FFMpegWriter的自制子类,受gaggio的回答启发。它不使用savefig(因此忽略savefig_kwargs),但只需对动画脚本的更改进行最小限度的更改。

from matplotlib.animation import FFMpegWriter 

class FasterFFMpegWriter(FFMpegWriter): 
    '''FFMpeg-pipe writer bypassing figure.savefig.''' 
    def __init__(self, **kwargs): 
     '''Initialize the Writer object and sets the default frame_format.''' 
     super().__init__(**kwargs) 
     self.frame_format = 'argb' 

    def grab_frame(self, **savefig_kwargs): 
     '''Grab the image information from the figure and save as a movie frame. 

     Doesn't use savefig to be faster: savefig_kwargs will be ignored. 
     ''' 
     try: 
      # re-adjust the figure size and dpi in case it has been changed by the 
      # user. We must ensure that every frame is the same size or 
      # the movie will not save correctly. 
      self.fig.set_size_inches(self._w, self._h) 
      self.fig.set_dpi(self.dpi) 
      # Draw and save the frame as an argb string to the pipe sink 
      self.fig.canvas.draw() 
      self._frame_sink().write(self.fig.canvas.tostring_argb()) 
     except (RuntimeError, IOError) as e: 
      out, err = self._proc.communicate() 
      raise IOError('Error saving animation to file (cause: {0}) ' 
         'Stdout: {1} StdError: {2}. It may help to re-run ' 
         'with --verbose-debug.'.format(e, out, err)) 

我能够在一半的时间来制作动画或小于使用默认FFMpegWriter

您可以使用is,如this example中所述。