OpenCV for Pythonの動画入力をThreadingで高速化する
概要
モチベーション
通常のVideoCapture
は動画の読み込み時にメインスレッドが止まってしまうので速度があまり出ない.
そこでthreading
を使うことで高速化を試みた.
既に同じことを行ってる英語記事があるが,
この実装だとカメラからの入力が遅くてキューが空っぽになってしまったらメインスレッドが変なところで終了するのと,
OpenCVのVideoCapture
の関数と戻り値の形式が異なったりして扱いにくかったので,
そのあたりの問題を解消しつつ書いてみた.
つくったもの
import threading import queue import cv2 class ThreadingVideoCapture: def __init__(self, src, max_queue_size=256): self.video = cv2.VideoCapture(src) self.q = queue.Queue(maxsize=max_queue_size) self.stopped = False def start(self): thread = threading.Thread(target=self.update, daemon=True) thread.start() return self def update(self): while True: if self.stopped: return if not self.q.full(): ok, frame = self.video.read() self.q.put((ok, frame)) if not ok: self.stop() return def read(self): return self.q.get() def stop(self): self.stopped = True def release(self): self.stopped = True self.video.release() def isOpened(self): return self.video.isOpened() def get(self, i): return self.video.get(i)
使い方
なるべくOpenCVのVideoCapture
と同じ仕様になるように設計してあるので,
下記記事のコードがほぼそのまま使える
(video.start()
を追加していることに注意.__init__
の中にこれも含めてしまってもよかったかもしれん).
ここでThreadingVideoCapture
本体と依存パッケージの読み込みは省略してある.
import numpy as np import cv2 # 動画ファイルを開く video = ThreadingVideoCapture('/path/to/video') # キューのサイズを変えるとき # video = ThreadingVideoCapture('/path/to/video', max_queue_size=1024) # PCに接続されたカメラの映像を表示 # video = ThreadingVideoCapture(0) if not video.isOpened(): raise RuntimeError video.start() cv2.namedWindow('frame', cv2.WINDOW_AUTOSIZE) while True: ok, frame = video.read() if not ok: break cv2.imshow('frame', frame) key = cv2.waitKey(int(1000 / 30)) if key == ord('q'): break video.release() cv2.destroyAllWindows()
threadingを行うことで,例えば,もしcv2.imshow
の直前に1秒ぐらい時間のかかる画像処理をいれていたとしても,
その処理中にもカメラや動画のフレームを読み込んでキューに積んでおいてくれる.
そのためI/O待ちが発生しなくなってその分速くなる.
追記 (2021/9/14):
「1秒ぐらい時間のかかる」と書いてるけど実際はそんなに時間かかってるとキューがパンクするし,代わりにキューをでかくするとメモリがパンクするので注意.時間のかからない画像処理でもI/Oと並列化するだけでかなり速くなる.
あと,もっと速度上げたいならcv2.imshow
はかなり遅いのでデバッグ時以外は切った方がいい.