Zoom会議で使うビデオカメラを使って、蚊の飛来を検知できないだろうか。
まずはビデオカメラから画像を取得して画面に表示してみる。cv2.VideoCapture()
の引数はカメラ番号で、カメラが1台であれば 0
のはずである:
#! /usr/bin/env python3 import cv2 capture = cv2.VideoCapture(0) # 0, 1, 2, ... if not capture.isOpened(): print("Cannot open camera") exit() while (True): ret, frame = capture.read() if not ret: break cv2.imshow("Frame", frame) if cv2.waitKey(1) == ord("q"): break capture.release() cv2.destroyAllWindows()
これを例えば test.py
という名前で保存し、実行許可を与え(chmod 755 test.py
)、実行(./test.py
)する。Mac では、最初はカメラにアクセスする許可がないので失敗する。システム環境設定の「セキュリティとプライバシー」の「プライバシー」で、「変更するにはカギをクリックします」をしてから、左側「カメラ」、右側「ターミナル」のチェックを付け、ターミナル(で動くPython)からカメラがアクセスできるようにすると、実行できるようになる。ビデオ表示画面がアクティブな状態でキーボードの「Q」を押すと止まる。
このプログラムを少しずつ改良して、蚊が飛んだらアラートが出る(例えば音を鳴らし画面に位置を表示する)ようにしてみよう。
まず考えられるのは、直前の画像を保存しておき、差分をとって、それがある条件を満たせば、アラートを出すことであろう。カラーの差分は解釈が難しいので、グレースケールに直してから差分の絶対値を求める。それをさらに画素値40を境として2値化し、白(つまり40以上変化のあったピクセル)の個数が一定レベルを超えればアラートを出す。ここでは個数を画面に出力し、いわゆるビープ音を出すための制御文字 "\x07"
を出力し、2値化した差分を画面に表示している:
#! /usr/bin/env python3 import cv2 import numpy as np capture = cv2.VideoCapture(0) # 0, 1, 2, ... if not capture.isOpened(): print("Cannot open camera") exit() prev = None while (True): ret, frame = capture.read() if not ret: break cv2.imshow("Frame", frame) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) if prev is not None: delta = cv2.absdiff(gray, prev) ret, thresh = cv2.threshold(delta, 40, 255, cv2.THRESH_BINARY) # 40は要調整 n = np.count_nonzero(thresh) if n > 10: # 10は要調整 print(n, "\x07") cv2.imshow("Frame", thresh) prev = gray if cv2.waitKey(1) == ord("q"): break capture.release() cv2.destroyAllWindows()
さらに cv2.imwrite("filename.png", frame)
などとしてアラート時の画像を出力すると後で便利である。このようにして出力した画像例(EMEET NOVA HD1080P 1920×1080だが、蚊らしいものの写っているところあたりを256×256に切り出したもの)と、両者の差分の輪郭を緑で示したものを、次に示す:
輪郭を出力するには、例えば次のようにする:
contours, hierarchy = cv2.findContours( thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours(frame, contours, -1, (0, 255, 0), 3) cv2.imwrite("diff.png", frame)
ちなみに、両者の写真(切り出したものではなく全体)をグレースケールにしたものの差分の絶対値の和は 4372333、差分の絶対値の平均は約 2.1、差分の絶対値が 40 以上のピクセルは 48 個であった。
改良すべきところはいろいろ考えられる。まず、直前との差分ではなく、直前のいくつかのフレームの移動平均を使う方が安定するであろう。また、蚊は小さいので大きい変化は無視するようにするべきであろう。これらは簡単に実装できる(前者は cv2.accumulateWeighted()
を使う)。蚊の形状を判断するのは課題である。
ビープよりましなアラート音を出すには、音のところで書いたようにすればよい。ただし sd.play()
で音が出ているときに再度 sd.play()
を呼び出したりしているとハングするようなので、適宜間隔をあける。
Webカメラにはけっこうノイズがある。次の例は、前後のフレームに何も写っていないので、ノイズだと思われる: