鬼滅画像分類

Aidemyで成果物を作ったのでまとめます。

内容は鬼滅の刃のキャラの分類です。

まずは一つ目についてまとめていきます。Aidemyの講座を参考にコーディングしました。手順は以下のようになります。

①データを集める

②データの加工

③分類器作成

①についてはアニメからスクリーンショットで集めたものをローカルに保存した後グーグルドライブに移動させました。登場キャラクター5種類ぐらいの写真を集めようとしていたのですが、登場回数が少ないこともありサンプルが集まらず3キャラで妥協しました。3キャラまとめて同じファイルに保存したので分類が多少面倒だったため、作ったモデルを利用して分類してくれるプログラムも書いてみました。後からこれ作っても意味ないんですけど興味本位で作りました。

②はデータを100*100の形にしてリストに格納した後に水増ししました。アニメのキャラはあまり白黒にしない方がいいのですが、漫画の写真も分類できるように白黒加工しました。加工処理は全ての訓練データに行っています。img_to_arrayと通常のpythonでは色の読み方が反対なのでリスト内で反対にしています。この処理をしないと水増ししたとき色が反対で炭治郎の顔色が悪くなりました。

import cv2
import matplotlib.pyplot as pyplot
import numpy as np
from keras.preprocessing.image import ImageDataGenerator #for  文でリスト
imgs = img_list
 #1 、画像を持ってくる #img_listとcv2 .imread("")の繋ぎ方
 #2 、訓練データ、テストデータに分ける #連続しているシーンをスクショしているのでシャッフルしたい  #rand_index  = np.random.permutation(np.arange(len(img_list))) #img_list  = img_list[rand_index]
train_data = []
test_data = []

for img in imgs[0:175]:
   train_data.append(img)

for img in imgs[175:200]:
   test_data.append(img)
 #写真は500枚を想定し 、それに白黒、反転でデータを水増し
def resize(dataset):
   my_img = cv2.resize(dataset, (100, 100))
   return my_img

def to_gray(dataset, x,y):
   for img in dataset[x:y]:
       my_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
       dataset.append(my_img)
   return dataset
def to_flip(dataset, x, y):
   for img in dataset[x:y]:
       my_img = cv2.flip(img, 0)
       dataset.append(my_img)
   return dataset #resize (train_data) #resize (test_data)
to_gray(train_data,0, 175)
to_gray(test_data,170 , 200)
to_flip(train_data,0, 175)
to_flip(test_data,170, 200) #保存する前に削除する関数作りたい  #標準化  #標準化はfitどちらでも 
def to_normal(X, y):
   datagen = ImageDataGenerator(samplewise_center=True, samplewise_std_normalizetion=True)
   g = datagen.flow(X, y, shuffle=False)
   X_batch, y_batch = g.next()
   return X_batch, y_batch #これは訓練データにだけ用いるものなの#白色化 
def no_correcation(X, y):
   dategen2 = ImageDataGenerator(featurewise_center=True, zca_whitening=True)
   datagen.fit(train_data)
   g = datagen.flow(X, y, shuffle = False)
   X_batch, y_batch = g.next()
   return X_batch, y_batch

for i, data in enumerate(train_data):
 cv2.imwrite("drive/MyDrive/Kimetsu_picture/Zennitsu/"+str(i)+".jpg", data)

for i, data in enumerate(test_data):
 cv2.imwrite("drive/MyDrive/Kimetsu_picture/Zennitsu_test/"+str(i)+".jpg", data)

③はデータ数が多いわけではなかったのでvggの重みを利用しました。精度は約60%であまり高くありませんが、モデルを作ってみることが目的なのでここでストップします。

from keras.layers import Input, Dense, Dropout, Flatten, Activation, Conv2D, MaxPooling2D, BatchNormalization,GlobalAveragePooling2D
from keras.models import Model, load_model, Sequential
from keras.utils.np_utils import to_categorical
from keras.utils.vis_utils import plot_model
from keras.applications.vgg16 import VGG16
import numpy as np
import matplotlib.pyplot as plt
from keras import optimizers #ここにデータセットを組み込む  #ラベル付  #分ける 
X = np.array(Tanziro_img_list + Inosuke_img_list + Zennitsu_img_list)
y =  np.array([0]*len(Tanziro_img_list) + [1]*len(Inosuke_img_list)+[2]*len(Zennitsu_img_list))
rand_index = np.random.permutation(np.arange(len(X)))
X = X[rand_index]
y = y[rand_index]

X_train = X[:int(len(X)*0.8)]
y_train = y[:int(len(y)*0.8)]
X_test = X[int(len(X)*0.8):]
y_test = y[int(len(y)*0.8):]
# 正解ラベルをone-hotの形にします
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)


input_tensor = Input(shape=(100, 100, 3))
vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)
# vggのoutputを受け取り、2クラス分類する層を定義します
# その際中間層を下のようにいくつか入れると精度が上がります
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(3, activation='softmax'))
# vggと、top_modelを連結します
model = Model(inputs=vgg16.input, outputs=top_model(vgg16.output))
# vggの層の重みを変更不能にします
for layer in model.layers[:15]:
   layer.trainable = False
# コンパイルします
model.compile(loss='categorical_crossentropy',
             optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),
             metrics=['accuracy'])
# 学習を行います
model.fit(X_train, y_train, batch_size=10, epochs=10, validation_data=(X_test, y_test)) #精度確認  #モデル保存 
path  =""
model.save("model.h5")

以上が一つ目の成果物のまとめです。講座でも同じようなことをしたのですが、いざ自分で作るとなるとわからないことが多かったため良いアウトプットになりました。下のモデルを利用した写真を移動するコードを記載しておきます。3キャラ以外のキャラをelseというファイルに移動させる機能(精度の最大値が50%以下の場合にelseと判定)も追加しようとしたのですが、精度が出せなかったため断念。

import glob
import os
import shutil
from keras.preprocessing.image import load_img, save_img, img_to_array, array_to_img
from keras.models import load_model
import numpy as np

path = "/content/drive/MyDrive/Kimetsu_picture/Kimetsu_random/*"
image_path_list = glob.glob(path)
img_list = [[img_to_array(load_img(name, target_size=(100, 100)))[:,:,::-1],name] for name in image_path_list] #anziro_img_list  = [img_to_array(load_img(name, target_size=(100, 100)) )[:, :, ::-1]for name in image_path_list]
classes = ["0", "1", "2"]  

model = load_model("/content/drive/MyDrive/Colab Notebooks/Kimetsu_classifer/model.h5") #画像リストをfor文で行列に直す 
for i in img_list:
 i,name = i[0],i[1]
 img = np.array([i])
 #img  = img_to_array(i)
 #data  += np.array([i])
 result = model.predict(img)[0]
 
 predicted = result.argmax()
 pre_answer = "これは"+ classes[predicted]+ " です"
 
 if classes[predicted]=="0":
   shutil.move(name, "drive/MyDrive/Kimetsu_picture/Tanziro/")
 elif classes[predicted]=="1":
   shutil.move(name, "drive/MyDrive/Kimetsu_picture/Zennitsu/")
 elif classes[predicted]=="2":
   shutil.move(name, "drive/MyDrive/Kimetsu_picture/Inosuke/")

まとめ

受講期間を通して機械学習、自然言語処理、画像処理について理解し、簡単ではあるものの実装までする事ができてよかったです。スクレイピングや時系列分析も講座で勉強できたので今後はデータ集めをしたり、kaggleで実践経験を積んでいきたいです。成果物を作るにあたって協力してくださった方々ありがとうございました。


この記事が気に入ったらサポートをしてみませんか?