育児×家事×IoT Raspberry Piで最強の防犯カメラを作ってみる(動画記録・配信、動体検知・Line通知、顔検知・顔認証、Alexa搭載)[4/6]

やりたいこと(再掲)

最終的にやりたいことは、以下の5つの機能を持つ「最強の防犯カメラ」を作ることです。機能①〜機能③は、市販されている多くの防犯カメラでも持っている機能ですが、機能④や機能⑤の顔認証機能を持つ監視カメラはまだ多くないと思います。

 機能①.動画を24時間撮影し、カメラ本体に動画で記録する
 機能②.動画をWebブラウザや他の機器から参照できるようにライブ配信する
 機能③.動体を検知したら、静止画をLineに通知する
 機能④.家族の顔を認証したら、静止画をLineに通知する
 機能⑤.家族の顔を認証したら、◯◯さんおかえり!と喋る

図1.png

家に帰ると、顔を見て「○○さん、おかえり!」と言ってくれる辺りが、スマートハウスに一歩づ近づいている気がします。

実現に向けた連載

最強の「防犯カメラ」を作成するために、以下のように少しずつに記事を書いていきます(予定)。
 1回目:カメラの設定と動画記録
 2回目:カメラ映像のライブ配信
 3回目:動体検知機能とLineへの通知
 4回目:顔認証機能とLineへの通知   ←この記事
 5回目:Raspberry PiへのAlexaの搭載
 6回目:顔認証後にAlexaで音声通知

4回目:顔認証機能とLineへの通知

前回は動体検知機能を作って、カメラが動体を検知した時にLineに通知するようにしてみました。今回は、いよいよ顔検知・顔認証機能を作っていきます。

今回はReal-Time Face Recognition: An End-to-End Projectで紹介されている方法で、顔検知・顔認証機能を作っていきます。

1.認証用の顔データの学習

顔認証を行うためには、認証する顔データを予め学習させておく必要があります。まずは、顔データの撮影と学習を行っていきます。

1.防犯カメラサービスの停止

まずは、防犯カメラサービスを止めておきます。

root@raspberrypi:/home/pi/camera# systemctl stop flask.service

2.顔データ撮影用のプログラムのダウンロード

顔データ撮影用のプログラムは自分で作っても良いですが、Real-Time Face Recognition: An End-to-End Projectで利用しているものがGitHubにあるので、これを利用します。以下のコマンドを入力して「01_face_dataset.py」をダウンロードします。

root@raspberrypi:/home/pi/camera# wget https://raw.githubusercontent.com/Mjrovai/OpenCV-Face-Recognition/master/FacialRecognition/01_face_dataset.py

このプログラムでは、カメラで画像を撮影し、その画像の中にある人の顔を認識して30枚の顔画像ファイルを出力するものです。

3.顔検知用の正面顔特徴分類器のダウンロード

次に「01_face_dataset.py」で顔検知するために必要な、正面顔用の特徴分類器「haarcascade_frontalface_default.xml」をOpenCVのGitHubからダウンロードします。

root@raspberrypi:/home/pi/camera# wget https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_default.xml

4.画像の格納ディレクトリの作成

画像を保存するディレクトリ「dataset」を作成しておきます。

root@raspberrypi:/home/pi/camera# mkdir dataset

5.登録用画像の撮影

それでは「01_face_dataset.py」を実行して、顔を撮影します。このプログラムは、画面に撮影の様子を表示するので、VNCでログインしたコンソールから実行します。また、カメラにアクセスするのでroot権限での実行が必要です。

root@raspberrypi:/home/pi/camera# python 01_face_dataset.py

プログラムを実行すると、以下のようにユーザIDを聞かれます。ユーザIDは、登録する人ごとに1からの連番を振るものです。一人目の登録であれば「1」を入力してエンターを押します。すると、画面にカメラの映像が表示されるので、カメラの前に立って顔を撮影します。顔を認識すると、青色の枠で表示されるので、様々な表情や近さで撮影してみてください。1〜2分ほどカメラを見ていると30枚分の顔画像が撮影され、プログラムが終了します。
図1.png
なお、複数の人の画像を登録する場合は、もう一度プログラムを起動して、ユーザIDに2などを入力して同様に撮影して下さい。

6.学習用のプログラムのダウンロード

次に、撮影した顔データを学習させるためのプログラム「02_face_training.py」をダウンロードします。

root@raspberrypi:/home/pi/camera# wget https://raw.githubusercontent.com/Mjrovai/OpenCV-Face-Recognition/master/FacialRecognition/02_face_training.py

7.02_face_training.pyの変更

オリジナルの「02_face_training.py」は、OpenCV「3.3.0」以上で動作するので、RaspberryPiにインストールされる「3.2.0」で実行するためには、以下の2点の修正を行う必要があります。

変更点1:21行目あたり「LBPHFaceRecognizer_create()」を以下のように変更。

#recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer = cv2.face.createLBPHFaceRecognizer()

変更点2:51行目あたり「recognizer.write()」を以下のように変更。

