コラ!勝手に持ってくんじゃない!!!〜WebカメラとOpenCVで俺のカップ麺を狙う奴に警告する〜

1 はじめに

防犯装置みたいなものをWebカメラとRaspberry Piで作成してみました。とりあえず動く程度のもので良ければ、比較的簡単に作れたので紹介させて下さい。

最初に動作している様子です。カップ麺の置き場はWebカメラで冠されています。不審な動きを検出すると、スピーカーから警告が発せられます。

2 動画

Webカメラは、USBに接続するだけで認識されていました。

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パッケージをインストールします。

$ sudo apt-get install libopencv-dev
$ sudo apt-get install python-opencv

ここまでの作業で以下のコードで動画が表示出来ます。

import cv2

cap = cv2.VideoCapture(0)

while(True):
    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ぐらいでも充分かも知れません。

cap = cv2.VideoCapture(0)

cap.set(3,320) # WIDTH
cap.set(4,240) # HEIGHT
cap.set(5,4) # FPS

(2) グレースケール

RGBを比較するのは面倒なので、白黒にしちゃいました。

ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

(3) モザイク

ドット数が多いのも大変なので、1/10(32×24)のに落としました。

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つ前のフレームとの差を比較してダンプ表示しています。

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 コード

実装したコードの全部です。(モザイクは、表示確認のためだけに書かれており、実際にロジックでは使用されていません)

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で画像にモザイク処理(全面、一部、顔など)