Frage

Ich möchte ein h264 oder Divx Film von Rahmen schaffen, dass ich in einem Python-Skript in matplotlib erzeugen. Es gibt etwa 100 k Frames in diesem Film.

In den Beispielen im Web [z. 1], ich habe nur die Methode des Sparens jedes Rahmens als png gesehen und dann läuft mencoder oder ffmpeg auf diese Dateien. In meinem Fall jeden Frame Speicher ist nicht praktikabel. Gibt es eine Möglichkeit, ein Grundstück von matplotlib und Rohr erzeugt nehmen sie direkt an ffmpeg, zu erzeugen keine Zwischendateien?

Programmierung mit ffmpeg des C-api ist zu schwierig für mich [z. 2]. Außerdem brauche ich eine Codierung, die gute Kompression wie x264 hat wie die Filmdatei sonst zu groß sein wird für einen nachfolgenden Schritt. So wäre es toll, zu halten mit mencoder / ffmpeg / x264.

Gibt es etwas, das mit Rohren durchgeführt werden kann [3]?

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

[2] Wie kann man codiert eine Reihe von Bildern in H264 den x264 C-API?

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

War es hilfreich?

Lösung

Diese Funktionalität ist jetzt (zumindest ab 1.2.0, vielleicht 1.1) gebacken in matplotlib über die MovieWriter Klasse und die Unterklassen im animation Modul. Sie müssen auch ffmpeg im Voraus installieren.

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

Dokumentation für animation

Andere Tipps

(Siehe Joe Kington Kommentare zu meiner Frage)

Nach ffmpeg Patchen, ich war in der Lage zu bekommen kochend png ffmpeg wie folgt:

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')

Es würde nicht funktionieren ohne die Patch , die trivialerweise modifiziert zwei Dateien und fügt libavcodec/png_parser.c. Ich musste manuell den Patch libavcodec/Makefile anzuwenden. Schließlich entfernte ich ‚-Nummer‘ von Makefile die man-Seiten zu bauen zu bekommen. Mit Kompilierungsoptionen,

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

Umwandlung in Bildformaten ist ziemlich langsam und fügt Abhängigkeiten. Nachdem auf dieser Seite schauen und ich habe es funktioniert rohe uncodiert Puffer mit mencoder (ffmpeg Lösung noch gesucht) verwendet wird.

Details unter: 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()

Ich habe ein paar schöne speedups.

Diese sind alle wirklich großen Antworten. Hier ist ein weiterer Vorschlag. @ User621442 ist richtig, dass der Engpass typischerweise das Schreiben des Bildes ist, also wenn Sie PNG-Dateien auf Ihren Video-Kompressor schreiben, wird es ziemlich langsam sein (auch wenn man sich durch ein Rohr statt Schreiben auf der Festplatte senden). Ich fand eine Lösung reine ffmpeg verwenden, die ich persönlich leichter finden zu verwenden, als matplotlib.animation oder mencoder.

Auch in meinem Fall wollte ich nur speichern Sie das Bild in einer Achse, anstatt alle der Zecke Labels, Abbildung Titel, Abbildung Hintergrund speichern usw. Im Grunde ich einen Film / Animation mit matplotlib Code machen wollte, aber haben es nicht „aussehen wie ein graph“. Ich habe enthalten diesen Code hier, aber Sie Standard-Grafiken und Rohr machen können sie stattdessen ffmpeg, wenn Sie wollen.

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()

Das ist großartig! Ich wollte das Gleiche tun. Aber ich konnte nie die gepatchte ffmpeg Quelle (0.6.1) in Vista mit MingW32 + MSYS kompilieren + pr Umgebung ... png_parser.c produziert Error1 während der Kompilierung.

So kam ich mit einer jpeg Lösung dieses mit PIL auf. Legen Sie einfach Ihre ffmpeg.exe im selben Ordner wie das Skript. Dies sollte ohne den Patch unter Windows mit ffmpeg arbeiten. Ich hatte stdin.write Methode zu verwenden, anstatt das Verfahren mit, die in der offiziellen Dokumentation über subprocess empfohlen. Beachten Sie, dass die zweite -vcodec Option gibt die Codierung Codec. Das Rohr wird durch p.stdin.close geschlossen ().

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()

Hier ist eine modifizierte Version von @tacaswell ‚s Antwort. Verändert die folgende:

  1. Sie nicht benötigen die pylab Abhängigkeit
  2. Fix mehrere Orte, S. T. Diese Funktion ist direkt ausführbar. (Das Original kann nicht sein copy-and-paste-and-run direkt und müssen an mehreren Stellen beheben.)

Vielen Dank für @tacaswell ist wunderbar Antwort !!!

def ani_frame():
    def gen_frame():
        return np.random.rand(300, 300)

    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(gen_frame(), cmap='gray', interpolation='nearest')
    im.set_clim([0, 1])
    fig.set_size_inches([5, 5])

    plt.tight_layout()

    def update_img(n):
        tmp = gen_frame()
        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=72)
    return ani
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top