システムデザイン

ラズベリーパイで電子工作 – 第3回 LBPH分析(学習)による顔認識

gear

*本記事は旧TechblogからCOLORSに統合した記事です。

こんにちは、エンジニアソリューション事業部のA.Sです。
今回は、ラズベリーパイのカメラと前回インストールしたOpenCVを利用して、
リアルタイムでの顔認識を行います。

顔認識は、
LBPH(Local Binary Patterns Histogram)での顔分析モデルを作成し、
事前に顔認識学習済か否かで判別を行います。

下記構成となっています。

  • ①写真撮影(顔の学習のためラズベリーパイのカメラで写真撮影)
  • ②顔分析モデル作成(上記①で撮影した写真を学習)
  • ③顔検知(顔分析モデルを利用し、学習した顔があるか検知)

※OpenCVは、下記でも紹介

https://colors.ambl.co.jp/2019/11/08/raspberry-pie2/

①写真撮影(顔の学習のためラズベリーパイのカメラで写真撮影)

顔を学習するための素材として写真撮影を行います。

下記、GetFace.pyを実行すると撮影が始まります。
顔検知後に撮影となるため、認識されない場合は少し離れたり角度を変えたりしてください。

また実行時の引数は下記に記載していますが、
第一引数のユーザIDは数値でナンバリングしてください。(Aさんは1、Bさんは2のように)
※OpenCVの顔学習の関数(train())では、数値でしか判別(マッピング)できないため。
第二引数は、撮影枚数です。撮影枚数が多いほど学習量も増えます。

GetFace.py(第一引数:ユーザID【数値】、第二引数:撮影枚数)

import cv2
import os
import sys
#画像保存先
path = "/home/pi/blog3/ReadFace/self"
#ユーザID(写真に名称付与)
#また、LBPHFaceRecognizerから生成する際、数値によるナンバリングしか行えない模様。train()の第二引数は数値の配列のみのようだったため
user_id = sys.argv
#顔画像の取得(撮影)枚数
takeCnt = int(sys.argv)
print("ユーザID:"+ user_id +"の顔データを取得します。全" + str(takeCnt) +"枚撮影")
print("出力先:" + path)
print("--撮影中--")
#公式サイトで入手したカスケードxmlファイルを指定。今回は、顔、正面のみ。
face_cascade = cv2.CascadeClassifier('/home/pi/temp_face/data/haarcascades/haarcascade_frontalface_default.xml')
#カメラデバイス起動(ビデオモード)
pcam = cv2.VideoCapture(0)
#実撮影枚数
cnt = 0
#撮影状態維持のため
while True:
    ret, img = pcam.read()
    #グレースケールでなくともよいがグレースケールが一般的のようなので、
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    #解析する幅。小さくすれば正確だが時間がかかる。
    faces = face_cascade.detectMultiScale(gray, 1.3, 5)
    #多少間を空けるために設定(短時間での連続撮影だと類似画像になるため)
    cv2.waitKey(100)
    
    for x,y,w,h in faces:
        #検出した領域の枠を描画、「(255,0,0),2」は()の左から青、緑、赤。右端は線の太さ。
        cv2.rectangle(img, (x,y), (x+w,y+h), (255,0,0), 0)     
        #画像保存。gray[y:y+h,x:x+w]は、顔認識の四角の範囲で切り出した部分。
        cv2.imwrite("/home/pi/blog3/ReadFace/self/" + str(user_id) + '.' + str(cnt) + ".jpg", gray[y:y+h,x:x+w])
        #カウント用
        cnt += 1
        print("顔データ収集中:" + str(cnt) +"/" + str(takeCnt) + " 枚目")
        #プレビュー用
        cv2.imshow('image', img)
    #撮影予定枚数を満たしていたらbreak
    if cnt >= takeCnt:
        break
#カメラデバイス終了
pcam.release()
#プレビュー閉じる
cv2.destroyAllWindows()
print("顔データ収集完了")

撮影すると確認のためプレビューが起動します。
プレビューは撮影完了後に閉じます。

青字の部分が学習用写真として保存されます。
目の白い部分は別途隠したもので処理とは関係ありません。

実行結果

>>> %Run GetFace.py 1 30
ユーザID:1の顔データを取得します。全30枚撮影
出力先:/home/pi/blog3/ReadFace/self
--撮影中--
顔データ収集中:1/30 枚目
顔データ収集中:2/30 枚目
顔データ収集中:3/30 枚目
・・・
顔データ収集中:30/30 枚目
顔データ収集完了
指定した枚数分撮影を行い、写真保存されます。
[ユーザID].[撮影枚数].jpg形式

②顔分析モデル作成(上記①で撮影した写真を学習)

撮影した写真を学習し、顔分析モデルを作成します。

OpenCVのLBPHFaceRecognizer_create()を利用し、
画像学習[train()]、学習結果のファイル出力[write()]を行います。
※出力されたファイルは顔分析モデルとして利用します。

また、LBPHFaceRecognizer_create()は、
[opencv-contrib-python]をインストールする必要があります。
[opencv-contrib-python]は、[opencv-python]と共存できないようで両方入っているとエラーになりました。

opencv-pythonアンインストール

sudo pip3 uninstall opencv-python

opencv-contrib-pythonインストール

sudo pip3 install opencv-contrib-python

LeaningFace.py

