ごはんと飲み物は紙一重

このブログは,自己のモチベーションアップのために情報工学関連について記事を更新していこうと思います。三日坊主にならないよう頑張ります…

【俺妹】大好きな黒猫をディープラーニングで認識しないわけがない【分類編】


前回の記事

twdlab.hatenablog.com


今度こそ最後です.長かった学習もこれにて一区切りです.やっと目的としていた,「大好きな黒猫をディープラーニングで認識」をしていきます!

分類器はそもそもどんな仕組みなのかを知らずに右往左往していたのですが,結局のところざっくりと仕組みを分けると・・・

  • OpenCVでキャラクターの顔認識をする
  • 未知のデータから切り取ったキャラクターに対してテストを行う

の2点になる形です.ん?あれ?えっと・・・

これ簡単に作れないか?

データ下準備編から見てくれた方は察すると思いますが,今回のこの学習までの過程で上記の2つのプログラムは作成してあるんです!つまりこの2つのプログラムを組み合わせて,学習したデータを読み込めば分類が可能ってわけです.

そうこうして組み合わせたプログラムがこちら.

# -*- coding: utf-8 -*-
import sys
sys.path.append("/usr/local/lib/python2.7/site-packages")
import numpy as np
import cv2
import os
import oreimoface as of
import cnn_keras_model as ckm
from keras.utils import np_utils

# #(0)keras関連のモジュール読み込み
from keras.models import Model
from keras.models import model_from_json
from keras.layers import Input, Dense, Dropout, Flatten, Convolution2D, MaxPooling2D

# 最適化手法に使うクラスを読み込む
from keras.optimizers import SGD, Adadelta, Adam, RMSprop

# 引数でファイル名を取得
filename = sys.argv[1]

# クラス数
nb_classes = 8

##(1)モデル準備
#model = Model(inputs, outputs)
json_string = open('1000learn/oreimo_cnn_model.json', 'r').read()
model = model_from_json(json_string)
model.load_weights('1000learn/oreimo_cnn_last_weight.hdf5')

# キャラクターのラベル
chara_name = np.loadtxt("labels.txt", str, delimiter="\t")

# 伝播の設定
def forward(x_data):
    ##(1)予測式(モデル)の構築
    # イメージのピクセルサイズ
    img_rows, img_cols = 28,28
    # 畳み込みフィルタの数
    nb_filters = 32
    # max poolingのサイズ
    nb_pool = 2
    # 畳み込みカーネルのサイズ
    nb_conv = 3

    ##入力を画像データ(配列でいれたい)
    # データの入力
    inputs = Input(shape=(3,img_rows, img_cols)) # 1はチャンネル数

    # 畳み込みによるConvolution Layerの作成(Reluによる正規化(0より小さい値を0とする関数))
    x = Convolution2D(nb_filters, nb_conv, nb_conv, border_mode='valid', activation='relu')(inputs)
    x = Convolution2D(nb_filters, nb_conv, nb_conv, border_mode='valid', activation='relu')(x)

    # MaxPoolingによって各領域の最大値をとって画像を圧縮する
    x = MaxPooling2D(pool_size=(nb_pool, nb_pool))(x)

    # 過学習を防ぐためにデータに対し制約をかける(1/4だけ減らす)
    x = Dropout(0.25)(x)

    x = Flatten()(x)

    x = Dense(128, activation='relu')(x)
    x = Dropout(0.5)(x)

    outputs = Dense(nb_classes, activation='softmax')(x)

##(2)予測式(モデル)のコンパイル
    #sgd_nesterov=SGD(lr=0.01, momentum=0.9, nesterov=True)

    model.compile(loss='categorical_crossentropy', # 誤差関数
            optimizer='adadelta',# 最適化手法(デフォルト設定)
            #optimizer=sgd_nesterov, # 最適化手法(手動設定)
            metrics=['accuracy'] # 評価指標
            )

    import matplotlib.pyplot as plt

