【python】顔にでてしまっている表情を評価

あなたは仕事中、他人に対して優しくありたいと思っています。なんで俺がやらなきゃいけないのという怒りを堪えて、めんどくさい案件を文句を言わずに対処しています。昼飯あとで食べよう、、ふう～とりあえずキリついた！少し背伸びしてから机を少しかたずけて、さてお昼っっ、という瞬間、あなたの余裕がありそうな瞬間を狙っていた後輩が、新たなめんどくさい案件をもってきました。あれ、気持ちが顔に出てませんか？後輩ビビってますよ？ビビることによってインシデント報告されなくなり、さらに重大な事件が。。。ということまではいかないと思いますが、仕事中気付かないうちに、怖い顔になっていませんか。それとも、今日はニコニコしていい感じでしょうか。画像認識技術を使って、客観的に評価しましょう。

パソコンに内蔵カメラもしくはUSBでつないだwebカメラを使って顔を撮影し、分析して怒り、むかつき、恐怖、幸せ、悲しい、驚き、中性で評価します。撮影間隔と撮影時間を自分で設定し、結果はグラフ化される上、エクセルで保存されます。中身は、tensorflowと表情学習済みモデルを使用しています。pythonなんて使えない！！という人に渡したいと思いましたが、pyinstaller使ってもどうしてもexe化できませんでした。tensorflowとかkerasとか激重なパッケージがあるからでしょうか。できる人教えてください。

![hyoujyou.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695506/e65e4863-4678-f059-ddb7-ad4d8154ae04.jpeg)

（最初は、獣医師が患者に説明をするときの評価用として設計しました。コード中の名前は適宜変えてください。）

１. 環境を作成します。
winはAnacondaのコマンドプロンプトを起動します。Macはターミナルを起動。以下を打ち込んでいけば、実行環境が整います。
必要なパッケージをダウンロードします。tensorflow使うためには、pythonのバージョンを落とさなければなりません。3.8ではtensorflowを使えません。3.6にしましょう！！

Win:　anacondaプロンプトで以下を順番に。
conda create -n face python=3.6.10
proceed ([y]/n)? →　y
activate face
pip install opencv-python
pip install pandas
pip install matplotlib
pip install openpyxl
pip install tensorflow
pip install keras

Mac:　Terminalで以下を順番にコピペし、１つづつEnterを押していく。
conda create -n face python=3.6.10 anaconda
proceed ([y]/n)? →　y
conda activate face
pip install opencv-python
pip install pandas
pip install matplotlib
pip install openpyxl
pip install tensorflow
pip install keras

２. githubにデプロイされている、Octavio Arriagaさんという方が作成した顔の表情認識モデルを使用します。こんなすばらしいものが無料でgithubにデプロイされているということに感謝しながらダウンロードします。
zipをダウンロードして、そこからfer2013_mini_XCEPTION.110-0.65.hdf5というファイルを取り出して、これから使うためどこかのフォルダに入れてください。
https://github.com/oarriaga/face_classification/tree/master/trained_models/emotion_models

３. 上記のファイルを保存したフォルダに、以下のpythonファイルを作ってください。

