场景:公司有个 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 我还以为有什么函数可以直接跳过帧的,结果还是要循环吗,那只能开个线程来循环读了。 |