# 読み込んだデータを0~1に正規化,numpy.arrayに変換
    X_test = []
    y_test = []
    for f in range(len(x_data)):
        X_test.append(x_data[f])
        y_test.append(f)

    X_test = np.array(X_test).astype(np.float32).reshape((len(X_test), 3, 28, 28)) / 255
    Y_test = np_utils.to_categorical(y_test, nb_classes)
##(4)学習結果の評価
# テストデータに対する評価値
    Y_test_pred = model.predict(X_test)
    #print(Y_test_pred.shape)

    plt.imshow(Y_test_pred, cmap = 'gray', interpolation='nearest', vmin=0, vmax=1)
    plt.show()

    return Y_test_pred

# 顔検出関数
def detect(filename, cascade_file="lbpcascade_animeface.xml"):
    if not os.path.isfile(cascade_file):
        raise RuntimeError("%s: not found" % cascade_file)

    # カスケード分類器の特徴量を取得
    cascade = cv2.CascadeClassifier(cascade_file)

    # ファイル読み込み
    img = cv2.imread(filename)

    # グレースケール変換
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # ヒストグラム表示用のイメージ作成
    gray = cv2.equalizeHist(gray)

    faces = cascade.detectMultiScale(gray,
            scaleFactor = 1.1,
            minNeighbors = 5,
            minSize = (24,24))

    return img, faces

# 検出された顔を識別する関数
def recognition(image,faces):
    face_images = []
    i=0
    for (x, y, w, h) in faces:
        dst = image[y:y+h, x:x+w]#切り取り
        dst = cv2.resize(dst, (28, 28))#リサイズ
        face_images.append(dst)#配列に落とす
        i += 1

    return forward(face_images), image

#識別結果を描画する関数
def draw_result(image, faces, result):
    count = 0
    for (x, y, w, h) in faces:
        classNum = 0
        result_data = result[count]
        rate = np.sort(result_data)[::-1]
        score = rate[0]
        classNum = result_data.argmax()
        recognized_class = chara_name[result_data.argmax()]
        if classNum == 0:
            color = (255,255,51)
            cv2.rectangle(image, (x, y), (x+w, y+h), color, 3)
            cv2.putText(image,"KosakaKirino",(x, y+h+15),cv2.FONT_HERSHEY_COMPLEX, 0.5 ,color)
            cv2.putText(image,"%4.1f%%" %(score*100),(x,y+h+30),cv2.FONT_HERSHEY_COMPLEX, 0.5 ,color)
        elif classNum == 1:
            color = (0,0,0)
            cv2.rectangle(image, (x, y), (x+w, y+h), color, 3)
            cv2.putText(image,"KosakaKyosuke",(x, y+h+15),cv2.FONT_HERSHEY_COMPLEX, 0.5 ,color)
            cv2.putText(image,"%4.1f%%" %(score*100),(x,y+h+30),cv2.FONT_HERSHEY_COMPLEX, 0.5 ,color)
        elif classNum == 2:
            color = (255,0,255)
            cv2.rectangle(image, (x, y), (x+w, y+h), color, 3)
            cv2.putText(image,"GokoRuri",(x, y+h+15),cv2.FONT_HERSHEY_COMPLEX, 0.5 ,color)
            cv2.putText(image,"%4.1f%%" %(score*100),(x,y+h+30),cv2.FONT_HERSHEY_COMPLEX, 0.5 ,color)
        elif classNum == 3:
            color = (255,255,255)
            cv2.rectangle(image, (x, y), (x+w, y+h), color, 3)
            cv2.putText(image,"AragakiAyase",(x, y+h+15),cv2.FONT_HERSHEY_COMPLEX, 0.5 ,color)
            cv2.putText(image,"%4.1f%%" %(score*100),(x,y+h+30),cv2.FONT_HERSHEY_COMPLEX, 0.5 ,color)
        elif classNum == 4:
            color = (255,0,0)
            cv2.rectangle(image, (x, y), (x+w, y+h), color, 3)
            cv2.putText(image,"TamuraManami",(x, y+h+15),cv2.FONT_HERSHEY_COMPLEX, 0.5 ,color)
            cv2.putText(image,"%4.1f%%" %(score*100),(x,y+h+30),cv2.FONT_HERSHEY_COMPLEX, 0.5 ,color)
        elif classNum == 5:
            color = (153,51,255)
            cv2.rectangle(image, (x, y), (x+w, y+h), color, 3)
            cv2.putText(image,"KurusuKanako",(x, y+h+15),cv2.FONT_HERSHEY_COMPLEX, 0.5 ,color)
            cv2.putText(image,"%4.1f%%" %(score*100),(x,y+h+30),cv2.FONT_HERSHEY_COMPLEX, 0.5 ,color)
        elif classNum == 6:
            color = (0,0,255)
            cv2.rectangle(image, (x, y), (x+w, y+h), color, 3)
            cv2.putText(image,"MakishimaSaori",(x, y+h+15),cv2.FONT_HERSHEY_COMPLEX, 0.5 ,color)
            cv2.putText(image,"%4.1f%%" %(score*100),(x,y+h+30),cv2.FONT_HERSHEY_COMPLEX, 0.5 ,color)
        elif classNum == 7:
            color = (0,255,0)
            cv2.rectangle(image, (x, y), (x+w, y+h), color, 3)
            cv2.putText(image,"Other",(x, y+h+15),cv2.FONT_HERSHEY_COMPLEX, 0.5 ,color)
            cv2.putText(image,"%4.1f%%" %(score*100),(x,y+h+30),cv2.FONT_HERSHEY_COMPLEX, 0.5 ,color)

        count+=1

    return image

