Python 如何从opencv中的捕获设备(照相机)获取最新帧
我想连接到摄像头,仅在事件发生时(例如按键)拍摄一帧。我想做的一个简化版本是:Python 如何从opencv中的捕获设备(照相机)获取最新帧,python,opencv,camera,video-capture,opencv3.0,Python,Opencv,Camera,Video Capture,Opencv3.0,我想连接到摄像头,仅在事件发生时(例如按键)拍摄一帧。我想做的一个简化版本是: cap = cv2.VideoCapture(device_id) while True: if event: img = cap.read() preprocess(img) process(img) cv.Waitkey(10) 但是,cap.read似乎只捕获队列中的下一帧,而不是最新帧。我在网上做了很多搜索,似乎有很多关于这方面的问题,但没有明确的
cap = cv2.VideoCapture(device_id)
while True:
if event:
img = cap.read()
preprocess(img)
process(img)
cv.Waitkey(10)
但是,cap.read似乎只捕获队列中的下一帧,而不是最新帧。我在网上做了很多搜索,似乎有很多关于这方面的问题,但没有明确的答案。只有一些肮脏的黑客行为,包括在抓取之前和之后打开和关闭捕获设备(这对我不起作用,因为我的事件可能每秒触发多次);或者假设一个固定的帧速率,并在每个事件上读取固定的n次(这对我来说不起作用,因为我的事件是不可预测的,可能在任何时间间隔发生)
一个很好的解决方案是:
while True:
if event:
while capture_has_frames:
img = cap.read()
preprocess(img)
process(img)
cv.Waitkey(10)
但是什么是捕获?有帧吗?有可能得到那个信息吗?我试着查看CV\u CAP\u PROP\u POS\u框架,但总是-1
现在,我有一个单独的线程,其中捕获以完整的fps运行,在我的活动中,我从该线程中获取了最新的图像,但这似乎有些过分
(我在Ubuntu 16.04 btw上,但我想这不重要。我也在使用pyqtgraph显示)如果你不想在没有事件发生时捕获帧,为什么要预处理/处理帧?如果不处理帧,则只需丢弃它,除非事件发生。您的程序应该能够以足够的速度捕获、评估您的状况并丢弃,即与您的相机FPS捕获速率相比足够快,以便始终获取队列中的最后一帧
如果Python不熟练,因为我在C++中做OpenCV,但看起来应该类似于:
vidcap = cv.VideoCapture( filename )
while True:
success, frame = vidcap.read()
If Not success:
break
If cv.waitKey(1):
process(frame)
根据OpenCV引用,vidcap.read()返回bool。如果帧读取正确,则为真。然后,捕获的帧存储在可变帧中。如果没有按键,循环将继续进行。按下某个键时,您将处理最后捕获的帧。我认为问题中提到的解决方案,即使用一个单独的线程清除缓冲区,是最简单的解决方案。这里有相当不错的(我认为)代码:
import cv2, queue, threading, time
# bufferless VideoCapture
class VideoCapture:
def __init__(self, name):
self.cap = cv2.VideoCapture(name)
self.q = queue.Queue()
t = threading.Thread(target=self._reader)
t.daemon = True
t.start()
# read frames as soon as they are available, keeping only most recent one
def _reader(self):
while True:
ret, frame = self.cap.read()
if not ret:
break
if not self.q.empty():
try:
self.q.get_nowait() # discard previous (unprocessed) frame
except queue.Empty:
pass
self.q.put(frame)
def read(self):
return self.q.get()
cap = VideoCapture(0)
while True:
time.sleep(.5) # simulate time between events
frame = cap.read()
cv2.imshow("frame", frame)
if chr(cv2.waitKey(1)&255) == 'q':
break
帧读取器线程封装在自定义VideoCapture类中,与主线程的通信通过队列进行
我为node.js发布了非常类似的代码,在node.js中使用JavaScript解决方案会更好。我对另一个问题的评论详细说明了为什么没有独立线程的非脆性解决方案看起来很困难
一种更简单但仅支持某些OpenCV后端的替代解决方案是使用CAP\u PROP\u BUFFERSIZE
。状态为“目前仅由DC1394[Firewire]v2.x后端支持”。根据中的评论,对于Linux后端V4L,支持是在2018年3月9日添加的,但我得到了设备不支持的VIDEOIO错误:V4L:Property(38)。也许值得一试;代码非常简单,如下所示:
cap.set(cv2.CAP_PROP_BUFFERSIZE, 0)
在我的树莓皮4上
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
它可以工作,并且是我的pi相机为我提供最新帧所需的全部,在相机前面的场景和在预览图像中显示该场景之间有3秒以上的一致延迟。我的代码处理一个图像需要1.3秒,所以我不确定为什么会出现另外2秒的延迟,但它是一致的,并且有效
旁注:由于我的代码处理图像需要一秒钟的时间,所以我还添加了
cap.set( cv2.CAP_PROP_FPS, 2 )
以防减少任何不必要的活动,因为我一秒钟都拍不到一帧。但是,当我将cv2.CAP_PROP_FPS设置为1时,我得到一个奇怪的输出,即我的所有帧几乎都是完全暗的,因此将FPS设置得太低可能会导致问题您误解了这个问题。OP面临的问题是vidcap.read()
返回缓冲区上的下一帧,因此如果他们花费大量时间处理前一帧,vidcap.read()
将不会生成最新帧,而是生成缓冲区中的下一帧buffer@Christian也许,我是。也许memo应该使用GPU来加速图像处理。这将在捕获时同时发生,无需手动创建额外的线程(异构计算)。虽然在完美的世界中,会有一种更优雅的方式来获取最新的帧数据,但这确实奏效了@ChristianScillitoe,在一个完美的世界中,我们通过制造商的API使用所有摄像头功能,并使用提供的摄像头触发器来实际“捕获事件发生时的帧”cv2.VideoCapture
相反,它是一个紧凑的跨平台通用类,用于方便和快速的原型设计。@main实际上,我非常喜欢使用类似OpenCV的跨平台设备无关API来处理视频捕获。另外,虽然OpenCV的HighGUI模块,但视频I/O模块(VideoCapture
,等等)不是。@mainCreal,我明白你的意思,但是如果VideoCapture不是为了提供精确的定时控制,那么grab()和retrieve()方法的作用是什么?难道他们不应该提供这样的控制吗?但是,如果你不能确定你检索到的图像与你抓取的图像相同,它们又有什么好处呢?我觉得我有点误解了。对于python 3,您希望执行导入队列作为队列
,而不是导入队列
。请不要对多个问题添加相同的答案。回答最好的一个,并将其余的标记为重复项。请看@Machavity Yep,我知道通常不会这样做,但在这种情况下,我在另一个问题中发布了副本,指向这个问题,Ulrich Stern的答案,以及另一个可能是dup的事实,因为另一个可能不是dup:另一个问题专门使用IP摄像头,而不是本地摄像头,我无法判断这里的答案是否适用于IP摄像机。这就是为什么我认为其他人可能会这么想