今日はdlibというC++で作成された機械学習のライブラリの中に含まれるface_recognitionというライブラリを使用して、複数の人が映った映像から特定の人物が表示されているときに、その人の顔を検出するPythonプログラムを作成してみました。

dlibについて以下のサイトを参照してください。
http://dlib.net/

face_recognitionについては以下のサイトを参照してください。
https://face-recognition.readthedocs.io/en/latest/face_recognition.html

準備

事前にVisual Studio Communityをインストールしておく必要があります。以下のサイトからダウンロードして、インストールしてください。

https://visualstudio.microsoft.com/ja/vs/community/

※インストールの際、「C++によるデスクトップ開発」をチェックすることを忘れないでください。

Python側で必要となるモジュールはOpenCVとdlib、cmake、face_recognitionです。以下のコマンドでインストールしてください。

実行した様子

プログラムのソースコードを解説する前に、実行した様子を示します。

プログラムを起動すると以下のようなウィンドウが表示されます。「映像読み込み」ボタンと「顔写真読み込み」ボタンそれぞれを押して、複数の人物が映った動画ファイルと特定の人物が映った画像ファイルを選択します。ここではTEDの映像と、イーロン・マスクの顔写真を読み込んでいます。

今回使った映像

今回使った画像

https://en.wikipedia.org/wiki/Elon_Musk

「検出開始」というボタンを押すと処理が始まります。イーロン・マスクが映っている場面では、このように顔が認識されています。

他の人の場合は、顔の検出はされません。

それではプログラム中身を紹介していきます。

プログラムの内容

ソースコードはこちらです。

あとファイルやフォルダを選択するダイアログ関連の機能をまとめた以下のスクリプト(MyUtility.py)を読み込んでいます。

プログラムの解説

FaceMovieDetectクラスについて説明します。

init関数初期化関数。GUIの設定を行っています。
open_moviefile関数動画ファイルを選択するダイアログを表示する。
open_pictfile関数顔写真ファイルを選択するダイアログを表示する。
detect_face_in_movie関数映像から特定の人物を検出する(これがメインの処理)

メインの処理をしているdetect_face_in_movie関数について見ていきましょう。

まずは動画ファイルと顔写真ファイルのパスを取得しています。また顔写真のファイル名を、その人の顔が検出されたときに映像に表示するので、「Elon Must.png」のように顔写真のファイルにはその人の名前を付けておいてください。

Numpyで顔写真のデータを開き、OpenCVでその画像を読み込んだ後、face_recognitionのface_encodings関数で顔の特徴点を抽出しています。
公式サイトによるface_encodings関数の説明は以下の通りで、引数として顔が映った画像(face_image)を取ります。他にも顔が映っている場所の情報(known_face_locations)などを指定できるようです。戻り値は顔の特徴抽出点のリストです(1つの顔につき128個のデータで、複数の顔を検出できるため「128個のデータ×顔の数分」のリストになっている)。

映像から1フレーム読み込んだ後、処理を軽くするために画像のサイズを4分の1にしています。また色をBGRからRGBに変換しています。

フレームの情報からface_recognitionのface_locations関数で顔の位置を特定しています。その後、face_recognitionのface_encodings関数で検出された顔の特徴点をエンコードしています。

face_locations関数の仕様は以下の通りです。

検出された顔(動画フレーム)をforループで1つずつ取り出して、face_recognitionのcompare_faces関数で比較対象の顔(静止画)と比べています。
また検出された顔(動画フレーム)と元の画像(静止画)がどのくらい近いか(似ていいるか)を表す数値(距離)をface_recognitionのface_distance関数で計算しています。

その後、face_distance関数の戻り値facedistが最小値となる要素をnp.argmin関数で見つけ、それを変数matchIndexに入れています。ただ、今回のプログラムでは1つの顔しか渡していないので、要素は1つしかなく、matchIndexの値は必ず0になります。このように一見不要な処理をしているのは今後、複数の人を同時に検出すること考えているためです。

compare_faces関数の戻り値のうち、face_distanceが最小となる結果がマッチ(True)していた場合、検出された顔の周囲に緑色の矩形を描画し、人物の名前(静止画のファイル名)を表示する処理をしています。

compare_faces関数とface_distance関数の仕様は以下の通りです。compare_faces関数による顔の判定は、距離(face_distance)の値が0.6以下だと同一人物の顔だと判断されるようです。これはtoleranceの値を変えることで変更でき、より小さい数字にすれば元の画像(静止画)とかなり似ていなければ同一人物だと判定されなくなります。

さいごに

他にも日本の芸能人の映像で試してみましたが、美人には顔に共通要素があるようで、誤認識されるケースも多くりました。例えば、長澤まさみを新垣結衣と誤認識する(またはその逆)ことがありました。今後は同時に複数の人物を区別するプログラムを作ってみようと思います。