Python 如何解析来自ip摄像机的mjpeg http流?
下面给出了从IP摄像机获取实时流的代码Python 如何解析来自ip摄像机的mjpeg http流?,python,opencv,live-streaming,ip-camera,mjpeg,Python,Opencv,Live Streaming,Ip Camera,Mjpeg,下面给出了从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)
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)
编辑(解释)
<>我刚才看到你提到你有C++代码在工作,如果是这样的话,你的相机也可以在Python中工作。上面的代码在不依赖opencv的情况下手动解析mjpeg流,因为在我的一些项目中,无论我做了什么(c++,python),opencv都不会打开url
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)
编辑2(从mjpg文件读取)
关于保存文件的问题,是的,可以使用相同的方法直接保存并重新打开文件,只需进行很小的修改。例如,您可以执行curlhttp://IPCAM >输出.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头,您可能希望删除这些头。或者,如果您有额外的cpu能力,可能只需要先编码到h264。但是,如果相机正在向http头帧添加一些元数据,例如通道、时间戳等,那么保留它们可能会很有用
编辑3(tkinter接口)
首先,请注意,您应该首先尝试直接使用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\u LOAD\u IMAGE\u COLOR
到cv2.IMREAD\u COLOR
和Python 3(字符串与字节处理的变化加上urllib):
下面是一个使用Python3requests模块而不是urllib的答案 不使用urllib的原因是它无法正确解释像
http://user:pass@IP地址:端口
在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的解决方案:只需使用VideoCapture在cam地址中添加用户和密码,如下所示:
例如
cv2.视频捕获(“”)
使用android版IPWebcam。我认为第一款anwser不适合其他格式的图像数据,例如png。 因此,我编写了以下代码,可以处理其他类型的图像
“”“
MJPEG格式
内容类型:multipart/x-mixed-replace;boundary=--BoundaryString
--边界字符串
内容类型:image/jpg
内容长度:12390
…图像数据在此。。。
--边界字符串
内容类型:image/jpg
内容长度:12390
…图像数据在此。。。
"""
输入io
导入请求
进口cv2
将numpy作为np导入
类MjpegReader():
定义初始化(self,url:str):
self.\u url=url
定义iter_内容(自身):
"""
提出:
访问违例
"""
r=requests.get(self.\u url,stream=True)
#解析边界
content\u type=r.headers['content-type']
index=content\u type.rfind(“boundary=”)
断言索引!=1
边界=内容\u类型[索引+长度(“边界=):]+“\r\n”
边界=边界。编码('utf-8')
rd=io.BufferedReader(r.raw)
尽管如此:
自我。跳到边界(rd,边界)
长度=自身长度(rd)
收益率rd.read(长度)
定义解析长度(self,rd)->int:
长度=0
尽管如此:
line=rd.readline()
如果行==b'\r\n':
返回长度
如果行开始使用(b“内容长度”):
长度=int(行解码('utf-8')。拆分(“:”[1])
断言长度>0
定义跳过边界(self、rd、boundary:字节):
对于范围(10)内的uu:
如果rd.readline()中有边界:
打破
其他:
引发运行时错误(“未检测到边界:”,边界)
mr=MjpegReader(“http://127.0.0.1/mjpeg.cgi")
对于mr.iter_content()中的内容:
i=cv2.imdecode(np.frombuffer(content,dtype=np.uint8),cv2.IMREAD\u COLOR)
cv2.imshow('i',i)
如果cv2.waitKey(1)==27:
打破
是CGI脚本返回视频流还是浏览器显示的HTML页面?@Andris它返回视频流,我尝试使用VLC播放它,效果很好。我没有IP摄像头,但在2009年与Axis摄像头进行了多次斗争。除此之外,可能会有所帮助。我看到你也返回了视频流,并且已经修复了mjpg。:)最后一个猜测是在OpenCV和IP摄像头之间使用gstreamer。@Andris我看到了,请看我对答案的评论。字节是一个不断增长的队列,它慢慢地被解析循环流消耗。read(16384)从http流中读取16384字节的数据,并将其添加到字节中,如果找到有效的jpeg帧,字节将被缩短。读缓冲区应该小于最小JPEG帧大小,否则可能会有问题。BTW,我也需要在C++中做同样的事情,这是在这里发布的。想法是一样的,但那里的实现比python更健壮,并且经过生产测试。如果您想为两个codebasehmm保持相同的行为,这可能对您很有用……我也很好奇如何做到这一点。我已经编辑了答案。我知道你在另一个问题中解决了这个问题,但我认为这个方法更好,因为gui会在这个答案中挂起io错误等。天哪!你是电脑黑客!有没有可能把它移植到Python3上?
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)
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))