[python]感情認識Webアプリの作成

深層学習を使って感情認識プログラムを作成し、FlaskによりWebアプリにしました。主な手順は以下のとおりです。

1. 画像スクレイピング
2. 顔認識 & 領域の切出し & クレンジング:手作業
3. 学習
4. Webアプリ作成

1. 画像スクレイピング

学習するための画像に関しては、どこかのプロセスで画像を目視で確認する必要があるため、スクレイピングの時点で手作業をいれました。

具体的には、Chromeページ上で選択したすべての画像をワンクリックでダウンロードできるFatkunを使い、感情(喜び、怒り、驚き、悲しみ)毎に分類したフォルダを作成しました。 Fatkunに関しては、以下の動画を参考にしました。

2. 顔認識 & 領域の切出し & クレンジング:手作業

1で分類した表情画像のサイズを統一して、顔認識を行い、顔の部分のみ切取ります。顔認識はhaarcascade の識別器を使いました。

顔認識及び切出しは以下のサイトを参考にしました。

ここで 苦戦したところとして imreadは日本語ファイルを上手く読み込めないので、Numpy.fromfile を使いました。また imwrite で書き出したファイル名が文字化けしたので、ファイル名変換フリーソフトを使ってローマ字にて保存しました。

import cv2
import numpy as np

img_array = np.fromfile("ダウンロード.jpg", dtype=np.uint8)
img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)

3. 学習

感情画像をVGG16を使って学習しました。

from keras.models import Sequential
from keras.layers import Conv2D,MaxPooling2D
from keras.layers import Activation,Dropout,Flatten,Dense
from keras.utils import np_utils
import keras
import numpy as np
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.layers import Dense, Dropout, Flatten, Input
from tensorflow.keras.applications.vgg16 import VGG16
from tensorflow.keras.models import Model, Sequential
from tensorflow.keras import optimizers
from PIL import Image

path_Angry = os.listdir("Angry directry path ")
path_Joy = os.listdir("Joy directry path")
path_Sorrow = os.listdir("Sorrow directry path")
path_Surprise = os.listdir("Surprise directry path")

img_Angry = []
img_Joy = []
img_Sorrow = []
img_Surprise = []

for i in range(len(path_Angry)):
img = cv2.imread("Angry directry path" + path_Angry[i])
img = cv2.resize(img, (50,50))
img_Angry.append(img)
for i in range(len(path_Joy)):
img = cv2.imread("Joy directry path" + path_Joy[i])
img = cv2.resize(img, (50,50))
img_Joy.append(img)
for i in range(len(path_Sorrow)):
img = cv2.imread("Sorrow directry path"+ path_Sorrow[i])
img = cv2.resize(img, (50,50))
img_Sorrow.append(img)
for i in range(len(path_Surprise)):
img = cv2.imread("Surprise directry path" + path_Surprise[i])
img = cv2.resize(img, (50,50))
img_Surprise.append(img)

X = np.array(img_Angry + img_Joy + img_Sorrow + img_Surprise)
y = np.array([0]*len(img_Angry) + [1]*len(img_Joy)+ [2]*len(img_Sorrow)+ [3]*len(img_Surprise))

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の定義をして、vggのImageNetによる学習済みモデルを作成してください
input_tensor = Input(shape=(50, 50, 3))
vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)

# 特徴量抽出部分のモデルを作成しています
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))
top_model.add(Dense(256, activation='sigmoid'))
top_model.add(Dropout(0.5))
top_model.add(Dense(4, activation='softmax'))

# vgg16とtop_modelを連結してください
model = Model(inputs=vgg16.input, outputs=top_model(vgg16.output))

# 以下のfor文を完成させて、15層目までの重みを固定させてください
for layer in model.layers[:15]:
layer.trainable = False

# 学習の前に、モデル構造を確認してください
model.summary()

# コンパイルをしています
model.compile(loss='categorical_crossentropy',
optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),
metrics=['accuracy'])

#学習を開始
model.fit(X_train, y_train, batch_size=32, epochs=50)

# 精度の評価
scores = model.evaluate(X_test, y_test, verbose=1)

print('Test loss:', scores[0])
print('Test accuracy:', scores[1])

# データの可視化(テストデータの先頭の10枚)
#for i in range(10):
# plt.subplot(2, 5, i+1)
# plt.imshow(X_test[i].reshape((50,50,3)),'gray')
#plt.suptitle("10 images of emotion test data",fontsize=20)
#plt.show()

def pred_emotion(img):
img = cv2.resize(img, (50, 50))
pred = np.argmax(model.predict(np.array([img])))
if pred == 0:
return '0:Angry'
elif pred == 1:
return '1:Joy'
elif pred == 2:
return '2:Sorrow'
elif pred == 3:
return '3:Surprise'

# pred_gender関数に顔写真を渡して感情を予測します
for i in range(5):
img = cv2.imread( "path"+ path_Joy[i]) 
plt.imshow(img)
plt.show()
print(pred_emotion(img))

#resultsディレクトリを作成
result_dir = 'results'
if not os.path.exists(result_dir):
os.mkdir(result_dir)
# 重みを保存
model.save(os.path.join(result_dir, 'model.h5')) 

4. Webアプリ作成

Flaskをつかって、3の学習ファイルをWebアプリにしました。

① デスクトップなどに適当な名前のフォルダ(例えばmain_app)を作成する。

② main_appフォルダの中にmain.pyを作成し、上記のコードをコピー&ペーストする

③ main_appフォルダの中にtemplatesフォルダを作成し、その中にindex.htmlを作成する

④ main_appフォルダの中にstaticフォルダを作成し、その中にstylesheet.cssを作成する(Flaskはtemplatesフォルダの中からhtmlファイルを探し、staticフォルダの中からcssファイルを探すことになっています)コードをstylesheet.cssにコピー&ペーストする

⑤ cd main_app/でemotion_appフォルダに移動する

⑥ ターミナルにpython main.pyと入力して実行する

⑦ http://127.0.0.1:5000/にアクセスする

#main.py

import os
from flask import Flask, request, redirect, render_template, flash
from werkzeug.utils import secure_filename
from tensorflow.keras.models import Sequential, load_model
from tensorflow.keras.preprocessing import image

import numpy as np

classes = ["0.Angry","1.Joy","2.Sorrow","3.Surprise"]

image_size = 50

UPLOAD_FOLDER = "uploads"
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])

app = Flask(__name__)

def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

model = load_model('./model.h5')#学習済みモデルをロード


@app.route('/', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
flash('ファイルがありません')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('ファイルがありません')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(UPLOAD_FOLDER, filename))
filepath = os.path.join(UPLOAD_FOLDER, filename)

#受け取った画像を読み込み、np形式に変換
#img = image.load_img(filepath, grayscale=True, target_size=(image_size,image_size))
img = image.load_img(filepath, grayscale=False, target_size=(image_size,image_size))
img = image.img_to_array(img)
data = np.array([img])
#変換したデータをモデルに渡して予測する
result = model.predict(data)[0]
predicted = result.argmax()
pred_answer = "これは " + classes[predicted] + " です"

return render_template("index.html",answer=pred_answer)

return render_template("index.html",answer="")

if __name__ == "__main__":
app.run()

これで特定の顔がうつっている画像を取り込んで感情識別します。

今後は以下の改良を加えていきたいです。

・認識率を上げる

・動画による感情認識を行う。




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