如何从OpenCV中的捕获设备(相机)获取最新帧 [英] How to get the latest frame from capture device (camera) in opencv

查看:426
本文介绍了如何从OpenCV中的捕获设备(相机)获取最新帧的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想连接到摄像机,并且仅在发生事件(例如按键)时捕获帧。我想做的一个简化版本是:

I want to connect to a camera, and only capture a frame when an event happens (e.g. keypress). A simplified version of what I'd like to do is this:

cap = cv2.VideoCapture(device_id)

while True:
    if event:
        img = cap.read()
        preprocess(img)

    process(img)
    cv.Waitkey(10)

但是,cap.read似乎只能捕获队列中的下一帧,不是最新的。我在网上做了很多搜索,似乎对此有很多疑问,但没有明确的答案。只有一些肮脏的骇客,涉及在抓取之前和之后打开和关闭捕获设备(这对我不起作用,因为我的事件每秒可能触发多次);或采用固定的帧频并在每个事件上读取固定的n次(这对我来说是行不通的,因为我的事件是不可预测的,并且可能在任何时间间隔发生)。

However, cap.read seems to only capture the next frame in the queue, and not the latest. I did a lot of searching online, and there seems to be a lot of questions on this but no definitive answer. Only some dirty hacks which involve opening and closing the capture device just before and after grabbing (which won't work for me as my event might be triggered multiple times per second); or assuming a fixed framerate and reading a fixed-n times on each event (which won't work for me as my event is unpredictable and could happen at any interval).

一个不错的解决方案是:

A nice solution would be:

while True:
    if event:
        while capture_has_frames:
            img = cap.read()
        preprocess(img)

    process(img)
    cv.Waitkey(10)

但是 capture_has_frames 是什么?是否有可能获得该信息?我尝试查看 CV_CAP_PROP_POS_FRAMES ,但始终为-1。

But what is capture_has_frames? Is it possible to get that info? I tried looking into CV_CAP_PROP_POS_FRAMES but it's always -1.

现在我有一个单独的线程,其中捕获以全fps的速度运行,并且在我的活动中,我正在从该线程中获取最新的图像,但这似乎过分了。

For now I have a separate thread where the capture is running at full fps, and on my event I'm grabbing the latest image from that thread, but this seems overkill.

(我在Ubuntu 16.04 btw上,但我想应该没关系。我也在使用pyqtgraph进行显示)

(I'm on Ubuntu 16.04 btw, but I guess it shouldn't matter. I'm also using pyqtgraph for display)

推荐答案

我认为问题中提到的解决方案(即拥有一个单独的线程来清除缓冲区)是最简单的非脆弱解决方案为了这。这里的代码相当不错(我认为):

I think the solution mentioned in the question, namely having a separate thread that clears the buffer, is the easiest non-brittle solution for this. Here reasonably nice (I think) code for this:

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类中,并且通过队列与主线程进行通信。

The frame reader thread is encapsulated inside the custom VideoCapture class, and communication with the main thread is via a queue.

我发布的内容非常相似node.js 问题的代码,而JavaScript解决方案会更好。我对另一个问题的评论答案详细说明了为什么没有单独线程的非脆弱解决方案似乎很困难。

I posted very similar code for a node.js question, where a JavaScript solution would have been better. My comments on another answer to that question give details why a non-brittle solution without separate thread seems difficult.

使用 CAP_PROP_BUFFERSIZE 较为简单但仅对某些OpenCV后端支持的替代解决方案 2.4个文档指出它仅目前由DC1394 [Firewire] v 2.x后端支持。对于Linux后端V4L,根据 3.4.5代码,已在2018年3月9日添加了支持,但出现 VIDEOIO错误:V4L:设备不支持的属性<未知属性字符串>(38)正是这个后端。可能值得一试。代码很简单:

An alternative solution that is easier but supported only for some OpenCV backends is using CAP_PROP_BUFFERSIZE. The 2.4 docs state it is "only supported by DC1394 [Firewire] v 2.x backend currently." For Linux backend V4L, according to a comment in the 3.4.5 code, support was added on 9 Mar 2018, but I got VIDEOIO ERROR: V4L: Property <unknown property string>(38) not supported by device for exactly this backend. It may be worth a try first; the code is as easy as this:

cap.set(cv2.CAP_PROP_BUFFERSIZE, 0)

这篇关于如何从OpenCV中的捕获设备(相机)获取最新帧的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