web-dev-qa-db-ja.com

opencv [python]でキャプチャデバイス(カメラ)から最新のフレームを取得する方法

カメラに接続し、イベントが発生したときにのみフレームをキャプチャします(キーを押すなど)。私がやりたいことの簡単なバージョンはこれです:

cap = cv2.VideoCapture(device_id)

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

    process(img)
    cv.Waitkey(10)

ただし、cap.readはキュー内の次のフレームのみをキャプチャし、最新のフレームはキャプチャしないようです。私はオンラインでたくさん検索しましたが、これについては多くの質問があるようですが、明確な答えはありません。キャプチャの直前と直後のキャプチャデバイスの開閉を含む一部のダーティーハックのみ(私のイベントが1秒に複数回トリガーされる可能性があるため、私にはうまくいきません)。または、固定フレームレートを想定し、各イベントで固定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も使用しています)

15
memo

私は、質問で言及された解決策、つまりバッファをクリアする別のスレッドを持つことは、このための最も簡単な非脆弱な解決策だと思います。ここに合理的にニース(私は思う)のコード:

_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バックエンドでのみサポートされています」。 LinuxバックエンドV4Lの場合、 .4.5コード のコメントによると、2018年3月9日にサポートが追加されましたが、このバックエンドについてはVIDEOIO ERROR: V4L: Property <unknown property string>(38) not supported by deviceを取得しました。最初に試してみる価値があるかもしれません。コードはこれと同じくらい簡単です:

_cap.set(cv2.CAP_PROP_BUFFERSIZE, 0)
_
11
Ulrich Stern