Edited at

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

More than 1 year has passed since last update.


はじめに

ミライアカリとココロヤミは同一人物ですが、

画像認識技術を用いて判別できるのでは?と思い

データセットの作成から、モデル作成・テストまでの一連の流れをやってみました。

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