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の仮想女性の心の声です。