2009年創業。埼玉県川越市一筋のIT企業です。
イー・レンジャー株式会社 電話
イー・レンジャー株式会社 > 【AI/ML】OpenCVによる顔検出と顔認識(CascadeClassifier, FaceDetectorYN, FaceRecognizerSF)

【AI/ML】OpenCVによる顔検出と顔認識(CascadeClassifier, FaceDetectorYN, FaceRecognizerSF)

最終更新日: 2024/03/06 9:21am

カテゴリー: AI, お知らせ, ソラコム, 先進技術, 画像認識, 監視カメラ

こんにちは。小高です。

 

先日のブログ「【AI】集合写真から社員をさがせ! Amazon Rekognition (search users by image)」では、AWS(Amazon Web Services)の顔検出(Face Detection)、顔認識(Face Recognition)サービス「Amazon Rekognition」を紹介しました。

ブログに書きましたように、非常に少ない手間で精度の高い結果をえることができました。

 

今回はクラウドを使わずに、pythonモジュールとして提供されているOpenCVをつかって、同じようなことをしてみたいと思います。

OpenCVでは(昔からある)Haar-Like特徴量をつかった顔検出、DNN(AI)を利用した顔検出・顔認識が提供されています。

 

なお、後者を使う制約からOpenCVは最新4.9を使いました。

 

Haar-like特徴量を用いたカスケード型分類器による顔検出

昔からある手法です。

Haar-Like 特徴量とカスケード型分類機については、群馬大学のサイトで簡単に説明したものがありますので、そちらを参照してください。

リンク:Haar-like特徴量を用いたカスケード分類器による前方車両の識別

 

OpenCV(Python)でのサンプル実装については、以下のサイトを参考にせていただきました。

リンク:【Python】OpenCVによる顔認識:画像から顔を検出する方法

 

pythonコードとしては、xmlで定義された特徴量(haarcascade_frontalface_default.xml)をつかってカスケード型分類機を作り、グレースケール化したイメージを分類機に送って顔を検出します。

# カスケード型分類器の生成

_cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml"))

# 顔を検出する

_ret = _cascade.detectMultiScale(grey-scaled-image.jpg, 1.1, 3, 0, minSize)

以下が結果です。

face detection by harr-like features

図1:Haar-Like特徴量による顔検出(1人)

face detection by harr-like features

図2:Haar-Like特徴量による顔検出(たくさん)

 

結構、ちゃんと顔の位置を検出できているとおもませんか?

次は「いい線いっているのだけど。。」という結果です。

face detection by harr-like features

図3:Haar-Like特徴量による顔検出(たくさん)

 

先ほど、pythonコードでカスケード型分類機を作成するとき、haarcascade_frontalface_default.xml というxmlを使いました。これに「frontalface」と書いてあります。「正面画像の特徴量」だという意味です。

他にどんなのがあるかと興味が湧きますよね。下のopencvのgithubで見ることができますが、full body、lower body, upper bodyの他にcat faceやsmileなんていうのもあります。

リンク:OpenCV: Open Source Computer Vision Library

最終更新日が何年も前ですので、最近はあまり流行っていないのがわかりますね。

 

AWS Rekognitionでは検出できた下の「横顔画像」については、(上の特徴量では)顔を検出することはできませんでした。

 

図4:Haar-Like特徴量による顔検出(検出できず)

 

 

AI(DNN)モデル(FaceDetectorYN)による顔検出

ここであらためて、以下のように言葉を定義しておきます。

– 顔検出(Face Detection):画像から顔を発見する。顔の特徴点(目・鼻・口・耳など)を発見する。

– 顔認識(Face Recognition):他の画像と比較して、その人を特定する。

 

OpenCVには、AIにもとづく顔検出クラスとして、FaceDetectorYNが用意されています。

YNはYuNetという顔検出モデル(AI)を指しています。

YuNet:opencv_zoo/yunet

 

