カメラからの画像取込を、なぜ、わざわざ別スレッドにしたいか?というと、よくある画像取込のプログラム
import cv2
# カメラを開く
cap = cv2.VideoCapture(0)
while True:
# 画像をキャプチャする
_, frame = cap.read()
# 画像を表示する
cv2.imshow("Image", frame)
# キーを押すとループを終了する
if cv2.waitKey(1) > 0:
break
# カメラを閉じる
cap.release()
# すべてのウィンドウを閉じる
cv2.destroyAllWindows()
を実行し、画像が表示されたウィンドウのタイトルバーの部分をマウスでクリックすると、while文のループ内で、waitKey(1) の部分で処理が停止します。
処理が停止するということは、画像の表示(imshow())や画像の取込(read())も、つられて実行されなくなってしまいます。
画像の表示は、最悪、停止してもしょうがないとしても、画像の取込が停止するのは、画像の取込ながら画像処理する場合には、不都合です。
そこで、画像の取込部分を別スレッドにしたプログラムがこちら↓
import threading
import queue
import cv2
# カメラを開く
cap = cv2.VideoCapture(0)
# フレーム共用のキュー
q_frames = queue.Queue()
def frame_update():
'''画像取込'''
while True:
# カメラ画像の取得
_, frame = cap.read()
# 取得した画像をキューに追加
q_frames.put(frame)
print(f"qsize:{q_frames.qsize()}")
# 画像取り込みスレッドの作成
thread = threading.Thread(target=frame_update, daemon=True)
# スレッドの開始
thread.start()
# 画像表示用スレッド
while True:
frame = q_frames.get() # queueが空の場合、キューに何か入るまで、スレッドをブロックする。
cv2.imshow("Image", frame)
# キーを押すとループを終了する
if cv2.waitKey(1) > 0:
break
# カメラを閉じる
cap.release()
# すべてのウィンドウを閉じる
cv2.destroyAllWindows()
このようにすることで、画像を表示しているウィンドウのタイトルバーの部分をクリックしても、画像の取込(read())が停止する事は無くなります。
ただし、少々、厄介なのが、別スレッドからGUIの表示を行う事が出来ないので、queueを介して画像データをメインスレッド側で画像を表示するようにしています。
今回は、waitKeyの部分で処理が停止してしまうのを、スレッドで回避していますが、waitKeyの関数に限らず、処理が停止してしまう関数が他にもあるので、注意が必要です。