Подтвердить что ты не робот

Как разобрать HTTP-поток mjpeg с 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

Где я ошибаюсь? Кроме того, почему frame No здесь? Есть ли проблемы с преобразованием?

4b9b3361

Ответ 1

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)   

изменить (объяснение)

Я только что увидел, что вы упоминаете, что у вас есть код С++, который работает, если это так, ваша камера может работать и на python. Приведенный выше код вручную анализирует поток mjpeg, не полагаясь на opencv, так как в некоторых моих проектах url не будет открыт opencv независимо от того, что я сделал (С++, 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, такие как канал, временная метка и т.д. Тогда может быть полезно сохранить их.

edit 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()

Ответ 2

Прежде всего, имейте в виду, что сначала попробуйте просто использовать функции захвата изображений 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 - 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)

Ответ 3

Вот ответ, используя модуль Python 3 запросы вместо urllib.

Причиной не использования urllib является то, что он не может правильно интерпретировать URL-адрес, например http://user:[email protected]: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))

Ответ 4

У меня была та же проблема. Решение без запросов или urllib: просто добавьте пользователя и пароль в адрес камеры, используя VideoCapture, например:

например.

cv2.VideoCapture('http://user:[email protected]/video')

используя IPWebcam для Android.