LoginSignup
7
10

More than 3 years have passed since last update.

Kerasでアニメ画像を学習し予測する(CNN)

Posted at

環境

  • Python:3.6
  • TensorFlow:1.8.0
  • Keras:2.2.5

アニメ画像切り出し

下記を参考にフォルダ内全ての画像を切り出しする。
OpenCVでアニメの顔検出 - Qiita

import os
import cv2

import glob

list = glob.glob("/Users/katasugirupan/Desktop/sample/*")

classifier = cv2.CascadeClassifier('lbpcascade_animeface.xml')

output_dir = 'corpus'
if not os.path.exists(output_dir):
    os.makedirs(output_dir)

n = 0
for i in list:
    print(i)
    image = cv2.imread(i)
    gray_image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    faces = classifier.detectMultiScale(gray_image)
    # print(faces)

    for j, (x,y,w,h) in enumerate(faces):
        face_image = image[y:y+h, x:x+w]
        resize_image = cv2.resize(face_image,(64,64),interpolation = cv2.INTER_AREA)
        output_path = os.path.join(output_dir,str(n)+'.jpg'.format(j))
        cv2.imwrite(output_path,resize_image)

    n += 1

lbpcascade_animeface.xml
https://github.com/nagadomi/lbpcascade_animeface

画像の名前を連番にする

特に必要な作業ではないですが管理がしやすいように、ファイル名を「A0.jpg - A1.jpg ...」に変更しておきます。

import os
import glob

list = glob.glob("/Users/katasugirupan/Desktop/corpus/*")

output_dir = 'dataset'
if not os.path.exists(output_dir):
    os.makedirs(output_dir)

n = 0
for file_name in list:
    print(file_name)
    os.rename(file_name, '/Users/katasugirupan/Desktop/' + output_dir + '/' + 'A' + str(n) + '.jpg') 
    n += 1

kerasで自前のデータセットを読み込み、表示してみる

# データセット作成 表示
import keras
from keras.utils import np_utils
from keras.preprocessing.image import array_to_img, img_to_array, load_img
import numpy as np

from PIL import Image

import os
import re

# 画像表示
def ConvertToImg(img):
    return Image.fromarray(np.uint8(img))

def list_pictures(directory, ext='jpg|jpeg|bmp|png|ppm'):
    return [os.path.join(root, f)
            for root, _, files in os.walk(directory) for f in files
            if re.match(r'([/w]+/.(?:' + ext + '))', f.lower())]

X = []

# 画像フォルダから、64x64で取り込む
for picture in list_pictures('./dataset'):
    img = img_to_array(load_img(picture, target_size=(64,64)))
    X.append(img)

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

# 画像一つの幅
chr_w = 64
# 画像一つの高さ
chr_h = 64

# 画像をPILで1枚の画像に描画する(15枚*15枚)
canvas = Image.new('RGB', (int(chr_w * 15), int(chr_h * 15)), (255, 255, 255))

# 画像を読み込んで描画
i = 0
for y in range(15):
    for x in range(15):
        chrImg = ConvertToImg(x_train[i])
        canvas.paste(chrImg, (chr_w*x, chr_h*y))
        i = i + 1

canvas.show()
# 表示した画像をJPEGとして保存
canvas.save('test.jpg', 'JPEG', quality=100, optimize=True)

今回はMakeGirlsMoeから画像を頂き、切り出しています。
表示するとこのような感じ。
sample.jpeg

kerasでCNNを実装

下記の記事を参考にさせていただきました。
kerasでCNN 自分で拾った画像でやってみる - Qiita
Kerasは相撲取りとアイドルを見分けられるかやってみた - Qiita

私もやってみたい。
と言う事で、Fate0のセイバーが好きなのでセイバー分類機を作成することに。
* セイバーかセイバーではないか分類。
* セイバー : 300枚
* セイバーではない : 600枚
* 学習とテストは合計900枚を利用し、その内10%をテストに使用
* プラス、未知のデータとして検証用にそれぞれ3枚づつ用意
画像はgoogle検索で頑張る。

