PythonMania

普段はロボットとAIを組み合わせて色々作ってます。Python関係以外も色々投稿していくと思います。

【Python】画像認識 - 画像の読み込み・前加工から学習精度検証までを一通りやってみる【DeepLeaning】


今回は前回同様CNNを活用して、画像認識のやり方についてまとめていきます。

 

今回も題材にしたコンペはこちらです。

 

 
www.kaggle.com



全体の流れとして

①データの読み込み
②OpenCVを使ったデータ加工(マスク処理等)
③Kerasによるデータ複製
④KerasでCNN構築
⑤モデル評価・推論等

のような流れです。

以下コードになります。

 

 

#必要なライブラリのインポート
import cv2
from glob import glob
import numpy as np
from matplotlib import pyplot as plt
import math
import pandas as pd

#画像のサイズ指定
ScaleTo = 70
seed = 7

#トレーニングデータの読み込み
path = "./train/*/*.png"
files = glob(path)

trainImg = []
trainLabel = []
j = 1
num = len(files)

for img in files:
    print(str(j) + "/" + str(num) , end="\r")
    trainImg.append(cv2.resize(cv2.imread(img) ,(ScaleTo,ScaleTo)))
    trainLabel.append(img.split("/")[-2])
    j += 1

trainImg = np.asarray(trainImg)
trainLabel = pd.DataFrame(trainLabel)


#いくつか画像を確認してみる
for i in range(8):
    plt.subplot(2,4,i+1)
    plt.imshow(trainImg[i])





#-----------------------------------------------------------------------
#データを整形する
#今回は目的物(植物)が全て緑であるということを生かし
#緑の部分だけを残して他の部分を削除するマスクを作成
#手順は①ノイズの除去②RGB→HSVに変換③マスクを作成④ブールマスクを作成⑤マスクの適用

clearTrainImg = []
examples = [];getEx = True

for img in trainImg:
    #ぼかしを入れてノイズを除去
    blurImg = cv2.GaussianBlur(img ,(5,5),0)
    #RGBからHSVに変換
    hsvImg = cv2.cvtColor(blurImg , cv2.COLOR_BGR2HSV)
    #マスクを作成
    lower_green = (25,40,50)
    upper_green = (75,255,255)
    mask = cv2.inRange(hsvImg , lower_green ,upper_green)
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(11,11))
    mask = cv2.morphologyEx(mask , cv2.MORPH_CLOSE,kernel)

    #ブールマスクの作成
    bMask = mask > 0

    #マスクの適用
    #空のイメージの作成
    clear = np.zeros_like(img , np.uint8)
    #オリジナル画像にブールマスクを適用
    clear[bMask] = img[bMask]

    clearTrainImg.append(clear)

    if getEx:
      plt.subplot(2, 3, 1); plt.imshow(img)  # Show the original image
      plt.subplot(2, 3, 2); plt.imshow(blurImg)  # Blur image
      plt.subplot(2, 3, 3); plt.imshow(hsvImg)  # HSV image
      plt.subplot(2, 3, 4); plt.imshow(mask)  # Mask
      plt.subplot(2, 3, 5); plt.imshow(bMask)  # Boolean mask
      plt.subplot(2, 3, 6); plt.imshow(clear)  # Image without background
      getEx = False

clearTrainImg = np.asarray(clearTrainImg)

for i in range(8):
    plt.subplot(2, 4, i + 1)
    plt.imshow(clearTrainImg[i])



#-----------------------------------------------------------------------

#データを正則化する

clearTrainImg = clearTrainImg / 255


#ラベルデータを数値化、ワンホットエンコーディングする
#必要なライブラリのインポート
from keras.utils import np_utils
from sklearn import preprocessing
import matplotlib.pyplot as plt

#ラベルのエンコードとクラスの作成
le = preprocessing.LabelEncoder()
le.fit(trainLabel[0])
print("Classes:" + str(le.classes_))
encodeTrainLabels = le.transform(trainLabel[0])

#カテゴリカルラベルの作成
clearTrainLabel = np_utils.to_categorical(encodeTrainLabels)
num_clases = clearTrainLabel.shape[1]
print("Number of classes:" + str(num_clases))

#ラベルごとの数の表示
trainLabel[0].value_counts().plot(kind = "bar")





#-----------------------------------------------------------------------
#モデルの作成

#データセットの分割
from sklearn.model_selection import train_test_split
trainX , testX ,trainY ,testY = train_test_split(clearTrainImg , clearTrainLabel, test_size = 0.1 ,random_state = seed , stratify = clearTrainLabel)


#データの複製(ズーム、回転等)
from keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(
        rotation_range=180,  # randomly rotate images in the range
        zoom_range = 0.1, # Randomly zoom image
        width_shift_range=0.1,  # randomly shift images horizontally
        height_shift_range=0.1,  # randomly shift images vertically
        horizontal_flip=True,  # randomly flip images horizontally
        vertical_flip=True  # randomly flip images vertically
    )
datagen.fit(trainX)


#モデル作成
#必要なライブラリのインポート
import numpy
from keras.models import Sequential
from keras.layers import Dense
from keras.models import Dropout
from keras.layers import Flatten
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.layers import BatchNormalization


numpy.random.seed(seed)

model = Sequential()