FaceDetectorYNの使い方については、以下のサイトが大変参考になりました。ありがとうございました。

リンク Qiita OpenCVの新しい顔検出を試してみる

 

注意点としては、上の記事後にAIモデルが更新されているので、YuNetのサイトから face_detection_yunet_2023mar.onnx をダウンロードして使ってください。

このモデルをpythonの実行ディレクトリにおいたら、以下のような感じとなります。

# FaceDetectorYNの生成
face_detector = cv.FaceDetectorYN_create("face_detection_yunet_2023mar.onnx", "", (320, 320), 0.6, 0.3, 5000, cv.dnn.DNN_BACKEND_DEFAULT, target_id=cv.dnn.DNN_TARGET_CPU)

# 画像の読み込み
image = cv.imread("image.jpg")

# 画像サイズを設定する
face_detector.setInputSize((image.shape[1], image.shape[0]))

# 顔検出
_, faces = face_detector.detect(image)

 

バウンティングボックスに加えて、顔の特徴点(両目、鼻の頭、口の端)を描いてみました。

face detection by cv.FaceDetectorYN

図5:FaceDetectorYNによる顔と特徴点の検出

 

Haar-Like特徴量+カスケード分類機で誤検出のあった画像もうまく検出てきています。

face detection by cv.FaceDetectorYN

図6:FaceDetectorYNによる顔との検出(複数人)

 

Haar-Like特徴量+カスケード分類機では検出できなかった、横顔についても検出できています。

face detection by cv.FaceDetectorYN

図7:FaceDetectorYNによる顔との検出(横顔)

 

YuNetのページ(opencv_zoo/yunet)には「light-weight, fast, accurate」と書いてあります。

ためしに、録画済みの動画の顔検出をしましたので掲載しておきます。

 

 

AI(DNN)モデル(FaceRecognizerSF)による顔認識

OpenCVには、AIにもとづく顔認識クラスとして、FaceRecognizerSF が用意されています。

SFは「SFace」という顔認識のための損失関数の名前のようです。

SFace:opencv_zoo/SFace

ArXive: SFace: Sigmoid-Constrained Hypersphere Loss for Robust Face Recognition

 

FaceDetectorYNについても(先と同じ著者の)以下のサイトが大変参考になりました。

リンク Qiita OpenCVの新しい顔検出を試してみる

 

流れはAmazon Rekognitionでユーザー検索をしたときに似ています。

Rekognitionでは、ユーザー(認識したい人)の顔の特徴量を「コレクション」というデータベースに登録しましたが、FaceDetectorYNはこれを「辞書(ディクショナリー)」と呼んでいます。

つまり、

1.  ユーザーの特徴量を作成して辞書に登録しておく。

2.  画像から顔を検出して特徴量を計算し、辞書内の一番近い特徴量を探し出す。

3. 上(2)の結果から、ユーザー名を特定する。

という流れになります。

1と2で特徴量を計算するときには、YuNetで顔を切り出して精度を上げます。

 

辞書の作成は以下のような流れです。imageは登録用画像、userは登録する人の名前と思ってください。

# 顔を切り出すためのFaceDetectorYNの生成
face_detector = cv.FaceDetectorYN_create("face_detection_yunet_2023mar.onnx", "", (320, 320), 0.6, 0.3, 5000, cv.dnn.DNN_BACKEND_DEFAULT, target_id=cv.dnn.DNN_TARGET_CPU)

# 画像サイズの取得
height, width, _ = image.shape
face_detector.setInputSize((width, height))


# 顔を検出する
_, faces = face_detector.detect(image)
    if len(faces) > 0:
    # FaceRecognizerSF の生成
    face_recognizer = cv.FaceRecognizerSF_create("face_recognizer_fast.onnx", "")
    # 顔を切り抜き特徴を抽出する
    aligned_face = face_recognizer.alignCrop(image, faces[0])
    # 特徴量の抽出
    face_feature = face_recognizer.feature(aligned_face)

    # 特徴量ベクトルを user.npy に保管する
    np.save(user, face_feature)


