LoginSignup
9
11

More than 5 years have passed since last update.

ミライアカリとココロヤミを画像認識してみた OpenCV Keras DeepLearning CNN 【データセット作成からモデル作成・テストまで】

Last updated at Posted at 2018-08-29

はじめに

ミライアカリとココロヤミは同一人物ですが、
画像認識技術を用いて判別できるのでは?と思い
データセットの作成から、モデル作成・テストまでの一連の流れをやってみました。
top.jpg
すこ

データセット作成

・画像収集
画像の収集方法ですが、スクレイピングして画像収集するのには手間がかかるので、
動画から画像をフレーム単位で切り取って保存します。
コードはこちら↓
https://github.com/HiroakiFuse/opencv_faceAnime
・Opencvを使ってアニメの顔を検出してデータセット作成
lbpcascade_animeface.xmlを使用して、
顔の部分を検出し切り取って画像データを保存します。
lbpcascade_animeface.xmlは↓
https://github.com/nagadomi/lbpcascade_animeface

データセットをもとにモデル作成

モデルの学習は google colaboratoryを使いました。
GPU:NVIDIA Tesla K80が無料で使えるのでオススメです。

google colaboratoryを使用する

googledriveにアクセスするための設定

keras.ipynb
!apt-get install -y -qq software-properties-common python-software-properties module-init-tools
!add-apt-repository -y ppa:alessandro-strada/ppa 2>&1 > /dev/null
!apt-get update -qq 2>&1 > /dev/null
!apt-get -y install -qq google-drive-ocamlfuse fuse

認証

keras.ipynb
from google.colab import auth
auth.authenticate_user()
from oauth2client.client import GoogleCredentials
creds = GoogleCredentials.get_application_default()
import getpass
!google-drive-ocamlfuse -headless -id={creds.client_id} -secret={creds.client_secret} < /dev/null 2>&1 | grep URL
vcode = getpass.getpass()
!echo {vcode} | google-drive-ocamlfuse -headless -id={creds.client_id} -secret={creds.client_secret}

このコードを実行するとURLが吐き出されるので、クリックして許可してください。

googledriveの特定フォルダをマウント

keras.ipynb
!cp -f ~/.gdfuse/default/config config
!sed -i -e "s/^root_folder=$/root_folder=[フォルダーID]/" config

!mkdir -p drive
!google-drive-ocamlfuse -config ./config -cc drive
!ls drive

フォルダーIDを指定することで、特定のフォルダのみをマウントできる

Kerasを使ってCNN構築してみる

必要なパッケージをインポートする

keras.ipynb
# coding:utf-8
import keras
from keras.utils import np_utils
from keras.layers.convolutional import Conv2D, MaxPooling2D
from keras.models import Sequential,model_from_json
from keras.layers.core import Dense, Dropout, Activation, Flatten
from keras.preprocessing.image import array_to_img, img_to_array, list_pictures, load_img
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

画像の読み込みと行列への変換 データセットの作成

keras.ipynb
X = []
Y = []

# ミライアカリの画像
for picture in list_pictures('./drive/image/mirai/'):
    img = img_to_array(load_img(picture, target_size=(64,64)))
    X.append(img)

    Y.append(0)


# ココロヤミの画像
for picture in list_pictures('./drive/image/kokoro/'):
    img = img_to_array(load_img(picture, target_size=(64,64)))
    X.append(img)

    Y.append(1)


# arrayに変換
X = np.asarray(X)
Y = np.asarray(Y)

# 画素値を0から1の範囲に変換
X = X.astype('float32')
X = X / 255.0

# クラスの形式を変換
Y = np_utils.to_categorical(Y, 2)

# 学習用データとテストデータ
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.2, random_state=1)

モデルの構築

keras.ipynb
# CNNを構築
model = Sequential()

model.add(Conv2D(32, (3, 3), padding='same',
                 input_shape=X_train.shape[1:]))
model.add(Activation('relu'))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(2))       # クラスは2個
model.add(Activation('softmax'))

# コンパイル
model.compile(loss='categorical_crossentropy',
              optimizer='SGD',
              metrics=['accuracy'])
#TensorBoardのログ設定
log_filepath = './logs'
tbcb = keras.callbacks.TensorBoard(log_dir=log_filepath,histogram_freq=1, write_graph=True, write_images=True)
cbks = [tbcb]

# 実行。出力はなしで設定(verbose=0)。
history = model.fit(X_train, y_train, batch_size=16, epochs=60,
                   validation_data = (X_test, y_test),verbose = 1)

model accuracyの描画

keras.ipynb
plt.plot(history.history['acc'])
plt.plot(history.history['val_acc'])
plt.title('model accuracy')
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.legend(['acc', 'val_acc'], loc='lower right')
plt.show()

