如何从opencv [python]

时间:2017-04-27 18:15:48

标签: 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似乎只捕获队列中的下一帧,而不是最新的帧。我在网上做了很多搜索,似乎有很多问题,但没有明确的答案。只有一些脏兮兮的黑客涉及在抓取之前和之后打开和关闭捕获设备(由于我的事件可能每秒被触发多次,因此对我不起作用);或者假定一个固定的帧速率并且在每个事件上读取固定的n次(由于我的事件不可预测并且可能在任何时间间隔发生,因此对我来说不会起作用)。

一个很好的解决方案是:

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。

现在我有一个单独的线程,其中捕获以全fps运行,而在我的事件中,我从该线程中抓取最新图像,但这似乎有点过分。

(我在Ubuntu 16.04顺便说一句,但我觉得这不重要。我也使用pyqtgraph进行显示)

3 个答案:

答案 0 :(得分:2)

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

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 question发布了非常相似的代码,使用JavaScript解决方案会更好。我对该问题的另一个answer的评论详细说明了为什么没有单独线程的非脆弱解决方案似乎很困难。

一个更简单但仅某些OpenCV后端支持的替代解决方案正在使用CAP_PROP_BUFFERSIZE。在2.4 docs状态下,“目前仅受DC1394 [Firewire] v 2.x后端支持。”根据{{​​3}}中的评论,对于Linux后端V4L,已在2018年3月9日添加了支持,但我完全为此后端得到了VIDEOIO ERROR: V4L: Property <unknown property string>(38) not supported by device。首先可能值得尝试;代码很简单:

cap.set(cv2.CAP_PROP_BUFFERSIZE, 0)

答案 1 :(得分:0)

在我的Raspberry Pi 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设置得太低会导致问题

答案 2 :(得分:-1)

如果您不希望在没有事件发生时捕获帧,为什么要预处理/处理帧?如果您不处理框架,则可以将其丢弃,除非事件发生。您的程序应该能够以足够的速度(即与相机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()返回布尔值。如果正确读取帧,它将为True。然后,将捕获的帧存储在变量 frame 中。如果没有按键,则循环继续进行。按下键后,您将处理最后捕获的帧。