```python:face.py

from tkinter import *
from tkinter import ttk
import tkinter
import cv2
import os
import time
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np
from keras.models import load_model
from keras.preprocessing.image import ImageDataGenerator
from keras.preprocessing import image
import glob
import openpyxl
import datetime
from tkinter import filedialog


os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # not to show an error message related to CPU

def _destroyWindow():  # function to close the tinker window
    root.quit()
    root.destroy()
    
def askfileplace():
    cd =  filedialog.askdirectory() # function to ask which directly to use for saving image and excel    
    path.set(cd)

def resource_path(relative_path):   # function to get the target path
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.dirname(__file__)
    return os.path.join(base_path, relative_path)
    
# create a main function which is to be used after pressing a button
def save_frame_camera_key(device_num,interval1,duration1,vetname1,name1,path1):

    os.makedirs("{}//{}_{}".format(path1,vetname1,name1)) # create a folder to save above
    cap = cv2.VideoCapture(device_num)                    # create a videocapture object    

    if not cap.isOpened():  # check if it has been opened
        return

    n = 0 # increment initiated with 0
    while n <= int(duration1)/int(interval1) :  # calculation of the terminating point of the increment 

        ret, frame = cap.read() # read the object which returens two variables
        cv2.imwrite("{}//{}_{}//{}.jpg".format(path1,vetname1,name1,n), frame) # save the pictures
        time.sleep(int(interval1)) # wait for interval
        n += 1 # increment

    image_paths = glob.glob("{}//{}_{}//*.jpg".format(path1,vetname1,name1)) # get the jpg files in the folder
    
    result=[] # create a list of the results

    for image_path in image_paths: # process each file in the jpg files
        img = image.load_img(image_path, color_mode="grayscale" , target_size=(64, 64)) # convert it to grayscale
        img_array = image.img_to_array(img) # convert it to array
        pImg = np.expand_dims(img_array, axis=0) / 255 # make it between 0 ~ 1
        
        # read the face recognision trained model hdf5
        model_path = resource_path(".//fer2013_mini_XCEPTION.110-0.65.hdf5")
        emotions_XCEPTION = load_model(model_path, compile=False)
        
        prediction = emotions_XCEPTION.predict(pImg)[0] # predict a image using the model
        result.append(prediction) # append the result to the list named result
    
    df = pd.DataFrame(result) # convert it to DataFrame
    df.columns=['angry','disgust','fear','happy','sad','surprise','neutral'] # name the columns
    df = pd.concat([df,pd.DataFrame(df.mean(axis=0),columns=['Mean']).T]) # create a mean row on the bottom
    df.to_excel("{}//{}_{}//result.xlsx".format(path1,vetname1,name1), sheet_name='result') # save a excel file
    
    # create a graph using mtplotlib
    fig = plt.figure() # create object
    ax=fig.add_subplot(1,1,1) # create subplot indicating the place of the subplot
    mycolor = ["#ff0033", "#cc99ff", "#ffff66", "#ff9999", "#6699ff", "#ff9966", "#99ffcc"] # create a list for color
    ax.pie(df.tail(1).values[0],labels=df.columns,counterclock=True, startangle=90, autopct="%1.1f%%",colors=mycolor) # create piechart
    centre_circle = plt.Circle((0,0),0.7,color='white', fc='white',linewidth=1.25) # make it donuts shape
    fig = plt.gcf() #get current figure
    fig.gca().add_artist(centre_circle) # get the current axes and cover the white donuts on the pie chart

    # Create new window by Tkinter Class
    root = tkinter.Tk()
    root.title("表情分析結果")
    root.withdraw()
    root.protocol('WM_DELETE_WINDOW', _destroyWindow)  # When you close the tkinter window.

    # Canvas and put the figure to the canvas
    canvas = FigureCanvasTkAgg(fig, master=root)  # Generate canvas instance, Embedding fig in root
    canvas.draw()
    canvas.get_tk_widget().pack() #canvas._tkcanvas.pack()
    root.update()
    root.deiconify()  

# create interface
root = Tk()
root.title('表情評価ツール')
root.resizable(True, True)

#create text
frame1 = ttk.Frame(root, padding=(32))
frame1.grid()

label1 = ttk.Label(frame1, text='撮影間隔(秒)', padding=(5, 2))
label1.grid(row=0, column=0, sticky=E)

label2 = ttk.Label(frame1, text='撮影時間(秒)', padding=(5, 2))
label2.grid(row=1, column=0, sticky=E)

label3 = ttk.Label(frame1, text='獣医師名（英数字のみ）', padding=(5, 2))
label3.grid(row=2, column=0, sticky=E)

label4 = ttk.Label(frame1, text='患者名（英数字のみ）', padding=(5, 2))
label4.grid(row=3, column=0, sticky=E)

label5 = ttk.Label(frame1, text='写真・結果の保存先', padding=(5, 2))
label5.grid(row=4, column=0, sticky=E)

# create textboxes
interval = StringVar()
interval_entry = ttk.Entry(
    frame1,
    textvariable=interval,
    width=20)
interval_entry.insert(0,"2")
interval_entry.grid(row=0, column=1)

duration = StringVar()
duration_entry = ttk.Entry(
    frame1,
    textvariable=duration,
    width=20)
duration_entry.insert(0,"10")
duration_entry.grid(row=1, column=1)

vetname = StringVar()
vetname_entry = ttk.Entry(
    frame1,
    textvariable=vetname,
    width=20)
vetname_entry.insert(0,"vet")
vetname_entry.grid(row=2, column=1)

name = StringVar()
name_entry = ttk.Entry(
    frame1,
    textvariable=name,
    width=20)
name_entry.insert(0,datetime.datetime.now().strftime("%Y%m%d_%H%M")) # time
name_entry.grid(row=3, column=1)

path =StringVar()
path_entry = ttk.Entry(
    frame1,
    textvariable=path,
    width=20)
path_entry.grid(row=4, column=1)

path_button = ttk.Button(frame1,text="フォルダ選択",command= lambda : [askfileplace()] )
path_button.grid(row=4, column=2)

# create bottens
frame2 = ttk.Frame(frame1, padding=(0, 5))
frame2.grid(row=5, column=1, sticky=W)

button1 = ttk.Button(
    frame2, text='解析開始',
    command= lambda : [save_frame_camera_key(0,interval.get(),duration.get(),vetname.get(),name.get(),path.get())]) # read the function I made
button1.pack(side=LEFT)

button2 = ttk.Button(frame2, text='終了', command=quit)
button2.pack(side=LEFT)


root.mainloop()
```
４．Anacondaコマンドプロンプトで、以下を実行してください。
pythonファイルのパスを取得してください。Winだったらshift+右クリック、macを右クリック後、option。
python 上記で取得したパス 
（Win入力例：python "C:\Users\山田\Desktop\FaceEmotionAnalysis\face.py"）
（Mac入力例：python /Users/佐藤/Desktop/FaceEmotionAnalysis/face.py）


もしよかったら、自分のＨＰものぞいてみてください。他の例（猫の活動量計ＡＩ）も掲載しています。
https://meknowledge.jpn.org/