読者です 読者をやめる 読者になる 読者になる

【逃げ恥】恋ダンスでただガッキーだけを見ていたかったのでディープラーニングする(後編)

taka-say.hateblo.jp

上記記事の続きです。

復習は面倒だが役に立つ

前回の記事では、ガッキーの顔とそうでないものの二値分類を行うモデルを作成し、実際に動画への適用を試みました。
残念ながら上手くいきませんでした。

詳細は前編をどうぞ。

忍耐は辛いが役に立つ②

前回の取り組みで顕在化した問題に対処していきます。
対処方法として、分類するクラスを増やすことで解決を図ります。

  • ガッキー
  • その他出演者4名
  • その他顔
  • その他顔として検出されてしまう物体

上記の7クラスの分類を行う顔判別機を作る方針でいきます。

本来であれば、各々の顔を前編でやったような手法で収集しなければいけませんが、面倒だったので、恋ダンス動画自体から顔データの収集を行います。
顔画像取得のコードは下記の通りです。

import cv2
import datetime
import numpy as np
from keras.models import load_model

# 動画関係
target = "input.mp4"
movie = cv2.VideoCapture(target)

# 顔認識準備
model = load_model('./Models/gakky_face_model.hdf5')
cascade_path = '/path/to/your/opencv3/haarcascade_frontalface_default.xml'
cascade = cv2.CascadeClassifier(cascade_path)
gakky_n = 0

if movie.isOpened() is True:
    ret, frame = movie.read()
else:
    ret = False
count = 0
while ret:
    # 顔検出
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    frontfaces = cascade.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=5)
    # 顔認識
    for (x, y, w, h) in frontfaces:
        dst = frame[y:y + h, x:x + w]
        image = cv2.resize(dst, (64, 64))
        image = image.transpose(2, 0, 1)
        image = image / 255.
        image = image.reshape(1, 3, 64, 64)
        if model.predict_classes(np.array(image)) == 0:
            cv2.imwrite('./FaceImagesFromVideo/Gakky/' + str(count) + '.jpg', dst)
        else:
            cv2.imwrite('./FaceImagesFromVideo/Others/' + str(count) + '.jpg', dst)
        count += 1
    ret, frame = movie.read()
    # 50フレームごとに経過を出力
    if movie.get(cv2.CAP_PROP_POS_FRAMES) % 50 == 0:
        print(datetime.datetime.now().strftime('%H:%M:%S'),
              '現在フレーム数:' + str(int(movie.get(cv2.CAP_PROP_POS_FRAMES))))

前回作成したモデルは、ガッキーかそうでないかを判別する能力はありませんでしたが、顔検出した箇所が、顔っぽいかそうでないかを判別する能力はありそうだったので、その予測値を利用して保存するディレクトリを選択しています。
この事前分類をしておくことで、後の分類負担を多少緩和できます。
処理が終わると、恋ダンスの動画中で顔だと思われる箇所が全て保存されるはずです。

あとは、冒頭で示した7つのクラスに合致するように、手動で画像を各々のディレクトリに分類していきます。
ディレクトリ名は下の画像のように、数字の接頭辞を付けておくとよいです。

f:id:taka_say:20161218212929p:plain

最終的に動画から切り出した顔画像の枚数は、

  • ガッキー:1516
  • 出演者A:349
  • 出演者B:398
  • 出演者C:295
  • 出演者D:452
  • その他顔:85
  • その他物:2591

となりました(アンバランス!)。
前編で用意したデータと統合すると、顔画像データは合計で7,525枚になりました。

繰り返しは気怠いが役に立つ

新しい顔画像データで再度学習を行っていきます。
修正箇所は下記の2ヵ所です。

  • 顔データのNumPy配列出力を行う処理の箇所で、data_dir_pathを新しい顔画像データのあるディレクトリに修正
  • モデルオプションの指定をnb_classes = 7に修正