keras2系で記述しています。

from keras.models import Sequential
from keras.layers import Activation, Dense, Dropout, Conv2D, Flatten, MaxPooling2D
from keras.utils.np_utils import to_categorical
from keras.optimizers import Adagrad
from keras.optimizers import Adam
import numpy as np
from PIL import Image
import os
import matplotlib.pyplot as plt

# 学習用のデータ作成
X = []
Y = []

for dir in os.listdir("drive/My Drive/keras/data2/train"):

    dir1 = "drive/My Drive/keras/data2/train/" + dir 
    label = 0

    # Labelを付ける
    if dir == "saber": 
        label = 0
    elif dir == "unknown": 
        label = 1

    for file in os.listdir(dir1):
        Y.append(label)
        filepath = dir1 + "/" + file
        image = np.array(Image.open(filepath).resize((64, 64)))
        image = image.transpose(2, 0, 1)
        X.append(image / 255.)

X = np.array(X)
Y = to_categorical(Y) # [1,0],[0,1] One hot

# CNN
model = Sequential()
model.add(Conv2D(32, (3, 3), padding="same", input_shape=(3, 64, 64)))
model.add(Activation("relu"))
model.add(Conv2D(32, (3, 3)))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2, 2), padding=("same")))
model.add(Dropout(0.25))

model.add(Flatten())

model.add(Dense(200))
model.add(Activation("relu"))
model.add(Dropout(0.2))

model.add(Dense(200))
model.add(Activation("relu"))
model.add(Dropout(0.2))

model.add(Dense(2))
model.add(Activation("softmax"))

opt = Adam(lr=0.0001)
model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"])
# 10%はテストに使用
hist = model.fit(X, Y, epochs=50, batch_size=25, validation_split=0.1)

# テスト用ディレクトリの画像でチェック、正解率を表示
total = 0.
ok_count = 0.

for dir in os.listdir("drive/My Drive/keras/data2/test"):

    dir1 = "drive/My Drive/keras/data2/test/" + dir 
    label = 0

    if dir == "saber":
        label = 0
    elif dir == "unknown":
        label = 1

    for file in os.listdir(dir1):
        label_list.append(label)
        filepath = dir1 + "/" + file
        image = np.array(Image.open(filepath).resize((64, 64)))
        print(filepath)
        image = image.transpose(2, 0, 1)
        result = model.predict_classes(np.array([image / 255.]))
        print("label:", label, "result:", result[0])

    total += 1.

    if label == result[0]:
        ok_count += 1.

print("seikai: ", ok_count / total * 100, "%")


# 正解率の推移をプロット
plt.plot(hist.history['acc'])
plt.plot(hist.history['val_acc'])
plt.title('Accuracy')
plt.legend(['train','test'], loc='upper left')
plt.show()

# ロスの推移をプロット
plt.plot(hist.history['loss'])
plt.plot(hist.history['val_loss'])
plt.title('Loss')
plt.legend(['train','test'], loc='upper left')
plt.show()

model.summary()

sample2.jpeg

drive/My Drive/keras/data2/test/saber/F302.jpg
label: 0 result: 0
drive/My Drive/keras/data2/test/saber/F303.jpg
label: 0 result: 0
drive/My Drive/keras/data2/test/saber/F301.jpg
label: 0 result: 0
drive/My Drive/keras/data2/test/unknown/A560.jpg
label: 1 result: 1
drive/My Drive/keras/data2/test/unknown/A561.jpg
label: 1 result: 1
drive/My Drive/keras/data2/test/unknown/A562.jpg
label: 1 result: 1
seikai:  100.0 %

未知のデータ6枚に対しても正しく認識しているようです。
枚数を増やして検証すれば正解率は下がるかもしれません。

終わりに

  • 下記のモデルも試して見たいと思うのですが、長くなり疲れてきたので一旦終わりにしようと思います。

sample.png

  • padding='valid'にしたり、もう一段階Denseを増やしたりと色々考えれると思います。

  • qiitaの記事がありましたので、私でもCNNを試せました。先人に感謝致します。

7
10
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
7
10