Как разобрать MJPEG http поток с ip-камеры?
Ниже приведен код, написанный для получения живого потока с 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
при запуске кода вывод, который я получаю:
Cam not found
где я ошибаюсь? Кроме того, почему здесь нет фрейма? Есть ли какие-то проблемы с преобразованием?
4 ответов
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)
edit (объяснение)
Я только что видел, что вы упомянули, что у вас есть код C++, который работает, если это так, ваша камера может работать и в python. Приведенный выше код вручную анализирует поток mjpeg, не полагаясь на opencv, поскольку в некоторых моих проектах url-адрес не будет открыт opencv независимо от того,что я сделал(c++, python).
Mjpeg по http является multipart / x-mixed-replace с информацией о граничном фрейме, а данные jpeg просто отправляются в двоичном формате. Так вы не нужно заботиться о заголовках протокола HTTP. Все кадры jpeg начинаются с маркера 0xff 0xd8
и 0xff 0xd9
. Таким образом, приведенный выше код извлекает такие кадры из потока http и декодирует их один за другим. как внизу.
...(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 (чтение из файла mjpg)
Что касается вашего вопроса о сохранении файла, да, файл может быть непосредственно сохранен и снова открыт с помощью того же метода с очень небольшими изменениями. Например, вы бы сделали curl http://IPCAM > output.mjpg
а затем изменить линия stream=urllib.urlopen('http://localhost:8080/frame.mjpg')
так что код становится
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)
конечно, вы сохраняете много избыточных заголовков http,которые вы можете удалить. Или, если у вас есть дополнительная мощность процессора, возможно, сначала Закодируйте h264. Но если камера добавляет некоторые метаданные в HTTP-заголовочные кадры, такие как канал, временная метка и т. д. Тогда, возможно, будет полезно сохранить их.
редактировать 3 (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()
прежде всего, пожалуйста, имейте в виду, что вы должны сначала попробуй просто используя функции видеозахвата OpenCV напрямую, например,cv2.VideoCapture('http://localhost:8080/frame.mjpg')
!
это работает просто отлично для меня:
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)
в любом случае, вот решение Zaw Lin, портированное на OpenCV 3 (только изменение cv2.CV_LOAD_IMAGE_COLOR
to cv2.IMREAD_COLOR
и Python 3 (изменена обработка строк и байтов плюс 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)
вот ответ с использованием Python 3 запросы модуль вместо urllib.
причина отказа от использования urllib заключается в том, что он не может правильно интерпретировать URL-адрес как http://user:pass@ipaddress:port
добавление параметров аутентификации является более сложным в urllib, чем модуль запросов.
вот хорошее, краткое решение с использованием модуля запросов:
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))
У меня была та же проблема. Решение без запросов или urllib: просто добавьте пользователя и пароль в адрес cam, используя VideoCapture, например:
Э. Г.
cv2.Захватить видео('http://user:password@XXX.XXX.XXX.XXX/video')
использование IPWebcam для android.