PythonでOpenCVのwaitKey()はメインスレッドじゃないと動かない?

マルチスレッドとOpenCVの勉強を同時進行でやろうと、PythonでOpenCVをマルチスレッドで動かそうとした時にちょっとハマったのでメモ。

初めのコード

やりたかったのは、描画するウィンドウ関係の処理は全部それ用のスレッドに投げてしまって、メインスレッドはスレッドを起動するだけ、みたいなこと。どうせスレッドたくさん立てる事態になったらこういうことしたくなるんだろうな、と。

import threading
import cv2

# 描画スレッド
class ImageProcessing(threading.Thread):

	def __init__(self):

		threading.Thread.__init__(self)

		self.windowname = "ImageProcessing"
		cv2.namedWindow(self.windowname)
		self.capture = cv2.VideoCapture(-1)

	def run(self):

		while True:
			ret, frame = self.capture.read()
			cv2.imshow(self.windowname, frame)

			if cv2.waitKey(10) == 27: # ESC
				break

# メインスレッド
if __name__ == "__main__":

	ip = ImageProcessing()
	ip.start()

	ip.join()

で、動きませんでした。初めの画像が映るだけで画像が更新されない静止画状態。ついでにwaitKey() も反応しない。ウィンドウが完全にフリーズしたような状態。

そして当のスレッドは走りっぱなしなのでターミナルを強制終了したり立ち上げたりが本当に面倒だった。

何が悪いのか

取得した frame を適当にターミナルに出力してみると値自体は更新されているようなので、問題はウィンドウの更新らへんにありそう。でもその辺りのことはあまり詳しくない。 試行錯誤の途中、スレッドを走らせる際の start() を run() に書き換えると画像も更新されてwaitKey()も反応するようになった。

if __name__ == "__main__":
    ip = ImageProcessing()
    ip.run()
    ip.join()

ただこれだと、スレッディングではなくメイン文の中で描画ループを回しているだけ。

で、検索してみた結果、C++だけど似たような報告があったので、どうやらwaitKey()はメインスレッドじゃないと動かないってことでいいのかな。不便だけど、もしそうなら他の人はどうしてるんだろう。

imshow() and waitkey() from within a thread –> not updating the display

結局処理をメインスレッドに

というわけで描画ウィンドウの生成と waitKey() はメインスレッドに移した。ついでに終了処理を渡したりするのが面倒なのでデーモンスレッドに

import threading
import cv2

class ImageProcessing(threading.Thread):

	# 描画ウィンドウ名を受け取るように
	def __init__(self, windowname):

		threading.Thread.__init__(self)

		# デーモンスレッドに
		threading.Thread.daemon = True

		self.windowname = windowname
		self.capture = cv2.VideoCapture(-1)

	def run(self):

		while True:
			ret, frame = self.capture.read()
			cv2.imshow(self.windowname, frame)

if __name__ == "__main__":

	# 描画ウィンドウ生成をメインスレッド内に
	windowname = "ImageProcessing"
	cv2.namedWindow(windowname)

	ip = ImageProcessing(windowname)
	ip.start()

	# waitKey() をメインスレッド内に
	while True:
		if cv2.waitKey(10) == 27: # ESC
			break

これで画像も更新されて waitKey() も動作するようになった。imshow() はメインスレッドじゃなくても動きそう。 でもこれだと、結局何がしたかったんだか…ってことに。メイン文が太るのはあんまり好きじゃない。何かいい方法はないものか。

  • Mac OS X 10.9
  • Python 2.7.4
  • OpenCV 2.4.4
Advertisement