コラ!勝手に持ってくんじゃない!!!〜WebカメラとOpenCVで俺のカップ麺を狙う奴に警告する〜
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
1 はじめに
防犯装置みたいなものをWebカメラとRaspberry Piで作成してみました。とりあえず動く程度のもので良ければ、比較的簡単に作れたので紹介させて下さい。
最初に動作している様子です。カップ麺の置き場はWebカメラで冠されています。不審な動きを検出すると、スピーカーから警告が発せられます。
2 動画
Webカメラは、USBに接続するだけで認識されていました。
1 2 3 4 5 6 | pi@raspberrypi:~ $ lsusb Bus 001 Device 004: ID 046d:081b Logitech, Inc. Webcam C310 ・・・ pi@raspberrypi:~ $ ls -la /dev/video * crw-rw----+ 1 root video 81, 0 Sep 14 12:58 /dev/video0 |
続いて、OpenCVとPython版OpenCVパッケージをインストールします。
1 2 | $ sudo apt-get install libopencv-dev $ sudo apt-get install python-opencv |
ここまでの作業で以下のコードで動画が表示出来ます。
1 2 3 4 5 6 7 8 9 10 11 12 | import cv2 cap = cv2.VideoCapture( 0 ) while ( cap.isOpened() ): ret, frame = cap.read() cv2.imshow( 'frame' ,frame) if cv2.waitKey( 1 ) ! = - 1 : break cap.release() cv2.destroyAllWindows() |
3 画像の調整
今回は、取得した動画のフレームごとの差分を確認したいわけですが、あまり情報量が多いと判定が厄介なので、ちょっと畳み込んで見ました。
(1) 画像サイズとフレーム数
まずは、画像サイズを320×240とし、フレームも4に減らしました。FPSは1ぐらいでも充分かも知れません。
1 2 3 4 5 | cap = cv2.VideoCapture( 0 ) cap. set ( 3 , 320 ) # WIDTH cap. set ( 4 , 240 ) # HEIGHT cap. set ( 5 , 4 ) # FPS |
(2) グレースケール
RGBを比較するのは面倒なので、白黒にしちゃいました。
1 2 | ret, frame = cap.read() frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) |
(3) モザイク
ドット数が多いのも大変なので、1/10(32×24)のに落としました。
1 2 3 | def mosaic(src): dst = cv2.resize(src, None , fx = 0.1 , fy = 0.1 , interpolation = cv2.INTER_NEAREST) return cv2.resize(dst, src.shape[: 2 ][:: - 1 ], interpolation = cv2.INTER_NEAREST) |
4 変化の検出
下記のコードは、1つ前のフレームとの差を比較してダンプ表示しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | def difference(a, b): if (a < b): return b - a return a - b def dump(src, keep): os.system( 'clear' ) count = 0 for y in range ( 0 , 24 ): str = '' for x in range ( 0 , 32 ): target = src[y * 10 ][x * 10 ] diff = difference(keep[y][x],target) if diff > 50 : str + = '{:02} ' . format (diff) count + = 1 else : str + = '-- ' keep[y][x] = target print ( str ) return count |
モザイクの情報なので、確認するのは10×10ドット単位です。白黒の階調を取得して、前回のものと比較しています。
比較した値(diff)が50を超えた場合に「変化あり」を検出し、そのドット数をカウントしています。
しきい値を50としたのは、カップ麺置き場の前を通っただけで、その影が画像に反映され敏感に反応したりするのを防ぐためです。この数字は、環境によって微妙に調整が必要でしょう。
このdump()では変化したドットの総数を返し、メインのルーチンで、この数が一定数を超えたら画像に変化があったと判定ようになっています。ここでの「一定数」も、若干のノイズを無視するためのものです。
5 コード
実装したコードの全部です。(モザイクは、表示確認のためだけに書かれており、実際にロジックでは使用されていません)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | import os import cv2 def mosaic(src): dst = cv2.resize(src, None , fx = 0.1 , fy = 0.1 , interpolation = cv2.INTER_NEAREST) return cv2.resize(dst, src.shape[: 2 ][:: - 1 ], interpolation = cv2.INTER_NEAREST) def difference(a, b): if (a < b): return b - a return a - b def kora(): os.system( 'mpg321 -q /home/pi/camera/kora.mp3&' ) def dump(src, keep): os.system( 'clear' ) count = 0 for y in range ( 0 , 24 ): str = '' for x in range ( 0 , 32 ): target = src[y * 10 ][x * 10 ] diff = difference(keep[y][x],target) if diff > 50 : str + = '{:02} ' . format (diff) count + = 1 else : str + = '-- ' keep[y][x] = target print ( str ) return count cap = cv2.VideoCapture( 0 ) cap. set ( 3 , 320 ) # WIDTH cap. set ( 4 , 240 ) # HEIGHT cap. set ( 5 , 30 ) # FPS keep = [[ 0 ] * 32 for i in range ( 24 )] busy = True while ( True ): ret, frame = cap.read() gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) mos = mosaic(gray) count = dump(gray, keep) print ( '{0} {1}' . format (count,busy)) if busy and count = = 0 : busy = False if busy = = False and count > 5 : busy = True kora() cv2.imshow( 'frame' ,frame) cv2.imshow( 'mos' ,mos) if cv2.waitKey( 1 ) ! = - 1 : break cap.release() cv2.destroyAllWindows() |
6 最後に
今回は、不審な動作の検出だけを行いましたが、これをトリガーにして、不審者の画像を撮影したり、AWS IoTなどでクラウドに接続して、リアルタイムにメッセージを飛ばすような仕組みも作れそうです。
もしかすると、ペットの様子を見たり、何らかの見守り、防犯装置的なものも作れそうです。楽しくないですか!
7 参考にさせて頂いたリンク
OpenCV カメラから動画を撮影する
OpenCV 色空間の変換
Python, OpenCVで画像にモザイク処理(全面、一部、顔など)