Python 如何解析来自网络摄像机的 mjpeg http 流?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21702477/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 23:27:03  来源:igfitidea点击:

How to parse mjpeg http stream from ip camera?

pythonopencvlive-streamingip-cameramjpeg

提问by Prakhar Mohan Srivastava

Given below is the code written for getting live stream from an IP Camera.

下面给出的是为从 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

On running the code the output that I am getting is:

在运行代码时,我得到的输出是:

Cam not found

Where am I going wrong? Also, why is frame None here? Is there some problem with the conversion?

我哪里错了?另外,为什么这里没有框架?转换有问题吗?

采纳答案by Zaw Lin

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

编辑(解释)

I just saw that you mention that you have c++ code that is working, if that is the case your camera may work in python as well. The code above manually parses the mjpeg stream without relying on opencv, since in some of my projects the url will not be opened by opencv no matter what I did(c++,python).

我刚刚看到您提到您有可以运行的 C++ 代码,如果是这种情况,您的相机也可以在 python 中运行。上面的代码不依赖opencv手动解析mjpeg流,因为在我的一些项目中,无论我做什么(c++,python),opencv都不会打开url。

Mjpeg over http is multipart/x-mixed-replace with boundary frame info and jpeg data is just sent in binary. So you don't really need to care about http protocol headers. All jpeg frames start with marker 0xff 0xd8and end with 0xff 0xd9. So the code above extracts such frames from the http stream and decodes them one by one. like below.

http 上的 Mjpeg 是 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 (reading from mjpg file)

编辑 2(从 mjpg 文件中读取)

Regarding your question of saving the file, yes the file can be directly saved and reopened using the same method with very small modification. For example you would do curl http://IPCAM > output.mjpgand then change the line stream=urllib.urlopen('http://localhost:8080/frame.mjpg')so that the code becomes this

关于您保存文件的问题,是的,可以使用相同的方法直接保存并重新打开文件,只需进行很小的修改。例如,您将执行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)   

Of course you are saving a lot of redundant http headers, which you might want to strip away. Or if you have extra cpu power, maybe just encode to h264 first. But if the camera is adding some meta data to http header frames such as channel, timestamp, etc. Then it may be useful to keep them.

当然,您正在保存大量冗余的 http 标头,您可能希望将其去除。或者,如果您有额外的 CPU 能力,也许可以先编码为 h264。但是如果相机正在向 http 标头帧添加一些元数据,例如通道、时间戳等。那么保留它们可能会很有用。

edit 3 (tkinter interfacing)

编辑 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()

回答by bugmenot123

First of all, please be aware that you should first trysimply using OpenCV's video capture functions directly, e.g. cv2.VideoCapture('http://localhost:8080/frame.mjpg')!

首先,请注意,你应该先尝试简单地使用OpenCV中的视频捕捉功能直接,如cv2.VideoCapture('http://localhost:8080/frame.mjpg')

This works just fine for me:

这对我来说很好用:

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)

Anyways, here is Zaw Lin's solution ported to OpenCV 3 (only change is cv2.CV_LOAD_IMAGE_COLORto cv2.IMREAD_COLORand Python 3 (string vs byte handling changed plus urllib):

不管怎么说,这里是ZAW林的解决方案移植到OpenCV的3(唯一的变化是cv2.CV_LOAD_IMAGE_COLOR,以cv2.IMREAD_COLOR和Python 3(字符串VS字节处理改变及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)

回答by Varun Chatterji

Here is an answer using the Python 3 requestsmodule instead of urllib.

这是使用 Python 3 requests模块而不是urllib的答案。

The reason for not using urllib is that it cannot correctly interpret a URL like http://user:pass@ipaddress:port

不使用 urllib 的原因是它不能正确解释一个像 http://user:pass@ipaddress:port

Adding authentication parameters is more complex in urllib than the requests module.

在 urllib 中添加身份验证参数比在 requests 模块中更复杂。

Here is a nice, concise solution using the requests 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))

回答by Sansanito

I had the same problem. The solution without requests or urllib: just add the user and password in the cam address, using VideoCapture, like this:

我有同样的问题。不带requests或urllib的解决方法:在cam地址中添加用户和密码,使用VideoCapture,如下所示:

E.g.

例如

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

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

using IPWebcam for android.

使用IPWebcam for android。