web-dev-qa-db-fra.com

Comment analyser le flux http mjpeg depuis une caméra IP?

Ci-dessous, le code écrit pour obtenir le flux en direct d'une caméra IP.

from cv2 import *
from cv2 import cv
import urllib
import numpy as np
k=0
capture=cv.CaptureFromFile("http://IPADDRESS of the camera/axis-cgi/mjpg/video.cgi")
namedWindow("Display",1)

while True:
    frame=cv.QueryFrame(capture)
    if frame is None:
        print 'Cam not found'
        break
    else:
        cv.ShowImage("Display", frame)
    if k==0x1b:
        print 'Esc. Exiting'
        break

Lors de l'exécution du code, la sortie que j'obtiens est:

Cam not found

Où est-ce que je vais mal? Aussi, pourquoi le cadre None est-il ici? Y a-t-il un problème avec la conversion?

25
import cv2
import urllib 
import numpy as np

stream = urllib.urlopen('http://localhost:8080/frame.mjpg')
bytes = ''
while True:
    bytes += stream.read(1024)
    a = bytes.find('\xff\xd8')
    b = bytes.find('\xff\xd9')
    if a != -1 and b != -1:
        jpg = bytes[a:b+2]
        bytes = bytes[b+2:]
        i = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8), cv2.CV_LOAD_IMAGE_COLOR)
        cv2.imshow('i', i)
        if cv2.waitKey(1) == 27:
            exit(0)   

modifier (explication)

Je viens de voir que vous mentionnez que vous avez du code c ++ qui fonctionne, si c'est le cas, votre caméra peut également fonctionner en python. Le code ci-dessus analyse manuellement le flux mjpeg sans compter sur opencv , car dans certains de mes projets, l'url ne sera pas ouverte par opencv, peu importe ce que j'ai fait (c ++, python).

Mjpeg sur http est multipart/x-mixed-replace avec des informations de cadre limite et les données jpeg sont juste envoyées en binaire. Vous n'avez donc pas vraiment besoin de vous soucier des en-têtes de protocole http. Tous les cadres jpeg commencent par le marqueur 0xff 0xd8 Et se terminent par 0xff 0xd9. Le code ci-dessus extrait donc ces cadres du flux http et les décode un par un. comme ci-dessous.

...(http)
0xff 0xd8      --|
[jpeg data]      |--this part is extracted and decoded
0xff 0xd9      --|
...(http)
0xff 0xd8      --|
[jpeg data]      |--this part is extracted and decoded
0xff 0xd9      --|
...(http)

modifier 2 (lecture du fichier mjpg)

Concernant votre question de sauvegarder le fichier, oui le fichier peut être directement enregistré et rouvert en utilisant la même méthode avec de très petites modifications. Par exemple, vous devez faire curl http://IPCAM > output.mjpg, Puis modifier la ligne stream=urllib.urlopen('http://localhost:8080/frame.mjpg') pour que le code devienne ceci

import cv2
import urllib 
import numpy as np

stream = open('output.mjpg', 'rb')
bytes = ''
while True:
    bytes += stream.read(1024)
    a = bytes.find('\xff\xd8')
    b = bytes.find('\xff\xd9')
    if a != -1 and b != -1:
        jpg = bytes[a:b+2]
        bytes = bytes[b+2:]
        i = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8), cv2.CV_LOAD_IMAGE_COLOR)
        cv2.imshow('i', i)
        if cv2.waitKey(1) == 27:
            exit(0)   

Bien sûr, vous enregistrez un grand nombre d'en-têtes http redondants, que vous voudrez peut-être supprimer. Ou si vous avez une puissance de processeur supplémentaire, codez peut-être d'abord en h264 en premier. Mais si la caméra ajoute des métadonnées aux cadres d'en-tête http tels que le canal, l'horodatage, etc. Il peut être utile de les conserver.

modifier 3 (interface tkinter)

import cv2
import urllib 
import numpy as np
import Tkinter
from PIL import Image, ImageTk
import threading

root = Tkinter.Tk()
image_label = Tkinter.Label(root)  
image_label.pack()

def cvloop():    
    stream=open('output.mjpg', 'rb')
    bytes = ''
    while True:
        bytes += stream.read(1024)
        a = bytes.find('\xff\xd8')
        b = bytes.find('\xff\xd9')
        if a != -1 and b != -1:
            jpg = bytes[a:b+2]
            bytes = bytes[b+2:]
            i = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8), cv2.CV_LOAD_IMAGE_COLOR)            
            tki = ImageTk.PhotoImage(Image.fromarray(cv2.cvtColor(i, cv2.COLOR_BGR2RGB)))
            image_label.configure(image=tki)                
            image_label._backbuffer_ = tki  #avoid flicker caused by premature gc
            cv2.imshow('i', i)
        if cv2.waitKey(1) == 27:
            exit(0)  

thread = threading.Thread(target=cvloop)
thread.start()
root.mainloop()
75
Zaw Lin

Tout d'abord, sachez que vous devez essayez d'abord simplement en utilisant les fonctions de capture vidéo d'OpenCV directement, par ex. cv2.VideoCapture('http://localhost:8080/frame.mjpg')!

Cela fonctionne très bien pour moi:

import cv2
cap = cv2.VideoCapture('http://localhost:8080/frame.mjpg')

while True:
  ret, frame = cap.read()
  cv2.imshow('Video', frame)

  if cv2.waitKey(1) == 27:
    exit(0)

Quoi qu'il en soit, voici la solution de Zaw Lin portée sur OpenCV 3 (seule la modification est cv2.CV_LOAD_IMAGE_COLOR à cv2.IMREAD_COLOR et Python 3 (gestion chaîne contre octets modifiée plus urllib):

import cv2
import urllib.request
import numpy as np

stream = urllib.request.urlopen('http://localhost:8080/frame.mjpg')
bytes = bytes()
while True:
    bytes += stream.read(1024)
    a = bytes.find(b'\xff\xd8')
    b = bytes.find(b'\xff\xd9')
    if a != -1 and b != -1:
        jpg = bytes[a:b+2]
        bytes = bytes[b+2:]
        i = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8), cv2.IMREAD_COLOR)
        cv2.imshow('i', i)
        if cv2.waitKey(1) == 27:
            exit(0)
34
bugmenot123

Voici une réponse en utilisant le module Python 3 requêtes au lieu de rllib.

La raison de ne pas utiliser urllib est qu'il ne peut pas interpréter correctement une URL comme http://user:pass@ipaddress:port

L'ajout de paramètres d'authentification est plus complexe dans urllib que dans le module des demandes.

Voici une solution sympa et concise utilisant le module requêtes:

import cv2
import requests
import numpy as np

r = requests.get('http://192.168.1.xx/mjpeg.cgi', auth=('user', 'password'), stream=True)
if(r.status_code == 200):
    bytes = bytes()
    for chunk in r.iter_content(chunk_size=1024):
        bytes += chunk
        a = bytes.find(b'\xff\xd8')
        b = bytes.find(b'\xff\xd9')
        if a != -1 and b != -1:
            jpg = bytes[a:b+2]
            bytes = bytes[b+2:]
            i = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8), cv2.IMREAD_COLOR)
            cv2.imshow('i', i)
            if cv2.waitKey(1) == 27:
                exit(0)
else:
    print("Received unexpected status code {}".format(r.status_code))
10
Varun Chatterji