为 OpenCV 'VideoCapture.read' 函数设置自定义超时



我正在编写一个从相机读取rtps流的脚本。问题是,有时连接不完美,帧需要一段时间才能到达。根据我的发现,cv2.VideoCapture中的read函数没有超时,我们可以在不重新编译的情况下进行修改,默认值(30秒(对我来说太多了。

我尝试了两种方法,一种是使用threading,另一种使用multiprocessing。前者没有像预期的那样工作,因为我不能足够快地杀死线程,脚本也就死了。后者意味着,当一切正常工作时,我以1/fps的速率创建和销毁进程,我认为这不是一个好主意。

以下是一个最低限度的工作示例。当proc = True时,它使用多处理,而当proc = False时,它则使用线程。使用TIMESLEEP > 0可以模拟读取功能的延迟

import cv2
import time
import queue
import psutil
import threading
import multiprocessing as mp
TIMESLEEP = 0
class FrameThread(threading.Thread):
def __init__(self, func, res):
super().__init__()
self.daemon = True
self.res  = res
self.func = func
def run(self):
time.sleep(TIMESLEEP)
self.res.put(self.func)
def putframe(func, res):
time.sleep(TIMESLEEP)
res.put(func)
class Test(object):
def __init__(self, url, proc = True):
self.url   = url
self.black = [1, 2, 3]
self.fps   = 10
self.proc  = proc
self._rq   = mp.Queue() if self.proc else queue.Queue()
def _timeout_func(self, func, timeout = 10):
if self.proc:
_proc = mp.Process(target = putframe, args = (func, self._rq))
_proc.start()
else:
FrameThread(func, self._rq).start()
try:
t1  = time.time()
ret, frame = self._rq.get(block = True, timeout = timeout)
diff_fps = 1 / self.fps - (time.time() - t1)
time.sleep(diff_fps if diff_fps > 0 else 0)
if self.proc:
_proc.terminate()
frame = frame if ret else self.black.copy()
except queue.Empty:
diff_fps = 1 / self.fps - timeout
time.sleep(diff_fps if diff_fps > 0 else 0)
if self.proc:
_proc.terminate()
ret, frame = True, self.black.copy()
return ret, frame

def run(self):
cap  = cv2.VideoCapture(self.url)
while True:
ret, frame = self._timeout_func(cap.read(), timeout = 0.1)
if not ret:
break
print(self.proc if self.proc else len(psutil.Process().threads()), end='r')
proc = False
test = Test('./video.mp4', proc = proc)
test.run()

你们还有其他想法或方法吗?或者对上述代码有任何改进?

谢谢!

没有尝试过这种脚本,但我看到了类似的问题,我建议您使用e VLC python绑定(您可以使用pip-install-python VLC安装它(并播放流:

import vlc
player=vlc.MediaPlayer('rtsp://:8554/output.h264')
player.play()

然后每隔一秒左右拍一张快照:

while 1:
time.sleep(1)
player.video_take_snapshot(0, '.snapshot.tmp.png', 0, 0)

然后您可以使用SimpleCV或其他东西进行处理(只需将图像文件".snapshot.tmp.png"加载到处理库中(。

最新更新