(03)Python+OpenCVでUVC規格のカメラ制御(バッファについて)

 2nd January 2023 at 11:20am

WEBカメラなどUVC規格のカメラから、Pythonで画像を取得する方法で一番有名なのは、OpenCVのVideoCaptureクラスを使う方法ではいでしょうか。

以下のような簡単なプログラムで画像が取得できます。(事前にpip install opencv-pythonしておいてください)

>>> import cv2
>>> cap = cv2.VideoCapture(0) # 複数ある場合は適切なIDを指定してください
>>> ret, img = cap.read() # ret必須です

表示を行いたい時は、以下を実行してください。

>>> cv2.imshow("img", img)
>>> cv2.waitKey(1)

さて、ret, img = VideoCapture.read()は、ret = VideoCapture.grab()ret, img = VideoCapture.retrieve()が一度ずつ行われます。それぞれのメソッドの特徴を以下にあげます。

VideoCapture.grab()
・カメラから次のフレームを取得し、バッファーに入れます。
VideoCapture.retrieve()
・バッファにあるフレームを画像配列(NumPyの配列)にして取得します。
・バッファが空の時、ret = Falseimg = None
・バッファが空ではない時には画像を取り込みますが、キューではないので空にしたりはしません。grab()せずにretrieve()を連続実行すると同じ画像が撮り込まれます。(OpenCV4.1.0)

grab()してから、retrieve()した場合。

>>> import cv2
>>> cap = cv2.VideoCapture(0)
>>> cap.grab()
True ←撮像成功
>>> ret, img = cap.retrieve()
>>> ret
True ←画像として取り込み成功
>>> ret, img = cap.retrieve()
>>> ret
True ←画像として取り込み成功、ただし上と同じ画像

grab()せずに、retrieve()した場合。

>>> import cv2
>>> cap = cv2.VideoCapture(0)
>>> ret, img = cap.retrieve()
>>> ret
False ←取り込み失敗
>>> img is None
True ←imgはNone

マルチスレッドなどで実行すると、撮像が追随しない場合があります。つまり古い画像が取り込まれる場合があります。常に新しい画像を取得したい場合には工夫が必要です。解決方法が以下のページにありました。

how to get the latest frame from capture device (camera) in opencv [python]

Queueモジュールを利用します。まず、別スレッドで以下を常時繰り返します。
VideoCapture.read()で画像を撮り込む ⇒ キューに画像が入っている時は画像を取り出し、空の時はキューに画像を入れる

次に、メインスレッドからキューに入っている画像を撮りだします。常に新しい画像になっているはずです。仮にキューが空だった時は、キューに画像が入るまで待機してくれます。(Queue.get()の機能)

こちらに自作サンプル「camera_uvc.py」を置いておきます。ダウンロードして、Libフォルダにコピーしてください。以下のような感じで使います。

>>> import camera_uvc import *
>>> cap = BufferlessCapture()
>>> img = cap.read()
>>>
>>> help(BufferlessCapture) ← Helpでメソッドの一覧が見れます
Help on class BufferlessCapture in module camera_uvc:

class BufferlessCapture(builtins.object)
 |  Get the latest frame from capture device (camera).
 |  
 |  Methods defined here:
 |  
 |  __init__(self, id=0, width=640, height=480, exposure=-6)
 |      Initialize
 |  
 |  read(self)
 |      Read

ひとこと

ライブラリによって撮像の考え方がずいぶん異なるな~と思いました(例えば、opencv-pythonとpypylon)。理解するのが大変。


Homeへプログラミングの記事Topへ