Mon travail consiste récemment à créer des vidéos par programmation. En python, le flux de travail typique ressemble à ceci:
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"])
Ce flux de travail crée une image pour chaque image de la vidéo et l'enregistre sur le disque. Une fois toutes les images enregistrées, ffmpeg est appelé pour construire une vidéo à partir de toutes les images.
L'enregistrement des images sur le disque (et non la création des images en mémoire) consomme ici la majorité des cycles et ne semble pas nécessaire. Existe-t-il un moyen d'effectuer la même fonction, mais sans enregistrer les images sur le disque? Ainsi, ffmpeg serait appelé et les images seraient construites et transmises à ffmpeg immédiatement après avoir été construites.
Ok je l'ai fait fonctionner. grâce à la suggestion de LordNeckbeard d'utiliser image2pipe. J'ai dû utiliser l'encodage jpg au lieu de png car image2pipe avec png ne fonctionne pas sur ma version de ffmpeg . Le premier script est essentiellement le même que le code de votre question, sauf que j'ai implémenté une création d'image simple qui crée simplement des images allant du noir au rouge. J'ai également ajouté du code pour chronométrer l'exécution.
exécution en série
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"])
exécution parallèle (sans images enregistrées sur le disque)
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()
les résultats sont intéressants, j'ai exécuté chaque script 3 fois pour comparer les performances: série:
12.9062321186
12.8965060711
12.9360799789
parallèle:
8.67797684669
8.57139396667
8.38926696777
Il semble donc que la version parallèle soit plus rapide environ 1,5 fois plus rapide.
imageio supporte cela directement. Il utilise FFMPEG et Video Acceleration API , ce qui le rend très rapide:
import imageio
writer = imageio.get_writer('video.avi', fps=fps)
for i in range(frames_per_second * video_duration_seconds):
img = createFrame(i)
writer.append_data(img)
writer.close()