システムデザイン

ラズベリーパイで電子工作 – 第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開発部で部長を務めております。 主に金融案件のマネジメント業務に携わっています。趣味は剣道と旅行。

This website stores cookies on your computer. These cookies are used to provide a more personalized experience and to track your whereabouts around our website in compliance with the European General Data Protection Regulation. If you decide to to opt-out of any future tracking, a cookie will be setup in your browser to remember this choice for one year.

Accept or Deny