#はじめに
ミライアカリとココロヤミは同一人物ですが、
画像認識技術を用いて判別できるのでは?と思い
データセットの作成から、モデル作成・テストまでの一連の流れをやってみました。
すこ
#データセット作成
・画像収集
画像の収集方法ですが、スクレイピングして画像収集するのには手間がかかるので、
動画から画像をフレーム単位で切り取って保存します。
コードはこちら↓
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にアクセスするための設定
!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
##認証
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の特定フォルダをマウント
!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構築してみる
##必要なパッケージをインポートする
# 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
##画像の読み込みと行列への変換 データセットの作成
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)
##モデルの構築
# 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の描画
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()
学習結果:loss: 0.0184 - acc: 0.9963 - val_loss: 0.0289 - val_acc: 0.9851
##モデルを保存
model_json_str = model.to_json()
open('vtuber_model.json', 'w').write(model_json_str)
model.save_weights('vtuber_model.h5')
##ローカルドライブに保存したモデルを移動させる
!cp vtuber_model.json ./drive/vtuber_model.json
!cp vtuber_model.h5 ./drive/vtuber_model.h5
ローカルに移動させる理由ですが、google colaboratoryは使用してから12時間後に
インスタンスが初期化されるためです。
#テスト
作成したモデルをもとにテストしてみます。
全体画像から結果を検出するためにOpencvを使って顔を検出し、
そのデータをモデルに投げるようにしました。
##必要なライブラリのインポート
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
##学習済みモデルを用いて動作実験してみる
#画像から顔を検出する
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で顔を検出し、
そのデータをモデルに投げています。
###うまくいったところ
・
###うまくいかなかったところ
なんでぞ!!!
#ソースコード
※準備中(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