OpenCV の実用的で便利なコード集-並列処理編1 (Python)

OpenCV
スポンサーリンク

スポンサーリンク

はじめに

前回の記事では、カメラパラメータの変更方法について説明しました。

今回の記事では、カメラ映像を流しながら、時間のかかる処理をする場合に、カメラ映像を停止させないようにする方法について説明します。

前提条件

前提条件は、以下の通りです。

  • 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 を使用して、カメラ映像スレッドとメインスレッドを独立させて実装しました。
これを使用すれば、ロボットの動きを監視しながらカメラ映像を確認して、予期せぬ動作が実行された場合に、緊急停止ができるようになります。

次回は、オブジェクト指向のクラスを使用して、カメラキャプチャオブジェクトを作成し、並列処理をしながら、任意のタイミングでフレームを取得できるようにします。

コメント

タイトルとURLをコピーしました