Comment analyser le flux http de MJPEG à partir d'une caméra ip?

donné ci-dessous est le code écrit pour obtenir le flux en direct à partir 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

sur l'exécution du code la sortie que je reçois est:

Cam not found

Où est-ce que je vais mal? Aussi, pourquoi est-frame pas ici? Est-il un problème avec la conversion?

18
demandé sur Zaw Lin 2014-02-11 16:57:13

4 réponses

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 fonctionner en python aussi. Le code ci-dessus parsème manuellement le flux mjpeg sans s'appuyer sur opencv, car dans certains de mes projets l'url ne sera pas ouverte par opencv quoi que j'aie fait(c++,python).

MJPEG sur http est multipart / x-mixed-replace avec boundary frame info et les données jpeg sont simplement envoyées en binaire. Si vous vous n'avez pas vraiment besoin de vous soucier des en-têtes de protocole http. Tous les cadres jpeg commencent par un marqueur 0xff 0xd8 et à la fin avec 0xff 0xd9. Ainsi, le code ci-dessus extrait 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)

edit 2 (lecture du fichier mjpg)

en ce qui concerne votre question de sauvegarder le fichier, oui le fichier peut être directement sauvegardé et réouvert en utilisant la même méthode avec de très petites modifications. Par exemple, vous le feriez curl http://IPCAM > output.mjpg et puis changer 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 économisez beaucoup d'en-têtes http redondants, que vous pourriez vouloir supprimer. Ou si vous avez plus de puissance cpu, peut-être encodez juste à h264 d'abord. Mais si la caméra ajoute des méta-données à des cadres d'en-tête http tels que channel, timestamp, etc. Il peut être utile de les garder.

edit 3 (tkinter, interfaçage)

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()
66
répondu Zaw Lin 2018-08-28 10:18:24

tout d'Abord, s'il vous plaît être conscient que vous devez essayez d'abord il suffit d'utiliser les fonctions de capture vidéo D'OpenCV directement, e.g. cv2.VideoCapture('http://localhost:8080/frame.mjpg')!

Cela fonctionne 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)

de toute façon, voici la solution de Zaw Lin portée à OpenCV 3 (seul le changement est cv2.CV_LOAD_IMAGE_COLORcv2.IMREAD_COLOR et Python 3 (string vs octet de manutention changé 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)
26
répondu bugmenot123 2016-04-17 17:26:40
demande module au lieu de urllib.

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

ajouter des paramètres d'authentification est plus complexe dans urllib que dans le module requests.

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))
5
répondu Varun Chatterji 2017-03-20 13:30:41

j'ai eu le même problème. La solution sans requêtes ou urllib: il suffit d'ajouter l'utilisateur et le mot de passe dans l'adresse cam, en utilisant VideoCapture, comme ceci:

E. G.

cv2.VideoCapture ('