model_acc.PNG
学習結果:loss: 0.0184 - acc: 0.9963 - val_loss: 0.0289 - val_acc: 0.9851

モデルを保存

keras.ipynb
model_json_str = model.to_json()
open('vtuber_model.json', 'w').write(model_json_str)
model.save_weights('vtuber_model.h5')

ローカルドライブに保存したモデルを移動させる

keras.ipynb
!cp vtuber_model.json ./drive/vtuber_model.json
!cp vtuber_model.h5 ./drive/vtuber_model.h5

ローカルに移動させる理由ですが、google colaboratoryは使用してから12時間後に
インスタンスが初期化されるためです。

テスト

作成したモデルをもとにテストしてみます。
全体画像から結果を検出するためにOpencvを使って顔を検出し、
そのデータをモデルに投げるようにしました。

必要なライブラリのインポート

keras_run.ipynb
import numpy as np
import cv2
from keras.models import Sequential, model_from_json
from keras.models import Sequential
from keras.layers.convolutional import Conv2D
from keras.layers.pooling import MaxPool2D
from keras.layers.core import Dense,Activation,Dropout,Flatten
from keras.utils import np_utils
from keras.layers.core import Dense
from keras.optimizers import RMSprop
from keras.preprocessing.image import load_img
from keras.preprocessing.image import load_img, img_to_array
from PIL import Image
import matplotlib.pyplot as plt

学習済みモデルを用いて動作実験してみる

keras_run.ipynb
#画像から顔を検出する
def faceDetection(path,size):
    #cascadeパス
    cas_path = "lbpcascade_animeface.xmlのパスを指定"
    cvImg = cv2.imread(path)
    cascade = cv2.CascadeClassifier(cas_path)
    facerect = cascade.detectMultiScale(cvImg, scaleFactor=1.1, minNeighbors=1, minSize=(1, 1))
    faceData=[]
    for rect in facerect:
        faceImg = cvImg[rect[1]:rect[1]+rect[3],rect[0]:rect[0]+rect[2]]
        resized = cv2.resize(faceImg,None, fx=float(size/faceImg.shape[0]),fy=float( size/faceImg.shape[1]))
        CV_im_RGB = resized[:, :, ::-1].copy()
        pilImg=Image.fromarray(CV_im_RGB)
        faceData.append(pilImg)
    #複数検出される場合があるので最初のfaceDataを投げる
    return faceData[0]

def main():
    #画像パス
    img_path = "画像パス指定"
    #モデルパス
    model_path = "作成したモデルのjsonパス指定"
    #重みパス
    weight_path = "作成したモデルのh5パス指定"
    #ラベル
    label = ['ミライアカリ','ココロヤミ']

    #faceDetection呼び出し
    faceImg = faceDetection(img_path,64)
    #検出した顔データの確認
    plt.imshow(faceImg)
    plt.show()

    imgarray = []
    imgarray.append(img_to_array(faceImg))
    imgarray = np.array(imgarray) / 255
    imgarray.astype('float32')

    #モデルの読み込み
    model = model_from_json(open(model_path,'r').read())
    #重みの読み込み
    model.load_weights(weight_path)
    #コンパイル
    model.compile(loss='categorical_crossentropy',
              optimizer='SGD',
              metrics=['accuracy'])
    #テスト画像を入力し、結果を出力する
    y_pred = model.predict(imgarray)
    print(y_pred)
    #出力結果をクラスラベルから整数値に変換
    y_pred = np.argmax(y_pred, axis=1)
    print(y_pred)
    #Plotで画像表示する
    image = Image.open(img_path)
    img_list = np.asarray(image)
    plt.imshow(img_list)
    plt.show()
    print("予測結果:",label[int(y_pred)])
if __name__ == '__main__':
    main()

全体画像から正しく検出するためにopencvで顔を検出し、
そのデータをモデルに投げています。

うまくいったところ

kokoro.PNG
mirai.PNG

うまくいかなかったところ

kokoro2.PNG
なんでぞ!!!

ソースコード

※準備中(github整理しますので、もう少しお待ちください)

思ったこと

学習するデータ量に応じて、testデータの割合を変化させたほうがよさげ。
適切なバッチサイズを決める根拠を学ぶ必要がある。
quiver使って層の視覚化してみてみたい。

まとめ

※作成中

今後

AIを用いたWebサービスを作成してみようと思います。
WebフレームワークはFlaskかDjangoでやる予定
作成したら記事に一連の流れを載せようと思います。

参考サイト

機械学習で乃木坂46を顔分類してみた
http://blog.aidemy.net/entry/2017/12/17/214715
kerasでCNN 自分で拾った画像でやってみる
https://qiita.com/haru1977/items/17833e508fe07c004119
OpenCVでアニメの顔検出
https://qiita.com/mo4_9/items/fda37ac4f9ddab2d7f45

9
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
11