#recognizer.write('trainer/trainer.yml')
recognizer.save('trainer/trainer.yml')

8.学習済みデータ格納ディレクトリの作成

学習済みデータを格納する「trainer」ディレクトリを作成します。

root@raspberrypi:/home/pi/camera# mkdir trainer

9.学習用のプログラムの実行

それでは「02_face_training.py」を実行して、撮影した顔データを学習させます。「trainer」フォルダに「trainer.yml」が作成されていればOKです。

root@raspberrypi:/home/pi/camera# python 02_face_training.py 

 [INFO] Training faces. It will take a few seconds. Wait ...

 [INFO] 2 faces trained. Exiting Program

root@raspberrypi:/home/pi/camera# ls -l trainer/
合計 7232
-rw-r--r-- 1 root root 7404199  2月  1 15:58 trainer.yml

以上で、認証用の顔データの学習は完了です。

2.Line通知機能の修正

ここからは、顔が認証された場合にLineへ通知する機能を作成していきます。

1.functionノードの修正

Node-redの一つ目のfunctionノードに顔認証用の分岐条件を追加します。プログラムの内容は、ほぼ動体検知の場合と同じです。

function
let files = msg.req.files;
let action=msg.payload.action;
let text=msg.payload.text;

let msg1 = msg;
let msg2 = {};

//ファイル無し
if (files.length === 0) {
   msg1.payload = "サーバ通知ERR 添付ファイルなし";
   return [msg1, null];

//ファイル有り
}else{
    //動体検知
    if(action=='move'){
        msg1.payload = "サーバ通知OK";
        msg2.text = text;
        msg2.payload = files[0].buffer;
        msg2.filename = "/root/.node-red/cameraPicts/" + files[0].originalname;
        return [msg1, msg2];
    //-----ここから追加------------------------
    //顔認証
    }else if(action=='goHome'){
        msg1.payload = "サーバ通知OK";
        msg2.text = text+"が帰ってきました";
        msg2.payload = files[0].buffer;
        msg2.filename = "/root/.node-red/cameraPicts/" + files[0].originalname;
        return [msg1, msg2];
   //-----ここまで追加------------------------
    //その他
    }else{
        msg1.payload = "サーバ通知ERR アクション不正";
        return [msg1, null];
    }
}

2.ノードのデプロイ

画面右上の「デプロイ」ボタンを押して、作成したフローをデプロイします。
図16.png

3.顔検知・顔認証プログラムの作成

ここからは、Camera.pyに顔認証機能を追加していきます。なお、完全なソースコードはGitHubのnaka-kazz/RasPiCameraに置いたので、適宜参照してください。

1.定数定義の追加

まずは、Camera.pyの定数定義の一番下に、定数定義を追加します。

/home/pi/camera/Camera.py
###################################################
## 定数定義
###################################################
#名前変数
names=['none','papa','mama','taro','hanako','zirou']

# 顔認証のための学習データを読み込む
cascadePath ="haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascadePath);
recognizer = cv2.face.createLBPHFaceRecognizer()
recognizer.load('trainer/trainer.yml')

namesは、ライブ配信映像やログに顔を検知した時に名前を表示させるものです。配列要素の1番目から、顔の撮影時に登録したユーザIDの順に名前を入力して下さい。

faceCascadeは、カメラ画像の中から顔を検知するためのものです。また、recognizerは顔認証のためのエンジンであり、上で学習させた顔データをロードしています。

2.faceDetect関数の追加

今回のメインである、顔認証の機能であるfaceDetect関数を、moveDetect関数の下に追加します。

/home/pi/camera/Camera.py
    ###################################################
    ## 顔認証のためのメソッド
    ###################################################
    @staticmethod
    def faceDetect(img):
        #入力画像をグレースケールに変換
        grayImg=cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        #顔検知を行う
        faces = faceCascade.detectMultiScale(grayImg,scaleFactor = 1.2,minNeighbors = 5,minSize = (50, 50))

        # 顔が検出された場合
        for(x,y,w,h) in faces:
            #顔認証を行う
            no, confidence = recognizer.predict(grayImg[y:y+h,x:x+w])

            # 信頼度が0〜100でない場合は終了
            if (confidence < 0 and confidence > 100):
                break

            #現在日付を取得
            nowstr=datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
            nowTime=time.time()

            #ログ出力
            print(nowstr+' 顔検知  '+names[no]+':'+"  {0}%".format(round(100 - confidence)))
            #サーバに通知(一定時間間隔) 
            if int(nowTime) - befTimes[no] > interval:
                #検知時間を保存
                befTimes[no]=int(nowTime)
                #ファイルに保存 
                filename=pictpath+'/'+names[no]+nowstr+'.jpg'
                cv2.imwrite(filename, img)
                #サーバ通知
                file=open(filename, 'rb').read()
                files = {'file': (names[no]+nowstr+'.jpg', file, 'image/jpeg')}
                params = {'action': 'goHome', 'text': str(names[no])}
                response = requests.post(url, files=files, data=params)
                #サーバ通知のログを出力
                print(nowstr+' サーバ通知 顔'+str(response.status_code)+':'+str(response.content))

            #配信画像に枠と名前を付ける
            cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
            cv2.putText(img, str(names[no]), (x+5,y-5), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,255,255), 2)
            cv2.putText(img, str(confidence), (x+5,y+h-5), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,255,0), 1)

        return;

