どうも。まっきーです!
ラズパイとOpenCVを用いた「顔認証」「動体検知 」で監視(防犯)カメラを作成してみました。
「ラズパイを使って何かを自作したい!」と思っている方は参考にしてみてください!
初心者の方でもできるように説明していきます!
目次
完成イメージ図

実装する主要機能は以下の3つです。
- OpenCVで動体検知
- 顔認識(end to endリンク)
- LINE NotifyでLINEに通知
イメージ図のように、動体(不審な動き)を検知した場合、顔を認識した場合にLINEの通知を行うという簡易的な防犯カメラを作成していきます。
実際の動画
全体のフローチャート
全体的なフローは以下のようになります。

より実用的な監視(防犯)カメラにするために、動体検知後10秒以内に顔認証すれば、帰宅の通知。10秒たっても顔認証がされなかったら不審な動きと判断して通知を送るような機能にしてみました!
必要なもの
基本的に必要なものは以下の2つのみです!
用意するもの
- Raspberry Pi 3B+
- ラズパイカメラモジュール
ディレクトリ構造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
$ tree SecurityPJT │ ├── security_camera.py#メインプログラム ├── body_detect.py#動体検知プログラム ├── line.py#LINE通知用プログラム ├── face_recog.py#顔識別用プログラム │ ├── haarcascade_frontalface_default.xml#顔を取るための特徴量(OpenCvからダウンロード) ├── face_capture.py #顔認証用画像撮影プログラム ├── datasets #顔認証元画像保存ディレクトリ │ ├── User.1.1.jpg │ ├── User.1.2.jpg │ ├── User.1.3.jpg │ : │ (他の人を付け加えるならここに追加される) │ ├── face_train.py #顔画像訓練用 ├── trainer # 訓練データ保存先ディレクトリ │ └── trainer.yml └── image #動体検知・顔認証画像保存用ディレクトリ |
「datasets,trainer,image」のディレクトリはあらかじめ作成しておきます。
必要なライブラリ等のインストール
やることは3つのみです。
- OpenCVのインストール
- カスケード分類器のダウンロード
- LINE Notifyの登録
OpenCV
今回はOpenCVを使用しますので、OpenCVをダウンロードします。
ターミナルで以下のコマンドを入力してください。
1 2 3 4 5 6 7 8 9 10 |
sudo apt update sudo apt upgrade #必要なライブラリをインストール sudo apt-get install libatlas-base-dev sudo apt-get intall libjasper-dev sudo apt-get intall libqtgui4 sudo apt-get intall python3-pyqt5 sudo apt install libqt4-test #バージョン指定でOpenCVをインストール sudo pip3 install opencv-python==4.1.0.25 |
カスケード分類器のインストール
「SecurityPJT」ディレクトリで実行します。
1 |
wget https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_default.xml |
ダウンロードできない場合は「ラズパイ OpenCVインストール方法」 にも詳しく書いてありますので参考にしてみてください!
LINE Notify
LINENotifyの設定方法は「ラズパイからLINEに通知を送る with 人感センサー」に詳しくまとめてあるので、参考にしてください。(10分ぐらいで設定できます!)
-
-
ラズパイからLINEに通知を送る with 人感センサー
続きを見る
ソースコード
基本的にすべてのコードをコピペするだけで実装できると思います。細かなパラメーターの設定等、必要なところにはマークと解説を入れてあります!
body_detect.py
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 66 67 68 69 70 71 72 |
import cv2 import datetime import time def body_detect(): #画像保存ディレクトリ save_dir = './image/' file_suffix = 'detect.jpg' # カメラ映像を取得 cam = cv2.VideoCapture(0) #Video Graphics Array(略称:VGA)を設定 cam.set(3, 640) # set video 横 cam.set(4, 480) # set video 高さ #2値化したときのピクセル値 DELTA_MAX = 255 #ドットの変化を検知する閾値 DOT = 8 #比較用のデータを格納 avg = None while True: # 1フレームずつ取得する。 ret, frame = cam.read() if not ret: break #画像を反転 frame = cv2.flip(frame,-1) #画像ファイル名用の時間取得 date = datetime.datetime.now() file_name = str(date) + file_suffix # グレースケールに変換 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 比較用のフレームを取得するavgがNoneの場合進むつまり、値があればコピーしていく if avg is None: avg = gray.copy().astype("float") continue # 現在のフレームと移動平均との差を計算 cv2.accumulateWeighted(gray, avg, 0.6) frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(avg)) # デルタ画像を閾値処理を行う 閾値処理された後の二値画像が帰ってくる thresh = cv2.threshold(frameDelta, DOT, DELTA_MAX, cv2.THRESH_BINARY)[1] # 画像の閾値に輪郭線を入れる 戻り値は(画像、輪郭、階層) img,contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) #輪郭の面積を求めてデータサイズを超えるものを見つけた場合検知とする max_area = 0 for cnt in contours: area = cv2.contourArea(cnt)#この時の輪郭の値を計算して面積を求める if max_area < area: max_area = area if max_area > 8000:#面積は目的に適した値に調節 print("動体検知しました"+str(date)) #frameに枠を描画 #frame = cv2.drawContours(frame, contours, -1, (0, 255, 0), 3) cv2.imwrite(save_dir + file_name,frame) return save_dir + file_name else: pass frame = cv2.drawContours(frame, contours, -1, (0, 255, 0), 3) # 結果を出力 cv2.imshow("Frame", frame) key = cv2.waitKey(30) if key == 27: break cam.release() cv2.destroyAllWindows() |
動体検知をするためのコードです。マークがついている行の値を変更することによって、動体検知の精度を変更することができます。適当に変更してみて、ご自身の環境に適した値を見つけてみてください!
動体検知の仕組みについては、「ラズパイとOpenCVで動体検知」で詳しく解説しています。
-
-
ラズパイとOpenCVで動体検知
続きを見る
line.py
1 2 3 4 5 6 7 8 9 10 11 |
import requests def send(message,image): url = "https://notify-api.line.me/api/notify" access_token = '取得したアクセストークン' headers = {'Authorization': 'Bearer ' + access_token} payload = {'message': message} files = {'imageFile': open(image, 'rb')} r = requests.post(url, headers=headers, params=payload, files=files,) |
自身で取得したアクセストークンを入力することで、関数が使えるようになります!
face_recog.py
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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
import cv2 import numpy as np import os import datetime def face_recog(): #撮影画像保存用ディレクトリ設定 save_dir = './image/' file_suffix = 'recog.jpg' #顔認識の準備 recognizer = cv2.face.LBPHFaceRecognizer_create() recognizer.read('trainer/trainer.yml') cascadePath = "haarcascade_frontalface_default.xml" faceCascade = cv2.CascadeClassifier(cascadePath) font = cv2.FONT_HERSHEY_SIMPLEX #idの初期化 id = 0 # 名前とidを紐づける: 例 ==> まっきー: id=0, etc names = ['makky'] #必要に応じて増やす # カメラ初期化 cam = cv2.VideoCapture(0) cam.set(3, 640) # set video widht cam.set(4, 480) # set video height # 顔を認識する最小の長方形を定義 minW = 0.1*cam.get(3) minH = 0.1*cam.get(4) #制限時間を設定するための準備→現在時間(now)と+10秒した(end) now = datetime.datetime.now() end = now + datetime.timedelta(seconds = 10) while now <= end: ret, img =cam.read() img = cv2.flip(img) # 取得画像を逆さにしたい場合は(img,-1) gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #現在時間、ファイル名設定 date = datetime.datetime.now() file_name = str(date) + file_suffix #ループを抜けるための現在時間の取得 now = datetime.datetime.now() faces = faceCascade.detectMultiScale( gray, scaleFactor = 1.2, minNeighbors = 5, minSize = (int(minW), int(minH)), ) for(x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2) id, confidence = recognizer.predict(gray[y:y+h,x:x+w]) # 信頼度 100 ==> "0" confidenceが0に近いほど信頼度が高い if (confidence < 100): id = names[id] confidence = " {0}%".format(round(100 - confidence)) cv2.putText(img, str(id), (x+5,y-5), font, 1, (255,255,255), 2) cv2.putText(img, str(confidence), (x+5,y+h-5), font, 1, (255,255,0), 1) #cv2.putText(img, str(date), (x-50,y-50), font, 1, (255,0,0), 1) #撮影画像の保存 cv2.imwrite(save_dir+file_name,img) #ログ用 print("撮影"+id,save_dir+file_name) return id,save_dir+file_name else: id = "unknown" confidence = " {0}%".format(round(100 - confidence)) image = cv2.imwrite(save_dir+file_name,img) return id,save_dir+file_name #映像表示 cv2.imshow('camera',img) k = cv2.waitKey(10) & 0xff # Press 'ESC' for exiting video if k == 27: break #10秒経過後の処理 # Do a bit of cleanup print("\n [INFO] Exiting Program and cleanup stuff") cam.release() cv2.destroyAllWindows() id = "timeover" return id,save_dir+file_name |
10秒たっても顔認証されなかったら...というのに対応させるために、timedeltaを使用しています。
マークしてある、namesのところは、登録する方の数に応じて名前をご自身で付け加えてください!
face_capture.py
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 |
import cv2 import os cam = cv2.VideoCapture(0) cam.set(3, 640) # set video width cam.set(4, 480) # set video height face_detector = cv2.CascadeClassifier('haarcascade_frontalface_default.xml') # フェイスid入力要請 face_id = input('\n enter user id end press <return> ==> ') print("\n [INFO] Initializing face capture. Look the camera and wait ...") # count変数の初期化 count = 0 while(True): ret, img = cam.read() img = cv2.flip(img) # 取得写真を反転させる場合は(img,-1) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_detector.detectMultiScale(gray, 1.3, 5) for (x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h), (255,0,0), 2) count += 1 # 撮影した写真をdatasetsに保存 cv2.imwrite("datasets/User." + str(face_id) + '.' + str(count) + ".jpg", gray[y:y+h,x:x+w]) cv2.imshow('image', img) k = cv2.waitKey(100) & 0xff # Press 'ESC' for exiting video if k == 27: break elif count >= 30: # Take 30 face sample and stop video break # Do a bit of cleanup print("\n [INFO] Exiting Program and cleanup stuff") cam.release() cv2.destroyAllWindows() |
顔認証用の画像を撮影するためのプログラムです。countの数を変えることによって、撮影する枚数を変更できます。
face_train.py
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 |
import numpy as np from PIL import Image import os import cv2 # Path for face image database path = 'datasets' recognizer = cv2.face.LBPHFaceRecognizer_create() detector = cv2.CascadeClassifier("haarcascade_frontalface_default.xml") # function to get the images and label data def getImagesAndLabels(path): imagePaths = [os.path.join(path,f) for f in os.listdir(path)] faceSamples=[] ids = [] for imagePath in imagePaths: PIL_img = Image.open(imagePath).convert('L') # convert it to grayscale img_numpy = np.array(PIL_img,'uint8') id = int(os.path.split(imagePath)[-1].split(".")[1]) faces = detector.detectMultiScale(img_numpy) for (x,y,w,h) in faces: faceSamples.append(img_numpy[y:y+h,x:x+w]) ids.append(id) return faceSamples,ids print ("\n [INFO] Training faces. It will take a few seconds. Wait ...") faces,ids = getImagesAndLabels(path) recognizer.train(faces, np.array(ids)) # Save the model into trainer/trainer.yml recognizer.write('trainer/trainer.yml') # recognizer.save() worked on Mac, but not on Pi # Print the numer of faces trained and end program print("\n [INFO] {0} faces trained. Exiting Program".format(len(np.unique(ids)))) |
顔認証用画像の訓練用プログラムです。これはそのまま実行するだけで大丈夫です。
security_camera.py(メインのコード)
*顔認証後にtime.sleepを2秒加えました(11月6日)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import line import body_detect as body import face_recog as face import datetime import time if __name__ == "__main__": while True: #idの初期化 id = 0 #動体検知スタート(戻り値は保存した写真のパス) b_path = body.body_detect() #顔認証スタート(10秒間、戻り値id,写真のpath) id,f_path = face.face_recog() #顔検知後、2秒sleep time.sleep(2) #顔認証の戻り値で場合分け if id == "unknown": line.send("登録されていない方が写っています",f_path) elif id == "timeover": #動体検知の画像を送信 line.send("動体検知しました",b_path) else: line.send(id+"さんが帰ってきました",f_path) |
監視カメラプロジェクトのメインのプログラムです。
フローチャートの通りに書いてあります。idの値によって条件分岐しています。
実装の手順
実装の手順は以下のようになります
- face_capture.pyで訓練用の画像撮影
- face_train.pyで画像を訓練
- security_camera.pyを実行
この手順で実行することで、基本的な機能の確認ができると思います。
顔認証はコードの通り実行すれば問題ないですが、仕組みについて詳しく知りたい方は「ラズパイカメラとOpenCvを使って女優の顔認識してみた」を参考にしてみてください!
設置や自動起動については次回、やっていきたいと思います!
次に記事はコチラ→「ラズパイとOpenCVで監視カメラを自作してみた【後編】」