python - cam - ¿Cómo analizar mjpeg http stream desde la cámara ip?
python webcam streaming (4)
A continuación se muestra el código escrito para obtener transmisión en vivo desde una cámara 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
Al ejecutar el código, el resultado que obtengo es:
Cam not found
¿A dónde me voy mal? Además, ¿por qué está el cuadro Ninguno aquí? ¿Hay algún problema con la conversión?
Aquí hay una respuesta utilizando el módulo de solicitudes de Python 3 en lugar de urllib .
La razón para no usar urllib es que no puede interpretar correctamente una URL como http://user:pass@ipaddress:port
Agregar parámetros de autenticación es más complejo en urllib que el módulo de solicitudes.
Aquí hay una solución agradable y concisa usando el módulo de solicitudes:
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))
En primer lugar, tenga en cuenta que primero debe intentar usar las funciones de captura de video de OpenCV directamente , por ejemplo, cv2.VideoCapture(''http://localhost:8080/frame.mjpg'')
.
Esto funciona bien para mí:
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 todos modos, aquí está la solución de Zaw Lin portada a OpenCV 3 (solo el cambio es cv2.CV_LOAD_IMAGE_COLOR
a cv2.IMREAD_COLOR
y Python 3 (el manejo de la cadena frente a los bytes cambió más 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)
Yo tuve el mismo problema. La solución sin solicitudes o urllib: simplemente agregue el usuario y la contraseña en la dirección de la cámara, usando VideoCapture, como esto:
P.ej
cv2.VideoCapture ('' http: // usuario: [email protected]/video '')
utilizando IPWebcam para Android.
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)
editar (explicación)
Acabo de ver que mencionas que tienes un código c ++ que está funcionando, si ese es el caso, tu cámara también podría funcionar en python. El código anterior analiza manualmente el flujo de mjpeg sin depender de opencv, ya que en algunos de mis proyectos, la url no se abrirá con opencv, no importa lo que hice (c ++, python).
Mjpeg sobre http es multipart / x-mixed-replace con información de marco de límite y los datos jpeg se envían en formato binario. Por lo tanto, no es necesario preocuparse por los encabezados de protocolo http. Todas las tramas jpeg comienzan con el marcador 0xff 0xd8
y terminan con 0xff 0xd9
. Por lo tanto, el código anterior extrae dichos marcos de la secuencia http y los decodifica uno por uno. como abajo.
...(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)
edición 2 (lectura de archivo mjpg)
Con respecto a su pregunta de guardar el archivo, sí, el archivo se puede guardar y reabrir directamente usando el mismo método con una modificación muy pequeña. Por ejemplo, haría curl http://IPCAM > output.mjpg
y luego cambiaría el stream=urllib.urlopen(''http://localhost:8080/frame.mjpg'')
línea stream=urllib.urlopen(''http://localhost:8080/frame.mjpg'')
para que el código se convierta en este
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)
Por supuesto, está guardando muchos encabezados http redundantes, que tal vez quiera quitar. O si tiene potencia de CPU adicional, tal vez simplemente codifique a h264 primero. Pero si la cámara está agregando algunos metadatos a los marcos del encabezado http, como el canal, la marca de tiempo, etc. Entonces puede ser útil mantenerlos.
edición 3 (interfaz 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()