web-dev-qa-db-ja.com

IPカメラからMJPEG HTTPストリームを解析する方法は?

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

どこがおかしいの?また、なぜフレームなしはここにありますか?変換に問題はありますか?

_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で動作する場合もあります。上記のコードはopencvに依存せずにmjpegストリームを手動で解析します。なぜなら、私のプロジェクトのいくつかでは、私が何をしたとしても、opencvによってURLが開かれないからです(c ++、python)。

Http上のMjpegは、境界フレーム情報を含むmultipart/x-mixed-replaceであり、jpegデータはバイナリで送信されます。したがって、httpプロトコルヘッダーを実際に考慮する必要はありません。すべてのjpegフレームはマーカー_0xff 0xd8_で始まり、_0xff 0xd9_で終わります。したがって、上記のコードはhttpストリームからそのようなフレームを抽出し、それらを1つずつデコードします。以下のように。

_...(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)
_

編集2(mjpgファイルからの読み取り)

ファイルを保存するという質問に関しては、はい、ファイルを直接保存して、わずかな修正を加えて同じ方法で再度開くことができます。たとえば、_curl http://IPCAM > output.mjpg_を実行し、stream=urllib.urlopen('http://localhost:8080/frame.mjpg') soの行を変更して、コードが次のようになるようにします。

_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ヘッダーを保存しているので、それらを削除することもできます。または、追加のCPUパワーがある場合は、最初に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()
_
75
Zaw Lin

まず、最初の試行 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)

とにかく、ここにOpenCV 3に移植されたZaw Linのソリューションがあります(変更点は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)
34
bugmenot123

rllibの代わりにPython 3 requestsモジュールを使用した回答です。

Urllibを使用しない理由は、http://user:pass@ipaddress:portのようなURLを正しく解釈できないためです。

認証パラメータの追加は、urllibでは要求モジュールよりも複雑です。

以下は、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))
10
Varun Chatterji