web-dev-qa-db-fra.com

Pouvez-vous "diffuser" des images sur ffmpeg pour construire une vidéo, au lieu de les enregistrer sur le disque?

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.

42
Brandon

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.

55
Marwan Alsabbagh

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