はじめに
前回の記事では、カメラパラメータの変更方法について説明しました。
今回の記事では、カメラ映像を流しながら、時間のかかる処理をする場合に、カメラ映像を停止させないようにする方法について説明します。
前提条件
前提条件は、以下の通りです。
- Python == 3.9.13
- opencv-python == 4.6.0
- numpy == 1.23.4
- Windows11
また、今回はロジクールの C1000eR を使用しますが、プログラムの話になるので、どんなカメラでも問題ありません。
その他の条件も、バージョンが異なっても大丈夫です。
並列処理が必要になった経緯
並列処理が必要になった経緯についてですが、
- Dobot を使用して、3D-Picking する際、Dobot からの処理完了を待機していると、カメラのフレームが停止してしまう
- QRコードを読み取りする際に、高解像度にして処理をすると時間がかかる。その間カメラのフレームが停止してしまう
ことがあったからです。
どのように並列処理をするか
Python で並列処理を実施するには、ThreadPoolExecutor を用います。
ThreadPoolExecutor についての詳細な説明はこちらにあります。
この記事でも、後の方で詳細な説明をします。
今回は、カメラキャプチャを ThreadPoolExecutor で実装し、時間のかかる処理は、メイン関数にて実装します。なんだそれとなるかもしれませんが、ご容赦ください。
実装方法
早速、実装していきます。
ファイル名は、thread_capture.py とします。
import cv2
import time
from concurrent.futures import ThreadPoolExecutor
def threadCapture(arg1, arg2):
print("argument: ", arg1, arg2)
cap = cv2.VideoCapture(1, cv2.CAP_DSHOW)
count = 0
cap.set(cv2.CAP_PROP_SETTINGS, 0)
while True:
ret, frame = cap.read()
if ret:
frame = cv2.putText(frame, "count: "+str(count), (50, 50),
cv2.FONT_HERSHEY_SIMPLEX, 2, (255,255,255),2,cv2.LINE_8)
cv2.imshow("frame", frame)
count += 1
lastkey = cv2.waitKey(10)
if lastkey == ord("q"):
cap.release()
cv2.destroyAllWindows()
break
if __name__ == "__main__":
executor = ThreadPoolExecutor(max_workers=3)
camera_future = executor.submit(threadCapture, "arg1", "arg2")
while True:
if camera_future.running() == False:
print("camera shutdown")
executor.shutdown()
break
else:
time.sleep(5)
print("5 seconds ...")
print("program complete")
プログラムの説明をしていきます。
import cv2
import time
from concurrent.futures import ThreadPoolExecutor
concurrent.futures の ThreadPoolExecutor のみ import します。
def threadCapture(arg1, arg2):
print("argument: ", arg1, arg2)
cap = cv2.VideoCapture(1, cv2.CAP_DSHOW)
count = 0
cap.set(cv2.CAP_PROP_SETTINGS, 0)
while True:
ret, frame = cap.read()
if ret:
frame = cv2.putText(frame, "count: "+str(count), (50, 50),
cv2.FONT_HERSHEY_SIMPLEX, 2, (255,255,255),2,cv2.LINE_8)
cv2.imshow("frame", frame)
count += 1
lastkey = cv2.waitKey(10)
if lastkey == ord("q"):
cap.release()
cv2.destroyAllWindows()
break
threadCapture 関数を用意しました。
説明のために、引数を 2つ とるようにしました。
cv2.putText で、フレームのカウントもとるようにしました。
そのほかの部分は、前回までのベースプログラムと同じです。
executor = ThreadPoolExecutor(max_workers=3)
camera_future = executor.submit(threadCapture, "arg1", "arg2")
ThreadPoolExecutor(max_workers=3) は、最大 CPU スレッド使用数を指定します。
executor.submit(threadCapture, “arg1”, “arg2”) は threadCapture 関数に “arg1”, “arg2” の引数を渡して実行を開始します。
あとは、threadCapture 関数の無限ループが実行されます。
if camera_future.running() == False:
print("camera shutdown")
executor.shutdown()
break
camera_future.running() は、スレッドが実行中かどうかを True か False で返す関数です。
executor.shutdown() は、スレッドを完全に終了させます。
time.sleep(5)
print("5 seconds ...")
メインスレッドを、5 秒間停止させます。停止したかどうかわからないので print を使用して 5 秒ごとにターミナルに表示させます。停止している間もカメラが動いていれば成功です。
早速、動かしてみてください。
フレームのカウント数が増えている間に、ターミナルに 5 seconds … が表示されていれば、マルチスレッド処理ができています。
おわりに
今回は、ThreadPoolExecutor を使用して、カメラ映像スレッドとメインスレッドを独立させて実装しました。
これを使用すれば、ロボットの動きを監視しながらカメラ映像を確認して、予期せぬ動作が実行された場合に、緊急停止ができるようになります。
次回は、オブジェクト指向のクラスを使用して、カメラキャプチャオブジェクトを作成し、並列処理をしながら、任意のタイミングでフレームを取得できるようにします。
コメント