model.add(Conv2D(filters=64, kernel_size=(5, 5), input_shape=(ScaleTo, ScaleTo, 3), activation='relu'))
model.add(BatchNormalization(axis=3))
model.add(Conv2D(filters=64, kernel_size=(5, 5), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(BatchNormalization(axis=3))
model.add(Dropout(0.1))

model.add(Conv2D(filters=128, kernel_size=(5, 5), activation='relu'))
model.add(BatchNormalization(axis=3))
model.add(Conv2D(filters=128, kernel_size=(5, 5), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(BatchNormalization(axis=3))
model.add(Dropout(0.1))

model.add(Conv2D(filters=256, kernel_size=(5, 5), activation='relu'))
model.add(BatchNormalization(axis=3))
model.add(Conv2D(filters=256, kernel_size=(5, 5), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(BatchNormalization(axis=3))
model.add(Dropout(0.1))

model.add(Flatten())

model.add(Dense(256, activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.5))

model.add(Dense(256, activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.5))

model.add(Dense(num_clases, activation='softmax'))

model.summary()

# compile model
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])


#-----------------------------------------------------------------------
#学習の実行

from keras.callbacks import ModelCheckpoint,ReduceLROnPlateau,CSVLogger

#学習率を下げるコールバックの設定
learning_rate_reduction = ReduceLROnPlateau(monitor='val_acc',
                                            patience3,
                                            verbose=0,
                                            factor=0.4,
                                            min_lr=0.00001)

# チェックポイント
filepath="drive/DataScience/PlantReco/weights.best_{epoch:02d}-{val_acc:.2f}.hdf5"
checkpoint = ModelCheckpoint(filepath, monitor='val_acc',
                             verbose=1, save_best_only=True, mode='max')
filepath="drive/DataScience/PlantReco/weights.last_auto4.hdf5"
checkpoint_all = ModelCheckpoint(filepath, monitor='val_acc',
                                 verbose=1, save_best_only=False, mode='max')


callbacks_list = [checkpoint, learning_rate_reduction, checkpoint_all]

#学習の実行
hist = model.fit_generator(datagen.flow(trainX, trainY, batch_size=75),
                        epochs=35, validation_data=(testX, testY),
                        steps_per_epoch=trainX.shape[0], callbacks=callbacks_list)




#-----------------------------------------------------------------------



#モデル(精度)の評価
model.load_weights("../input/plantrecomodels/weights.best_17-0.96.hdf5")

data = np.load("../input/plantrecomodels/Data.npz")
d = dict(zip(("trainX","testX","trainY", "testY"), (data[k] for k in data)))
trainX = d['trainX']
testX = d['testX']
trainY = d['trainY']
testY = d['testY']

print(model.evaluate(trainX, trainY)) #トレーニングの精度
print(model.evaluate(testX, testY))  #テスト精度



#コンフュージョンマトリックスの作成
from sklearn.metrics import confusion_matrix
import itertools

def plot_confusion_matrix(cm, classes,
                          normalize=False,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):

    fig = plt.figure(figsize=(10,10))
    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=90)
    plt.yticks(tick_marks, classes)

    if normalize:
        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]

    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, cm[i, j],
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.tight_layout()
    plt.ylabel('True label')
    plt.xlabel('Predicted label')


predY = model.predict(testX)
predYClasses = np.argmax(predY, axis = 1)
trueY = np.argmax(testY, axis = 1)


confusionMTX = confusion_matrix(trueY, predYClasses)


plot_confusion_matrix(confusionMTX, classes = le.classes_)



#-----------------------------------------------------------------------
#テストデータの整形

clearTestImg = []
examples = []; getEx = True
for img in testImg:
    # Use gaussian blur
    blurImg = cv2.GaussianBlur(img, (5, 5), 0)

    # Convert to HSV image
    hsvImg = cv2.cvtColor(blurImg, cv2.COLOR_BGR2HSV)

    # Create mask (parameters - green color range)
    lower_green = (25, 40, 50)
    upper_green = (75, 255, 255)
    mask = cv2.inRange(hsvImg, lower_green, upper_green)
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11))
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)

    # Create bool mask
    bMask = mask > 0

    # Apply the mask
    clear = np.zeros_like(img, np.uint8)  # Create empty image
    clear[bMask] = img[bMask]  # Apply boolean mask to the origin image

    clearTestImg.append(clear)  # Append image without backgroung

    # Show examples
    if getEx:
        plt.subplot(2, 3, 1); plt.imshow(img)  # Show the original image
        plt.subplot(2, 3, 2); plt.imshow(blurImg)  # Blur image
        plt.subplot(2, 3, 3); plt.imshow(hsvImg)  # HSV image
        plt.subplot(2, 3, 4); plt.imshow(mask)  # Mask
        plt.subplot(2, 3, 5); plt.imshow(bMask)  # Boolean mask
        plt.subplot(2, 3, 6); plt.imshow(clear)  # Image without background
        getEx = False

clearTestImg = np.asarray(clearTestImg)

clearTestImg = clearTestImg / 255



#-----------------------------------------------------------------------
#推論の実行
pred = model.predict(clearTestImg)


#提出ファイルの作成
predNum = np.argmax(pred, axis=1)
predStr = le.classes_[predNum]

res = {'file': testId, 'species': predStr}
res = pd.DataFrame(res)
res.to_csv("res.csv", index=False)