场景:公司有个 ip 摄像头在海港那边。然后这边想访问一个 url,然后就返回摄像头那时候的一帧的截图(其实是返回图像识别过的 json )。
但是每次都去连接那个 ip 摄像头太慢了,于是便想将该 Video 连接持续化,将其放入一个 dict 里存起来,而不是每次连接完之后都 release,这样不用每次访问都去连接一次。大概像下面这样:
pool = {}
def create(camId):
cap = cv2.VideoCapture(baseUrl+camId)
if camId in pool:
return True
if cap.isOpened():
print("连接摄像头成功")
capDict = {}
capDict['time'] = int(time.time() * 1000)
capDict['camera'] = cap
pool[camId] = capDict
然后我用 flask 搭建的后台,每次访问 url,便调用下面的 detectBoat()函数,从 dict 中取出对应 id 的 Video 摄像头连接。这样只要最开始创建一次,就可以不用每次访问 url 都连接一次了。
def detectBoat(camId):
cap = pool[camId]['camera']
retryLimit = 3
if cap.isOpened():
results = ""
while True:
success, frame = cap.read()
try:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
except cv2.error:
print('reading frame failed, retry after 1s...')
time.sleep(1)
continue
img_name = f'{int(time.time() * 1000)}.jpg'
cv2.imwrite(f"{image_folder}/{img_name}", frame)
print(f'截取了图片: {img_name}')
resp = urlopen(myurl) #让别的分析后台来分析,再返回 json
results = json.loads(resp.read())
print(f"船舶识别: {results}")
break
del success, frame
return results
else:
return '请先连接摄像头'
然后问题来了,我发现我每次调用那个可持续化的摄像头连接 cap 的时候,他返回给我的永远是这一帧后的下一帧图。就是比如说,我 4 点 1 分调用这个函数,返回给我 4 点 1 分 0 秒的图,然后我 5 点再调用,还是返回给我 4 点 1 分 0 秒的图的下一帧的图给我,我再调用,就返回给我再下一帧图,我怀疑他是不是将每帧图都缓存起来了?我怎样才能让他返回给我当前时间的图呢。
个人现在来补充下,首先是1楼老哥说的方法是完全可行的。当时我根据他的说法+参考网上一些资料自己写了一个。
一开始是继承 cv2.VideoCapture 类写了一个新类 class NewVideoCapture(cv2.VideoCapture)
然后把继承改成写了一个新类,将cv2.VideoCapture()作为类成员,如下:
import threading
import cv2
class NewVideoCapture:
def __init__(self, url, *args, **kwargs):
self.frame_receiver_thread = threading.Thread(target=self.recv_frame)
self._cur_frame = None
self._reading = False
if isinstance(url, str) and url.startswith(("rtsp://", "rtmp://")):
self._reading = True
else:
raise ValueError('url格式不对')
self.url = url
self.video = cv2.VideoCapture(url)
self.can_read = False
def isOpened(self):
return self.video.isOpened()
def recv_frame(self):
while self.isOpened() and self._reading:
revtal, frame = self.video.read()
if not revtal:
break
self._cur_frame = frame
self.can_read = True
self._reading = False
def start_read(self):
if self.isOpened():
self.frame_receiver_thread.start()
def read(self, image=None):
while not self.can_read:
pass
if self._cur_frame is not None:
frame = self._cur_frame.copy()
else:
frame = None
revtal = frame is not None
return revtal, frame
def stop_read(self):
self._reading = False
if self.frame_receiver_thread.is_alive():
self.frame_receiver_thread.join()
def get(self, propId):
return self.video.get(propId)
def release(self):
self.stop_read()
self.video.release()
这个也是没问题的,然后我为啥今天突然补充下,是因为今天看到一个第三方库原来已经实现了这个功能了。
pip install imutils
from imutils.video import WebcamVideoStream
不过我看了源码,其实思路也是一样的,想用哪个就用哪个吧。
1
shuianfendi6 2019-08-22 18:20:08 +08:00
循环读取摄像头,将图片持续写到全局变量中
|
2
xiaolinjia OP @shuianfendi6 我还以为有什么函数可以直接跳过帧的,结果还是要循环吗,那只能开个线程来循环读了。
|