てっしーの雑記

主に技術系のネタを

OpenCVで顔認証をしてみる

OpenCVで顔認証をやってみました
使った画像がよくなかったのか精度がイマイチでちょっと残念な結果に。。。

環境

顔画像抽出

今回、アイドルの写真やアニメ画像での顔認識を試していたので、学習用の顔写真を集めます
inとなる画像は複数人が掲載されておりそこから一人一人に分割します

import time
from datetime import datetime
from pathlib import Path
import os
import cv2

root = os.getcwd()

classifier = cv2.CascadeClassifier(root + '/haarcascade_frontalface_default.xml')
# classifier = cv2.CascadeClassifier(root + '/lbpcascade_animeface.xml')

output_dir = root + '/out'
if not os.path.exists(output_dir):
    os.makedirs(output_dir)

p = Path(root + "/in")
list = list(p.glob("*"))

for file in list:
    print(file)
    # 顔の検出
    image = cv2.imread(str(file))
    # グレースケールで処理を高速化
    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    faces = classifier.detectMultiScale(gray_image)

    for i, (x, y, w, h) in enumerate(faces):
        now = time.time()
        utc = datetime.utcfromtimestamp(now)
        # 一人ずつ顔を切り抜く
        face_image = image[y:y+h, x:x+w]
        output_path = os.path.join(output_dir, '{0}_{1}.jpg'.format(i, datetime.now().timestamp()))
        cv2.imwrite(output_path, face_image)

    for x,y,w,h in faces:
        # 四角を描く
        cv2.rectangle(image, (x, y), (x+w, y+h), color=(0, 0, 255), thickness=3)

    cv2.imwrite(output_dir + '/' + str(file), image)

顔認識モデルは2種類使っています
用途に合わせて切り替えてください

  • haarcascade_frontalface_default.xml:人間用
  • lbpcascade_animeface.xml:アニメ用

顔認識できた座標で切り取り新たな画像を作成します
ここで作られた画像が個別認識用の学習画像となります

個別認識

抽出した画像を元に個別認識を行います
認識率が高いものが出てくると思うのですが、100%以上であっても一致してないことがあり、何がイケないのか悩ましいところです。。
いろいろな画像を試してみて検証する必要がありそうです

import cv2
import os
import numpy as np

root = os.getcwd()

train_path = root + '/out'
test_path = root + '/in'

cascadePath = root + "/haarcascade_frontalface_default.xml"
# cascadePath = root + "/lbpcascade_animeface.xml"
faceCascade = cv2.CascadeClassifier(cascadePath)

# LBPH
recognizer = cv2.face.LBPHFaceRecognizer_create()


def get_images_and_labels(path):
    images = []
    labels = []
    files = []
    for f in os.listdir(path):
        # 画像のパス
        image_path = os.path.join(path, f)

        image = cv2.imread(image_path)
        image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

        faces = faceCascade.detectMultiScale(image)

        # 検出した顔画像の処理
        for (x, y, w, h) in faces:
            roi = cv2.resize(image[y: y + h, x: x + w], (200, 200), interpolation=cv2.INTER_LINEAR)
            images.append(roi)
            # ファイル名からラベルを取得 "0_xxxxx.jpg みたいなファイル名を想定している"
            names = f.split("_")
            labels.append(int(names[0]))
            files.append(f)

    return images, labels, files


# トレーニング画像を取得
images, labels, files = get_images_and_labels(train_path)
# トレーニング実施
recognizer.train(images, np.array(labels))
# テスト画像を取得
test_images, test_labels, test_files = get_images_and_labels(test_path)

i = 0
while i < len(test_labels):
    label, confidence = recognizer.predict(test_images[i])
    print("Test Image: {}, Predicted Label: {}, Confidence: {}".format(test_files[i], label, confidence))
    i += 1

cv2.destroyAllWindows()

get_images_and_labels メソッドで学習用画像や判定画像を読み込み、 recognizer.train で学習しています
recognizer.predict で予測を行い顔認証を行っています

まとめ

僅かなコードで簡単に顔認証を行うことができましたが、画像の選定や学習用ファイルの精度が大事な感じです
ネットで拾った画像ではなく自分で撮った写真で試してみたいと思います

今回使ったソースはGitHubに上がっています

github.com