前回はGoogleが提供している機械学習のライブラリMediaPipeを使って手の検出を行いました。今回はMediaPipeに含まれているFace Meshという顔を468個の特徴点として検出するライブラリを使って、動画に移っている人の顔を検出してみました。
まずは今回紹介するプログラムを使っていくつかの動画を処理した様子をご覧ください。
ではプログラムの紹介をしていきます。
準備
必要なモジュールはOpenCVとMediaPipeです。
|
1 2 3 |
pip install opencv-python pip install opencv-contrib-python pip install mediapipe |
プログラムの内容
ソースコードはこちらです。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import os import cv2 import mediapipe as mp import time cap = cv2.VideoCapture("sample1.mp4") pTime = 0 mpDraw = mp.solutions.drawing_utils mpFaceMesh = mp.solutions.face_mesh faceMesh = mpFaceMesh.FaceMesh(max_num_faces=5) drawSpec = mpDraw.DrawingSpec(color=(255, 255, 255), thickness=1, circle_radius=3) while True: success, img = cap.read() if success == False: break imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) results = faceMesh.process(imgRGB) if results.multi_face_landmarks: for faceLms in results.multi_face_landmarks: mpDraw.draw_landmarks(img, faceLms, mpFaceMesh.FACEMESH_CONTOURS, drawSpec,drawSpec) cv2.imshow("Image", img) # FPS=30 cTime = time.time() wait = int(33.3 - (cTime - pTime) * 1000) pTime = cTime if wait > 0: cv2.waitKey(wait) else: cv2.waitKey(1) |
ソースコードとサンプルの動画はこちらからダウンロードできます。
プログラムの解説
ではプログラムの説明をしていきます。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
import os import cv2 import mediapipe as mp import time cap = cv2.VideoCapture("sample1.mp4") pTime = 0 mpDraw = mp.solutions.drawing_utils mpFaceMesh = mp.solutions.face_mesh faceMesh = mpFaceMesh.FaceMesh(max_num_faces=5) drawSpec = mpDraw.DrawingSpec(color=(255, 255, 255), thickness=1, circle_radius=3) |
まずOpenCVによって動画を読み込んでいます(6行目)。
pTimeはFPSが大体30になるように時間調節するための変数です(7行目)。
その後、MediaPipeの描画ユーティリティと顔検出モジュールをそれぞれmpDrawとmpFaceMeshという変数で保持しています(9、10行目)。
つぎにFaceMeshクラスのオブジェクトを作成しています(11行目)。クラスのコンストラクタ(初期化関数)は以下のような引数を取ります。
参照:https://github.com/google/mediapipe/blob/master/mediapipe/python/solutions/face_mesh.py
|
1 2 3 4 5 6 |
def __init__(self, static_image_mode=False, max_num_faces=1, refine_landmarks=False, min_detection_confidence=0.5, min_tracking_confidence=0.5): |
- static_image_mode:Falseにセットすると、入力される画像が一連の動画フレームと判断する(顔をトラッキングする場合は必ずFalseにする)。Trueの場合は、入力される画像は静止画(つまり連続した画像ではない)として扱う。デフォルトはFalse。
- max_num_faces:検出する顔の最大数。デフォルトは1。
- refine_landmarks:Trueにセットすると、目や口の周りのランドマークをもっと細かくする。デフォルトはFalse。
- min_detection_confidence:顔を検出する確信度。0.0~1.0の間で指定する。デフォルトは0.5。
- min_tracking_confidence:映像で連続するフレームで同じ顔が検出されたかどうかの閾値。0.0~1.0の間で指定する。デフォルトは0.5。static_image_modeがTrueにセットされている場合は無視される。
ここではmax_num_facesを5に指定しています。
その後、描画するランドマークの〇の大きさや、線の太さや色を指定します(12行目)。
|
14 15 16 17 18 19 20 21 22 23 24 25 |
while True: success, img = cap.read() if success == False: break imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) results = faceMesh.process(imgRGB) if results.multi_face_landmarks: for faceLms in results.multi_face_landmarks: mpDraw.draw_landmarks(img, faceLms, mpFaceMesh.FACEMESH_CONTOURS, drawSpec,drawSpec) cv2.imshow("Image", img) |
Whileループ内で、動画ファイルから1フレーム映像を取り出し、OpenCVの色情報(BGR)から一般的な色情報(RGB)に変換したあと、Face Meshモジュールによって画像を処理しています(15~19行目)。
そして顔が検出された場合、ランドマークを描画しています(20~23行目)。
|
27 28 29 30 31 32 33 34 |
# FPS=30 cTime = time.time() wait = int(33.3 - (cTime - pTime) * 1000) pTime = cTime if wait > 0: cv2.waitKey(wait) else: cv2.waitKey(1) |
ここの処理でFPSを大体30にしています(厳密にはFPS=30になっていないと思います)。FPSが30だと1フレームあたりの時間は1000(ms)÷30=33.3(ms)となります。各フレームの間が約33.3msになるように、処理にかかった時間を引いてやることで待ち時間 wait を計算しています。
さいごに
最初に示した動画にあるように、動画に映っている人が2人までなら顔の検出がうまくいくようです。でも3人以上の人が映っている場合は、うまく検出できないケースがありました。3人以上でもうまく顔が検出できるという方法を知っている方がいらっしゃればアドバイスいただけると幸いです。
- 投稿タグ
- プログラミング