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?
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()
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_COLOR
cv2.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)
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))