#ファイル読み込み
img, faces = detect(filename)

result, image = recognition(img, faces)

image = draw_result(image, faces, result)

cv2.imwrite(filename + '_detect.png',image)

完成まで気が焦っていたのもあってほぼやっつけで作ってました((((爆

基本的にはほとんどプログラムの再利用です.ここをオブジェクトごとに分離できていれば完璧だったのですが・・・

分類器の作成には以下のサイトを参考にいたしました.

ket-30.hatenablog.com

先人たちの知恵の結晶は本当に素晴らしいものですね.そして分類した結果がこちら!

f:id:ST_ha1cyon:20161031135935p:plain

100%は言いすぎでしょう・・・w

f:id:ST_ha1cyon:20161031140102p:plain

うををををを!!

f:id:ST_ha1cyon:20161031140151p:plain

だいぶいい感じに分類できているのではないでしょうか!(一部プログラム修正前の画像混じってますがあってるからいいでしょ(((((()

まぁもちろん失敗例もあり・・・

f:id:ST_ha1cyon:20161031140254p:plain

f:id:ST_ha1cyon:20161031140307p:plain

どうしてもちょこちょこバジーナさんの眼鏡を顔と誤認識してしまう時があるようです.ここはOpenCVのパラメータチューニングのところですので,しっかり調整せねば・・・

そんなこんなで無事目標を達成できたのですが,正直この結果にはまだまだ改良点がいっぱいあってもうすこし綺麗にしていかないといけないです.さらにいうなれば,コードもゴミがぼろぼろしてたりと公開できるような内容としてはほど遠いです.しかし,こうして自分のやってきた勉強が結果として現れることはすごく嬉しいことですし,なによりも次のモチベーションへとつながっていきます!

なので,まだまだの内容ではあるのですが公開という形にしました.ですので,指摘とかあればたくさんくれると非常に嬉しいです!この結果を踏まえた上でまた新たな方向へ挑戦していこうかなと考えています!

さぁ次は何を勉強しましょうかね!

おまけ

f:id:ST_ha1cyon:20161031140959p:plain

顔さえ認識してくれれば,なんでも分類はしてくれるみたいなので応用はできそうですw