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