2012-11-08 9 views
34

Moja praca polega ostatnio na programowym tworzeniu filmów. W Pythonie, typowy przepływ pracy wygląda tak:Czy możesz "przesyłać" obrazy do pliku ffmpeg, aby skonstruować wideo, zamiast zapisywać je na dysku?

import subprocess, Image, ImageDraw 

for i in range(frames_per_second * video_duration_seconds): 
    img = createFrame(i) 
    img.save("%07d.png" % i) 

subprocess.call(["ffmpeg","-y","-r",str(frames_per_second),"-i", "%07d.png","-vcodec","mpeg4", "-qscale","5", "-r", str(frames_per_second), "video.avi"]) 

Ten workflow tworzy obraz dla każdej ramki w wideo i zapisuje je na dysku. Po zapisaniu wszystkich obrazów, ffmpeg jest wywoływane w celu skonstruowania wideo ze wszystkich obrazów.

Zapisywanie obrazów na dysk (bez tworzenia obrazów w pamięci) powoduje zużycie większości cykli i nie wydaje się konieczne. Czy istnieje sposób na wykonanie tej samej funkcji, ale bez zapisywania obrazów na dysk? Tak więc ffmpeg zostanie wywołany, a obrazy zostaną skonstruowane i dostarczone do ffmpeg natychmiast po skonstruowaniu.

+8

Nie wiem, w jaki sposób tworzysz obrazy, ale ffmpeg akceptuje również wejścia do rur: 'ffmpeg -f image2pipe -c: v png -r 30000/1001 -i -'. – LordNeckbeard

+0

Dla uproszczenia wystarczy założyć, że 'createFrame (i)' zwraca obiekt obrazu Python Image Library, który przechowujemy w 'img'. Myślę, że twoja odpowiedź jest krokiem we właściwym kierunku, ale połowa wyzwania polegałaby na przesuwaniu skonstruowanych obrazów do ffmpeg podczas pracy w programie Pythona. – Brandon

+0

może kolejki, a następnie potok obrazów przez drugi wątek? – lolopop

Odpowiedz

49

Ok Mam to działa. dzięki sugestii LordNeckbeard do korzystania z image2pipe. Musiałem użyć kodowania jpg zamiast png because image2pipe with png doesn't work on my verision of ffmpeg. Pierwszy skrypt jest zasadniczo taki sam, jak kod twojego pytania, z wyjątkiem zaimplementowanego prostego tworzenia obrazu, który tworzy obrazy zmieniające się z czarnego na czerwony. Dodałem też kod do czasu wykonania.

wykonanie seryjny

import subprocess, Image 

fps, duration = 24, 100 
for i in range(fps * duration): 
    im = Image.new("RGB", (300, 300), (i, 1, 1)) 
    im.save("%07d.jpg" % i) 
subprocess.call(["ffmpeg","-y","-r",str(fps),"-i", "%07d.jpg","-vcodec","mpeg4", "-qscale","5", "-r", str(fps), "video.avi"]) 

równoległe wykonanie (bez obrazów zapisanych na dysku)

import Image 
from subprocess import Popen, PIPE 

fps, duration = 24, 100 
p = Popen(['ffmpeg', '-y', '-f', 'image2pipe', '-vcodec', 'mjpeg', '-r', '24', '-i', '-', '-vcodec', 'mpeg4', '-qscale', '5', '-r', '24', 'video.avi'], stdin=PIPE) 
for i in range(fps * duration): 
    im = Image.new("RGB", (300, 300), (i, 1, 1)) 
    im.save(p.stdin, 'JPEG') 
p.stdin.close() 
p.wait() 

wyniki są interesujące, wpadłem każdy scenariusz 3 razy, aby porównać wydajność: serial:

12.9062321186 
12.8965060711 
12.9360799789 

równolegle:

8.67797684669 
8.57139396667 
8.38926696777 

Więc wydaje się, że wersja równoległa jest szybsza o 1,5 razy szybciej.

+0

Genialny! Wypróbowałem to i działa cudownie. – Brandon

+16

Dla każdego, kto natknie się na to w przyszłości, zastąpienie "mjpeg" przez "png" i "JPEG" z "PNG" działało dla mnie, aby użyć png. – Brandon

+0

Udało mi się uzyskać najlepszą jakość przy użyciu '-vcodec png' i' im.save (p.stdin, 'PNG') 'chociaż rozmiar pliku to x4 – bluesummers

Powiązane problemy