ラブライブ!のソーシャルゲーム「スクールアイドルフェスティバル ALL STARS」、通称「スクスタ」の登場キャラを顔認識させるAIプログラム(Pythonサンプル)の内容について説明します。
GitHub公開プログラム
https://github.com/kotetsu99/animeface_recognition
↑のGitHub上で公開しているプログラムですが、主に3つのプログラムが存在します。
・顔検出プログラム:01-face_detection.py
・顔学習プログラム:02-cnn_face_train.py
・顔認識プログラム:03-cnn_face_recognition.py
以下、それぞれの解説を記します。
顔検出プログラム
データセットディレクトリにあるゲーム画像からキャラの顔を検出して、顔画像ファイルを別ディレクトリに保存するプログラムです。顔検出に活躍するのが、OpenCVという画像処理ライブラリおよび、アニメ顔の特徴を定義しているカスケード分類器と呼ばれるファイルです。
本プログラムのソースコードは以下になります。
https://github.com/kotetsu99/animeface_recognition/blob/master/01-face_detection.py
入出力先のディレクトリ定義、カスケード分類器のファイルパスの定義を最初に行った後、main関数が実行されます。各キャラクターのディレクトリ毎に、元画像(ゲームのスクリーンショット)を読み込んで、画像中の顔検出を行います。
顔検出を行うのが、detect_face関数です。画像毎に顔検出を実施していきます。顔検出の計算処理を軽くするため、以下のメソッドを用いて画像をグレースケールに変換します。
def detect_face(image, model): # グレースケール画像に変換 image_gs = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
顔検出において、重要となるのが以下の箇所です。
# 見落としを抑えるため、最初は低精度で検出。徐々に精度を上げていく。 for i_mn in range(1, 7, 1): # 顔検出 face_list = cascade.detectMultiScale(image_gs, scaleFactor=1.1, minNeighbors=i_mn, minSize=(200, 200))
detectMultiScale というOpenCVの関数ですね。この関数で顔検出を行いますが、このパラメータの設定が検出精度に大きく影響します。特に重要なものが以下の2つです。
・minNeighbors
・minSize
minNeighbors は、顔っぽい箇所を「顔」と識別する許容度と理解してもらえればいいと思います。この値が小さいほど許容度が緩くなり(低精度)、顔っぽい箇所を見逃すリスクが抑えられます。一方で顔でないものを誤検出してしまうリスクが上がります。逆にminNeighborsが大きいほど、許容度が厳しくなり(高精度)、誤検出を抑えられます。その反面、顔を見逃してしまうリスクが上がります。
本プログラムでは、1つの画像に対し、minNeighborsの値をだんだん大きくしながら、顔検出を繰り返します。最初は、低精度で緩く検出し、forループを繰り返すごとに精度を上げて、検出結果を上書きしていきます。これにより、顔の見落とし・誤検出の両方を抑えるように工夫しています。
minSizeは、指定した縦横サイズ以上のものを顔と認識するという意味の設定です。大きすぎると顔の見逃しが、小さすぎると誤検出がそれぞれ増えてしまいます。元画像中の顔部分の大きさを予め、ペイントなどの画像変種ツールで測ってあたりをつけた設定値とすればいいかなと。
顔学習プログラム
上記の顔検出プログラムで抽出した顔画像をAIに学習させ、予測モデルを生成するプログラムです。ソースコードは以下になります。
https://github.com/kotetsu99/animeface_recognition/blob/master/02-cnn_face_train.py
最初にカギとなるのが、AIに識別させたいキャラ名(クラス名)のリスト作りです。
# データセットのサブディレクトリ名(クラス名)を取得 classes = os.listdir(train_data_dir)
このos.listdir により、dataset/02-face/配下のディレクトリ名のリストを取得し、それをキャラ名リストとして定義します。[Ayumu, Kasumi, Setsuna] のようなリストが出来上がります。これを各ディレクトリ配下にある画像データの正解ラベル名として、AIに学習させます。
AIの予測モデル定義を行っているのが以下の関数です。
def cnn_model_maker(nb_classes):
今回のAIは画像認識に特化したニューラルネットワークである、畳み込みニューラルネットワーク(CNN:convolutional neural network)を採用します。
CNNは通常のニューラルネットワークと違い、畳み込み層、プーリング層と呼ばれる中間層が登場します。これらのパラメータ(フィルタサイズ、プーリングサイズ)や層数は、対象とする画像の特性により変わってきますが、今回は、一般物体認識でよく使われる画像データセット(CIFAR-10)向けの、CNNを使用します。
CIFAR-10 CNNの構成は以下の通りです。全9層のニューラルネットワークです。
----------------------------------------
・入力層: 1層
-画像サイズ: 64 x 64
-チャンネル数: 3(RGB)
・中間層(畳み込み層): 4層
-フィルタ数: 32, 64
-フィルタサイズ: 3 x 3
-活性化関数: relu関数
・中間層(プーリング層): 2層
-プーリングサイズ: 2 x 2
-ドロップアウト率: 0.25
・中間層(全結合層): 1層
-ニューロン数: 512
-活性化関数: relu関数
-ドロップアウト率: 0.5
・出力層: 1層
-ニューロン数(出力数): 3
-活性化関数: ソフトマックス関数
・その他
-ネットワークタイプ: 分類問題
-損失関数: 交差エントロピー
-最適化アルゴリズム: Adam
-バッチサイズ: 4
-エポック数: 2000回
----------------------------------------
ニューラルネットワークに関する基本事項等は、以下の書籍が参考になります。
■はじめてのディープラーニング Pythonで学ぶニューラルネットワークとバックプロパゲーション
AIを学習させる際は、Kerasで用意されている画像生成ジェネレータを用いてトレーニング(学習)/評価用データを生成します。それを行っているのが以下の関数です。
def image_generator(classes):
学習/評価用データはともに、Kerasの画像ジェネレータ ImageDataGenerator クラスにある便利なメソッド
flow_from_directory
を使用して生成します。これは指定したディレクトリ配下にあるサブディレクトリ名を正解ラベルとして認識してくれます。そのうえでサブディレクトリ配下の画像データと、正解ラベルを紐づけたデータセットを生成してくれます。
ここでは、dataset/02-face/ 配下にあるサブディレクトリ(Ayumu, Kasumi, Setsuna)が正解ラベル名になりますね。デフォルトではラベル名の順番は自動で決められますが、後の顔認識プログラムで作る正解ラベル名リストと順番を合わせる必要があるため、classesパラーメータにおいて、os.listdir(train_data_dir)で生成しておいたラベル名リストを渡しています。
画像ジェネレータを用いて、AIに学習させているのが以下です。
# CNN学習 history = cnn_model.fit_generator( train_generator, epochs=nb_epoch, validation_data=validation_generator, callbacks=[es_cb])
flow_from_directoryを用いて生成した画像データを学習に使う場合は、fit_generator関数を用います。学習/評価データの他、エポック数、収束判定の設定を行っています。収束判定の設定は、さらに話が長くなるのでまたの機会に。
上記で挙げたKerasによるデータ生成、ニューラルネットワーク実装は、以下の書籍が参考になります。
■必要な数学だけでわかる ディープラーニングの理論と実装
顔認識プログラム
上記の顔学習プログラムで生成した予測モデルを使って、テスト画像に対して顔認識を実行するプログラムです。ソースコードは以下の通りです。
https://github.com/kotetsu99/animeface_recognition/blob/master/03-cnn_face_recognition.py
全体的な流れは顔検出プログラムと似ていますが、OpenCVで顔検出したデータを、Kerasの顔予測モデルに渡して、キャラ名を識別。さらに名前を元のテスト画像に矩形付きで追記する点が大きな特徴です。
主に以下の箇所ですね。
# Keras向けにBGR->RGB変換、float型変換 face_img = cv2.cvtColor(face_img, cv2.COLOR_BGR2RGB).astype(np.float32) # 顔画像をAIに認識 name = predict_who(face_img, model)
OpenCVとKerasとでは、扱う画像データの色順番の定義が異なるため(前者はBGR、後者はRGB)、順番を入れ替えています。さらに型変換をしたうえで、predict_who(Keras側)に渡しています。
predict_whoでは、画像データを予測モデルに入力し、各キャラ名を予測率とともに出力します。コンソール画面上には、予測率トップ3人のキャラ名と予測率を表示させます。
関数としての戻り値は、トップ1人のキャラ名です。
上記のキャラ名は、以下の関数を用いて画像データに書き込んでいます。
cv2.putText(image, name, (x, y + height + 80), cv2.FONT_HERSHEY_DUPLEX, 3, (0, 0, 255) ,2)
キャラ名と顔近傍の矩形を書き込んだ画像データを新規ファイルとして、main関数最後に出力しています。
# 結果をファイルに保存 rec_file_name = os.path.join(rec_data_dir, "rec-" + test_image) cv2.imwrite(rec_file_name, rec_image)
これにより、以下のような認識結果の画像ファイルがdataset/04-rec/配下に生成されます。
プログラムの解説は以上になります。
OpenCVを使った画像処理、Kerasによる畳み込みネットワーク(CNN)の活用など、なかなかタフな実装ですが、それだけに出来たときの達成感はひとしおですね。本プログラムは、ラブライブ!キャラに限らず一般的なアニメキャラの顔認識にも利用できます。お役に立てれば幸いです。