从python生成电影而不将单个帧保存到文件

2024-04-29 01:07:51 发布

您现在位置:Python中文网/ 问答频道 /正文

我想用matplotlib中的python脚本生成的帧创建一个h264或divx电影。这部电影大约有10万帧。

在web上的示例中[例如1],我只看到了将每个帧保存为png,然后在这些文件上运行mencoder或ffmpeg的方法。在我看来,保存每一帧都是不切实际的。有没有办法获取从matplotlib生成的绘图并将其直接管道到ffmpeg,而不生成中间文件?

用ffmpeg的C-api编程对我来说太难了[例2]。另外,我需要一个压缩良好的编码,比如x264,因为电影文件对于后续步骤来说太大了。因此,坚持使用mencoder/ffmpeg/x264会很好。

有什么可以用管子做的吗?

[1]http://matplotlib.sourceforge.net/examples/animation/movie_demo.html

[2]How does one encode a series of images into H264 using the x264 C API?

[3]http://www.ffmpeg.org/ffmpeg-doc.html#SEC41


Tags: 文件方法脚本webhttp示例电影png
2条回答

在修补了ffmpeg(请参阅Joe Kington对我的问题的评论)之后,我能够将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')

如果没有patch,它将无法工作,后者只需简单地修改两个文件并添加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解决方案)使用原始的未编码缓冲区进行工作。

详情见: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()

我有一些不错的加速。

这个功能现在(至少从1.2.0开始,可能是1.1)通过MovieWriter类和animation模块中的子类烘焙到matplotlib中。您还需要提前安装ffmpeg

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

Documentation for ^{}

相关问题 更多 >