Amazon Rekognitionのときと同様、以下の正面写真をユーザーsuzukiで登録しました。
顔データ(正面)

 

顔認識の流れは以下の感じです。この部分のコードは先のQiitaの記事から借用させていただいてます。(ありがとうございます)

 

上で「辞書内の一番近い特徴量」と書きましたが、特徴量(ベクトル)間の「近さ(距離)」は自明ではないですよね。
下のNORML2というのは「L2ノルム」と言ってユークリッド距離(ピタゴラスの定理が成り立つ通常の意味での長さ)です。COSINEはユークリッド距離と違った特殊な距離です。
(論文では、特徴量ベクトルを「半径=1の球面(Hypersphere)」に配置して、球面上の点に関する損失関数としてSFaceというのを提案しています)

 

下のTHRESHOLDはそれぞれの距離を使った際の基準で、経験的な数値のようです。
opencv : DNN-based Face Detection And Recognition
# https://docs.opencv.org/4.x/d0/dd4/tutorial_dnn_face.html
COSINE_THRESHOLD = 0.363
NORML2_THRESHOLD = 1.128

def match(recognizer, feature1, dictionary):
    for element in dictionary:
    user_id, feature2 = element
    # FaceRecognizerSF でマッチする
    score = recognizer.match(feature1, feature2, cv.FaceRecognizerSF_FR_COSINE)
    if score > COSINE_THRESHOLD:
        return True, (user_id, score)

return False, ('unknown', 0.0)


if __name__ == "__main__":

.....

# 画像からの顔の検出
result, faces = face_detector.detect(image)
faces = faces if faces is not None else []


_idx=0
_l=list()
for face in faces:
    # 顔を切り抜き特徴を抽出する
    aligned_face=face_recognizer.alignCrop(image, face)
    _idx+=1
    feature=face_recognizer.feature(aligned_face)


    # 辞書とマッチングする
    result, user=match(face_recognizer, feature, dictionary)
実行した結果です。ただしくsuzukiが認識されました。
名前の脇はスコアです。
上のコードだとunknownの場合のスコアは強制的に0.0になっています。
face recognition by cv.FaceRecognizerSFace
図8:Face RecognizerSFによる顔認識

 

ここで先の横顔を入力してみたら、Unknownとなってしまいました。
face recognition by cv.FaceRecognizerSFace
図9:Face RecognizerSFによる顔認識(横顔失敗)

 

上の写真でユーザーが認識できなかったのは、横顔の写真を辞書に登録しなかったからです。

 

このRecognizerが興味深いのは、
– 特徴量が普通のnpyファイルで保管される
– マッチングの際、特徴量を辞書の頭から順に比較している
という点です。

 

これなら、1人で複数の特徴を登録できるようにできそうですね。
以下の画像もユーザーsuzukiとして登録しました。

 

顔データ(横)

 

横顔でも顔認識できるようになりましたね。

 

face recognition by cv.FaceRecognizerSFace
図10:Face RecognizerSFによる顔認識(横顔成功)

 

たくさんの集合写真でも認識できました。

 

face recognition by cv.FaceRecognizerSFace
図11:Face RecognizerSFによる顔認識(集合写真)

 

最後に、これらの機能を使って録画動画に名前のラベル付をしてみました。
FPSを20から15に下げて、32秒の動画の変換に105秒程度かかりました。

 

 

すごい駆け足でOpenCVが提供する顔検出(Face Detection)と顔認識(Face Recognition)について見てきました。

 

書くのも疲れましたが、最後まで読んでいただいた方はさぞかしお疲れと思います。ありがとうございました。

 

Amazon Rekognitionはすぐに使えて簡単というメリットがありますが、Wifi環境下という制約があります。
OpenCVはエッジに入れることができますので、場面によって遣い分けをするのがいいと思いました。

 

←「」前の記事へ   次の記事へ「」→