枚数が多くなったせいかそれなりに学習に時間がかかります。

最終的な学習経過はこのようになりました。

f:id:taka_say:20161219142240p:plain f:id:taka_say:20161219142248p:plain

若干学習途中のような気がしないでもないですが、とりあえずこれで良しとします。

結果は怖いが役に立つ

では新しいモデルでの処理結果をいくつかご紹介します。

f:id:taka_say:20161220004034g:plain
f:id:taka_say:20161220004038g:plain

動画中の顔データを使っているので当たり前といえば当たり前ですが、ある程度求めていた処理が行われているように思います。

では、全く異なる動画中の顔を正しく判別することはできるでしょうか。
検証のため、予告動画に対して同様の処理を試みます。

  • 多少うまくいっているパターン f:id:taka_say:20161220010442g:plain

  • ダメダメなパターン f:id:taka_say:20161220010457g:plain

上手く判別が行えている箇所も散見されましたが、上出来というには程遠い状態でした。
やはり顔データの学習画像が少なすぎるかつ似たパターンばかりという問題は大きいようで、きちんとした予測モデルを構築するためには、個々人の顔データを別途集め直す必要がありそうです。

DataAugmentationの訳は分からないが役に立つ

おまけとして、Kerasでお手軽にできるData Augmentation処理をしたモデルも作成してみます。
正直なところ、各パラメータの真偽でどのように扱いが変わるのかが分かりかねるので(勉強不足!)、設定に関してはいい加減です。

元々のソースコードmodel.fit()部分を下記のように書き換えて試みます。

from keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(
    rotation_range=20,
    width_shift_range=0.1,
    height_shift_range=0.1,
    horizontal_flip=True)
datagen.fit(X_train)
hist = model.fit_generator(datagen.flow(X_train, Y_train,
                           batch_size=batch_size),
                           samples_per_epoch=X_train.shape[0],
                           nb_epoch=nb_epoch,
                           validation_data=(X_test, Y_test),
                           verbose=1,
                           callbacks=[checkpointer])

先程上手く判別できていなかった箇所はこのようになりました。

f:id:taka_say:20161220011333g:plain

おおよそ正しく判別できています。
このシーンだけに着目するとかなり改善されたように思われますが、実際には誤判別も増えていたので、今回の場合はrecall増えてprecision減ったという印象でした。

逃げるは恥だが役に立つ

前編から引き続き、判別モデルの問題点解消に取り組みました。

当初の目的として掲げた、「恋ダンスでただガッキーだけを見ていたい」という目標はいくらか達成されたように思います。
記事中の検証用gifでは顔検出の枠線が多すぎてかなり気が散りますが、顔の上書きだけを行った動画を見ると、それなりに快適度がアップしており、個人的には満足しております。

f:id:taka_say:20161220012825g:plain

とはいえ、画像収集に実施した手法(動画からの切り出し!)がイマイチだったため、モデルの汎用性にはかなりの問題が見られました。
学習に使用するデータの重要性が再認識できたように思います。


ガッキーかわいい!

参考は別サイトだが役に立つ

モチベーション

新垣結衣 | アーティスト | レプロエンタテインメント
ご注文はDeep Learningですか? - kivantium活動日記

Keras

Keras Documentation
Are there any codes for AlexNet, ZF Net, GoogLeNet, VGGNet in Keras · Issue #1568 · fchollet/keras · GitHub
Error when checking model target: expected activation_2 to have shape (None, 10) but got array with shape (3, 1) · Issue #3109 · fchollet/keras · GitHub
Kerasでアニメキャラの顔認識 - Qiita
続・深層学習でアニメ顔を分類する with Keras - Qiita

動画

Python + OpenCV で雑コラ動画を作成する③ 雑コラ動画作成 - Qiita
overlay a smaller image on a larger image python OpenCv - Stack Overflow

Gif

PicGIF Lite を Mac App Store で