プログラムの特徴的な部分だけ説明していきます。まず、顔認証に色情報は不要なので、処理を軽くするために、カメラの画像をグレースケール画像に変換しています。

次に「faceCascade.detectMultiScale()」で、カメラで撮影した画像の中の人の顔を認識します。そして、次のforループの中で、認識されたそれぞれの顔について「recognizer.predict()」で顔認証を行っています。OpenCVを使うと、この一行だけで顔認証が行えます。

「recognizer.predict()」の戻り値は、顔を登録した時のユーザIDの番号と信頼度の2つです。信頼度が0以上で100以下だったら、顔認証OKとして判断し、ログ出力やサーバ通知を行っています。

また「#配信画像に枠と名前を付ける」の部分は、ライブ配信する画像に認識した顔の周りに枠をつけて、名前や信頼度を表示させるためのものです。

3.faceDetect関数の呼出し部分の追加

上で定義したfaceDetect関数を呼び出す部分を、moveDetect関数内の「#ログ出力」の下に追加します。動体検知後に顔認証を行うことで、全体の処理を軽くできます。

/home/pi/camera/Camera.py
#ログ出力
print(nowstr+' 動体検知 '+filename+' '+str(max_area))

#顔認証
Camera.faceDetect(img) #<------------------ここに追加

#サーバに通知(一定時間間隔)
if int(nowTime) - befTimes[0] > interval:

4.テスト

それでは、作成したプログラムを動かしてみましょう。プログラムが起動したら、カメラに顔を向けてみて下さい。以下のようにログが表示されて、Lineに通知が来れば完成です!!

root@raspberrypi:/home/pi/camera# ./cameraServer.sh 
Starting camera thread.
 * Serving Flask app "cameraServer" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
20200201_185254 動体検知 /home/pi/camera/picts/move_20200201_185254.jpg 1661.0
20200201_185254 サーバ通知 動体 200:サーバ通知OK
20200201_185256 顔検知  mama:  32.0%
20200201_185256 サーバ通知 顔200:サーバ通知OK

5.チューニング

プログラムがちゃんと動くようになったら、顔認証の精度を高くできるように、顔データやパラメータを調整してみて下さい。

・顔データ:光の当たり方や顔の大きさなどで、認証できたりできなかったりします。認証の精度を高めるためには、実際に防犯カメラを設置する場所に置いて、学習用の顔データを撮影すると良い結果となりました。
・minSize:faceCascade.detectMultiScale()のminSizeパラメータは、顔検知を行う最小サイズを設定します。カメラから離れている状態で顔認証してしまうと、範囲が狭く範囲内のピクセル数が少ないので精度が落ちてしまいます。これも、実際の設置場所に合わせて大きな値とすると精度が高くなりました。
・confidence:ドアセキュリティに利用する場合など、高い信頼性が求められる場合には、confidenceの判定条件をキツくした方が良いと思います。

ちなみにチューニングを繰り返していると、我が家では80%ぐらいの精度でそれぞれの人の顔を認証できるようになりました。たまに、子供の顔をパパとして認証してしまいますが、これは親子の顔が似ているからなんですかねー?

6.CPU・メモリ使用率

ここまでの動画配信・記録、動体検知・line通知、顔検知・顔認証機能を含む防犯カメラをRaspberry Pi 3B+で実行した時のCPU使用率、メモリ使用率は以下のとおりでした。

・平常時(動画配信・記録時)
図2.png
・繁忙時(動体検知・顔認証の動作時)
図3.png
この結果からも、十分にRaspberry Pi 3B+で動作可能かと思います。あとはAlexaの動作に耐えられるかです。

おわりに

今回は防犯カメラに顔認証機能を追加し、Lineに通知することをやってみました。次回は、防犯カメラにAlexaを搭載したいと思います。

間違い・改善点や質問など、あったらコメント欄に書いていただければと思います。

連載記事

 1回目:カメラの設定と動画記録
 2回目:カメラ映像のライブ配信
 3回目:動体検知機能とLineへの通知
 4回目:顔認証機能とLineへの通知  <--この記事
 5回目:Raspberry PiへのAlexaの搭載
 6回目:顔認証後にAlexaで音声通知

育児×IoTスマートホームの関連記事

Alexaをしゃべらせる(remote2 v3.3編)
自分のGoogleカレンダーにJWTを使って予定を追加してみる
Alexaに通知を送ってみる(Node-red編)
Node-redでスマートハウス-LG製テレビを操作してみる
Node-redでスマートハウス-Gravioで子供用プリキュアボタン
育児×IoT Gravioでミルク&オムツ交換記録をGoogleカレンダにつけてみる
育児×IoT HomeAssistantでスマートホームコントローラを作ってみる

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account