web-dev-qa-db-fra.com

Transférer des images OpenCV brutes dans FFmpeg

Voici un exemple assez simple de lecture d'une webcam en utilisant les liaisons python d'OpenCV:

'''capture.py'''
import cv, sys
cap = cv.CaptureFromCAM(0)                    # 0 is for /dev/video0
while True :
    if not cv.GrabFrame(cap) : break
    frame = cv.RetrieveFrame(cap)
    sys.stdout.write( frame.tostring() )

Maintenant, je veux diriger la sortie vers ffmpeg comme suit:

$ python capture.py | ffmpeg -f image2pipe -pix_fmt bgr8 -i - -s 640x480 foo.avi

Malheureusement, je ne peux pas obtenir l'incantation magique ffmpeg tout à fait juste et ça échoue avec

 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 
 Libavfilter 1.19. 0/1.19. 0 
 Libswscale 0.11. 0/0,11. 0 
 Libpostproc 51. 2. 0/51. 2. 0 
 Sortie # 0, avi, vers 'out.avi': 
 Flux # 0.0: Vidéo: flv, yuv420p , 640x480, q = 2-31, 19660 kb/s, 90 ktbn, 30 tbc 
 [Image2pipe @ 0x1508640] max_analyze_duration atteint 
 [Image2pipe @ 0x1508640] Estimation de la durée à partir du débit, cela peut être inexact 
 Entrée # 0, image2pipe, à partir de 'pipe:': 
 Durée: N/A, débit binaire: N/A 
 Flux # 0.0: Vidéo: 0x0000, bgr8, 25 fps , 25 tbr, 25 tbn, 25 tbc 
 SwScaler: 0x0 -> 640x480 est une dimension d'échelle non valide 
  • Les images capturées sont définitivement 640x480.
  • Je suis presque sûr que l'ordre des pixels pour le type d'image OpenCV (IplImage) est GBR, un octet par canal. Au moins, c'est ce qui semble sortir de la caméra.

Je ne suis pas un gourou ffmpeg. Quelqu'un at-il fait cela avec succès?

24
BrianTheLion

J'ai pris un peu de violon, mais je l'ai compris en utilisant le démultiplexeur FFmpeg rawvideo :

python capture.py | ffmpeg -f rawvideo -pixel_format bgr24 -video_size 640x480 -framerate 30 -i - foo.avi

Comme il n'y a pas d'en-tête dans la vidéo brute spécifiant les paramètres vidéo supposés, l'utilisateur doit les spécifier pour pouvoir décoder les données correctement:

  • -framerate Définissez la fréquence d'images vidéo d'entrée. La valeur par défaut est 25.
  • -pixel_format Définit le format de pixel de la vidéo d'entrée. La valeur par défaut est yuv420p.
  • -video_size Définissez la taille de la vidéo d'entrée. Il n'y a pas de valeur par défaut, cette valeur doit donc être spécifiée explicitement.

Et voici un petit quelque chose en plus pour les utilisateurs avec pouvoir. Même chose mais en utilisant VLC pour diffuser la sortie en direct sur le Web, au format Flash:

python capture.py | cvlc --demux=rawvideo --rawvid-fps=30 --rawvid-width=320 --rawvid-height=240  --rawvid-chroma=RV24 - --sout "#transcode{vcodec=h264,vb=200,fps=30,width=320,height=240}:std{access=http{mime=video/x-flv},mux=ffmpeg{mux=flv},dst=:8081/stream.flv}"

Edit: Créer un flux webm en utilisant ffmpeg et ffserver

python capture.py | ffmpeg -f rawvideo -pixel_format rgb24 -video_size 640x480 -framerate 25 -i - http://localhost:8090/feed1.ffm
31
BrianTheLion

Je suis un peu en retard, mais mon puissant VidGear Python Bibliothèque automatise le processus de traitement en pipeline des trames OpenCV dans FFmpeg sur n’importe quelle plate-forme . Voici un exemple de base de python:

# import libraries
from vidgear.gears import WriteGear
import cv2

output_params = {"-vcodec":"libx264", "-crf": 0, "-preset": "fast"} #define (Codec,CRF,preset) FFmpeg Tweak parameters for writer

stream = cv2.VideoCapture(0) #Open live webcam video stream on first index(i.e. 0) device

writer = WriteGear(output_filename = 'Output.mp4', compression_mode = True, logging = True, **output_params) #Define writer with output filename 'Output.mp4' 

# infinite loop
while True:

    (grabbed, frame) = stream.read()
    # read frames

    # check if frame empty
    if not is grabbed:
        #if True break the infinite loop
        break


    # {do something with frame here}
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # write a modified frame to writer
        writer.write(gray) 

        # Show output window
    cv2.imshow("Output Frame", frame)

    key = cv2.waitKey(1) & 0xFF
    # check for 'q' key-press
    if key == ord("q"):
        #if 'q' key-pressed break out
        break

cv2.destroyAllWindows()
# close output window

stream.release()
# safely close video stream
writer.close()
# safely close writer

Source: https://github.com/abhiTronix/vidgear/wiki/Compression-Mode:-FFmpeg#2-writegear-apicompression-mode-with-opencv-directly

Vous pouvez consulter VidGear Docs pour des applications et des fonctionnalités plus avancées.

J'espère que cela pourra aider!

2
AbhiTronix

Il m'a fallu une heure pour comprendre que, par défaut, les pipes Windows ne sont pas binaires. Cela entraîne la modification/l’omission de certains octets (en particulier les nouvelles lignes) et la vidéo résultante se déplace lentement car la taille de l’image n’est pas constante.

Pour résoudre ce problème, le fichier python modifié:

"""
videoCapture.py
"""
import cv2, sys
import time

if sys.platform == "win32":
    import os, msvcrt
    msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)

cap = cv2.VideoCapture(0)                    # 0 is for /dev/video0
while True :
    ret, frm = cap.read()
    sys.stdout.write( frm.tostring() )

Pour vérifier si le traitement de la vidéo brute est réussi, utilisez ffplay. Assurez-vous de spécifier un débit d'image plus élevé que ce qui provient du canal, sinon la vidéo commencera à prendre du retard.

python videoCapture.py | ffplay -f rawvideo -pix_fmt bgr24 -s 640x480 -framerate 40 -i -
1
hgabe

Je ne sais pas si c'est spécifique à Mac OS ou à python3, mais je devais convertir le cadre en chaîne pour que cela fonctionne pour moi, comme ceci:

sys.stdout.write(str(frame.tostring()))
0
Matt W