import cv2
import numpy as np
from PIL import Image
import os
# 画像データのパスを指定
path = '/home/pi/blog3/ReadFace/self'
#カスケードxmlファイルを指定。顔、正面。
detector = cv2.CascadeClassifier("/home/pi/temp_face/data/haarcascades/haarcascade_frontalface_default.xml")
#解析モデル出力
outPath = '/home/pi/blog3/train/train.yml'
# 画像データとラベル(id)を取得する関数
def mapImageLabel(path):
    #顔画像データの取得
    images = [os.path.join(path,f) for f in os.listdir(path)]
    #画像データ
    imgSamples=[] 
    #画像ID rain()の第二引数は数値の配列のみのようだったためLBPHFaceRecognizerから生成する際、数値によるナンバリングしか行えない模様
    ids = []
    # 各画像をループで
    for image in images:
        #取得元がグレースケール化していない場合は、Image.open(imagePath).convert('L')でグレースケール化する。
        #GetFace.py経由だとグレーアウト済みのため割愛
        #uint8は、8ビットの符号なし整数
        img = np.array(Image.open(image),'uint8')
        #GetFace.pyで撮影したファイル名は、XX.YY.jpg。(XXはIDの意味付けをさせており、この部分を取得)
        id = int(os.path.split(image)[-1].split(".")[0])
        #解析
        faces = detector.detectMultiScale(img)
        for x,y,w,h in faces:
            #解析結果を格納
            imgSamples.append(img[y:y+h,x:x+w])
            ids.append(id)
    return imgSamples,ids
#解析された画像とユーザーIDを取得
faces,ids = mapImageLabel(path)
#LBPH顔認識用インスタンス生成
recognizer = cv2.face.LBPHFaceRecognizer_create()
#顔認識用トレーニング(学習)開始
print("トレーニング開始")
recognizer.train(faces, np.array(ids))
#トレーニング(学習)結果をファイル出力 
recognizer.write(outPath)
print("トレーニング終了 結果:" + outPath)

実行結果

%Run LeaningFace.py
トレーニング開始
トレーニング終了 結果:/home/pi/blog3/train/train.yml
出力されたymlファイル

③顔検知(顔分析モデルを利用し、学習した顔があるか検知)

上記②で作成した顔分析モデル(ymlファイル)を利用し、
カメラに学習した顔に似ている顔が映った場合に検知し、
ユーザ名と類似率を表示します。

※登録されていない顔が映った場合は、「who?」を表示します。

下記は、最低限の設定で試しています。
ここの実装でカメラや環境に合わせたチューニングをすれば認識率は高まるようです。

import cv2
import numpy as np
import os 
#LeaningFace.pyでトレーニング(学習)した結果を読み込む
recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.read('/home/pi/blog3/train/train.yml')
faceCascade = cv2.CascadeClassifier("/home/pi/temp_face/data/haarcascades/haarcascade_frontalface_default.xml")
font = cv2.FONT_HERSHEY_SIMPLEX
#IDを名前に変換するための配列(ユーザー増やす場合やIDが大きい場合は別の方法で取得するほうが良さそう)
#下記は、ID0~2の3ユーザーのみ
users = ['ID:0, Name', 'ID:1, A.S', 'ID:2, S.A'] 
print("--撮影開始--")
#カメラデバイス起動(ビデオモード)
pcam = cv2.VideoCapture(0)
#起動状態保持のため
while True:
    ret, img = pcam.read()
    #グレースケールでなくともよいがグレースケールが一般的のようなので、
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    faces = faceCascade.detectMultiScale(gray, 1.3, 5)
    for x,y,w,h in faces:
        cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 0)
        #信頼度0が完全一致
        id, confidence = recognizer.predict(gray[y:y+h,x:x+w])
        if (confidence == 100):
            id = "Who?"
        else:
            #id-名前に変換
            id = users[id]            
            
        #名前表示
        cv2.putText(img, str(id), (x+5,y-5), font, 1, (255,255,255), 2)
        #信頼度表示(%表示)
        confidence = str(" {0}%".format(round(100 - confidence)))
        cv2.putText(img, confidence, (x+5,y+h-5), font, 1, (255,255,0), 1)
        print("人物の顔を確認:" + id + confidence)
    #プレビュー表示
    cv2.imshow('image', img)
    #画像撮影頻度調整かつEscキー入力による処理終了受付用
    k = cv2.waitKey(10)
    if k == 27:
        break
#カメラデバイス終了
pcam.release()
#プレビュー閉じる
cv2.destroyAllWindows()
print("--撮影終了--")
顔認識されてます。学習素材少ないため類似率低いですが

実行結果

%Run UseOrgRec.py
--撮影開始--
人物の顔を確認:ID:1, A.S 39%
人物の顔を確認:ID:1, A.S 40%
・・・
人物の顔を確認:ID:1, A.S 33%
--撮影終了--

以上で、顔認識処理の説明は終わりです。
今後はチューニングや学習素材を増やしていろいろ試したいと思います。

おまけ

久しぶりにラズベリーパイ触ったところ、
(パスワード変更した覚えがないが)パスワードが異なるようで、
sshやRDP接続できない事象が発生。

下記でデフォルトのパスワード(raspberry)に戻す。

sudo passwd pi

ちなみに下記は、ノートPCからラズベリーパイにRDP接続するためのモジュール

sudo apt install tightvncserver
sudo apt install xrdp
ABOUT ME
秋葉瞬
AMBL株式会社 Integration本部 Banking開発部で部長を務めております。 主に金融案件のマネジメント業務に携わっています。趣味は剣道と旅行。