今、話題の人工知能(AI)などで人気のPython。初心者に優しいとか言われていますが、全然優しくない! という事を、つらつら、愚痴っていきます

385.顔検出、顔認識(2)

»

初回:2024/10/09

 OpenCV を使用した、顔検出と顔認識について何回かに分けて述べてみたいと思います。

P子「前回は、cv2.CascadeClassifier を使用した顔検出だったわね」※1

 『Haar-like特徴量を用いたカスケード型分類器による顔検出』と言われるもので、割と古い方式になります。今回は、新しい顔検出の方法について、述べたいと思います。

 ≪参考資料1≫
  https://qiita.com/UnaNancyOwen/items/f3db189760037ec680f3
  OpenCVの新しい顔検出を試してみる
  最終更新日 2023年06月27日 投稿日 2021年12月03日
  @UnaNancyOwen(Tsukasa Sugiura)

P子「最初から、新しい方式を解説すればよかったんじゃないの?」

 私も顔検出を初めてラズパイで行ったのは、前回ご紹介した方式でした。この新しい方式では動かしたことがなかったので、実機で確認する必要がありました。

P子「要するに、時間稼ぎしたかったのね」

1.顔検出

 OpenCV は前回インストール済みなので、必要なのは新しい方式(FaceDetectorYN)で使用するonnxファイルの準備です。ファイルは、YuNetのサイトから取得してきます。

 ≪参考資料2≫
  YuNet:opencv_zoo/yunet
  https://github.com/opencv/opencv_zoo/tree/main/models/face_detection_yunet
  face_detection_yunet_2023mar.onnx

 いきなりですが、ソースコードを示します。

import time
import cv2

GREEN = (0, 255, 0)                 # BGR形式

if __name__ == '__main__':
    face_detector = cv2.FaceDetectorYN.create(FaceFilter.ONNX_PATH, "", (320, 320), 0.6, 0.3)

    cap = cv2.VideoCapture(0)
    try:
        while True:
            ret, frame = cap.read()

            if ret:
                face_detector.setInputSize((frame.shape[1], frame.shape[0]))
                _, faces = face_detector.detect(frame)						# ① 顔検索

                if faces is not None:                                       # ② 未検出時は、None になる
#               #   検出値はfloatなので int に変換する必要がある。
#                   for (x, y, w, h, *_) in faces:
#                        cv2.rectangle(frame, (int(x), int(y)), (int(x + w), int(y + h)), GREEN, 2, cv2.LINE_AA)
                    for face in faces:
                        (x, y, w, h, *_) = map(int, face)                   # ③ 検出値はfloat
                        cv2.rectangle(frame, (x, y), (x + w, y + h), GREEN, 2, cv2.LINE_AA)

                cv2.imshow('Face Detect Frame', frame)

            k = cv2.waitKey(10)                 # ミリ秒単位で表されるキーボード入力待ち時間
            if k == ord('q') or k == 27:        # q または、ESC で終了
                break

            time.sleep(0.1)                     # CPU処理の軽減化

    except KeyboardInterrupt:                   # Ctl+Cが押されたらループを終了
        print("\nCtl+C Stop")
    except Exception as ex:
        print(ex)                               # 例外処理の内容をコンソールに表示
        import traceback
        traceback.print_exc()                   # Exception のトレース
    finally:
        if cap is not None:
            cap.release()
        cv2.destroyAllWindows()
        print("終了")

 前回のソースと見比べられるように、同じ形式で書いています。

 ① 顔検索
  retval, faces のタプルが返ってきます。retval の使い道が判りませんが、faces のみで判断していきます。
 ② 未検出時は、None になる
  この値は、未検出時はNone になります。長さゼロの配列ではないので注意しましょう。
 ③ 検出値はfloat
  戻り値は、検出結果の配列ですが、個々の中身は少し複雑です。

  faces
  0-1: x, y of bbox top left corner
  2-3: width, height of bbox
  4-5: x, y of right eye (blue point in the example image)
  6-7: x, y of left eye (red point in the example image)
  8-9: x, y of nose tip (green point in the example image)
  10-11: x, y of right corner of mouth (pink point in the example image)
  12-13: x, y of left corner of mouth (yellow point in the example image)
  14: face score

 0-1、2-3 は、x, y, w, h ですが、float値なので、int に変換する必要があります。cv2.rectangle にパラメータ指定する時に、int にキャストする方法と、map で一括変換する方法を書いておきます。0-1、2-3 以降を、*_ 受け取っていますが、face をスライスで切り取っても構いません。
 4-13 は、各ポイントの位置を示していますが、このサンプルでは未使用です。
 14は、顔検出時のスコアを表しています。

 こちらの方が新しいのと、検出が早く、精度が高いと言われていますが、ラズパイレベルでは検出速度にあまり差は出ませんでした。ただ、検出精度に関しては、今回の新しい方式の方がよさそうです。

2.まとめ

 とりあえず、カメラ映像からの顔検出が出来ました。前回同様、本当にお手軽ですね。

 顔が検出出来れば、それ以降、色々と応用ができます。顔部分だけ切り抜く、モザイクをかける、別の顔に入れ替えるなどです。また、ある程度のリアルタイム処理もできそうなので、楽しみが増えます。

 次回は、本命の顔認識に取り組んでみたいと思います。

P子「時間稼ぎは不要なの?」

 場合によっては、別の話題で時間稼ぎするかもしれませんが、そこはご愛嬌という事で。

 ほな、さいなら

======= <<注釈>>=======

※1 P子「前回は、cv2.CascadeClassifier を使用した顔検出だったわね」
 P子とは、私があこがれているツンデレPythonの仮想女性の心の声です。

Comment(0)

コメント

コメントを投稿する