AzureのFaceを使ってリアルタイム感情分析をしてみた


背景

Azure Cognitive Services の Face を使うことで、顔写真から感情分析できるようです。
web会議やzoom飲みのときに自分がどんな感情でいるかを可視化してみたいと思い、PCのwebカメラで撮影した自分の顔から、リアルタイム感情分析を行ってみました。
※ウェブ会議などで相手がいる場合は、分析していることを相手にきちんと伝えた上で実行してください。

Microsoft Face紹介ページ:
https://azure.microsoft.com/ja-jp/services/cognitive-services/face/#features

実行結果

今回行った感情分析はこんな感じで出力されます。
PCカメラで取得した自分の顔にFaceで感情分析をかけています。(様々な表情を作ってみました笑)

Faceによる感情分析は、
・anger:怒り
・contempt:軽蔑
・disgust:嫌悪
・fear:恐れ
・happiness:幸福
・neutral:普通
・sadness:悲しみ
・surprise:驚き
の8つの要素の合計値が1になるように出力されます。
今回は、neutral以外の感情を時系列で加算してグラフ化する方法としました。
つまり、上述のグラフは、前半では「幸福」な状態だったが、途中で「怒り」の感情に変化し、最後は「驚き」の感情に変化している様子となっています。情緒不安定ですね笑

以下に、これを実装する手順を示します。

開発環境

macOS Catalina 10.15.6
MacBook Air (13-inch, Early 2015)
Python 3.6.10 Anaconda custom (64-bit)
OpenCV 4.0.0

環境構築

今回は、AzureのFaceを使用します。
Faceを使用するためには、キーとエンドポイントが必要になりますので、その手順を記載します。

まず、上記Microsoft FACE紹介ページから下記ページに移動し、赤枠の「無料で始める」をクリックします。

Microsoftアカウントでログインします。

Azureのホーム画面の左上の三本線マークをクリックし、すべてのサービスを選択します。

検索ウインドウに「Cognitive Services」と入力して出てきたアイコンを選択します。

「追加」ボタンを選択します。

Marketplaceから「Face」を検索し、選択します。

「作成」を選択します

次の画面の必要事項を入力します。名前は自由に記入して問題ありません。

デプロイが完了したら、「リソースに移動」を選択します。

メニューバーから「キーとエンドポイント」を選択すると、作成されたキーとエンドポイントが表示されます。

以上で、AzureのFaceを使用するためのキーとエンドポイントを入手することができました!この後の実装ではこの二つを使います。

ちなみに、課金体系は以下のようになっており、20トランザクション/分に抑えられれば、月当たり30000トランザクションまで無料で使用することができます。
https://azure.microsoft.com/ja-jp/pricing/details/cognitive-services/face-api/

実装

ここから、Faceを使ったリアルタイム感情分析の実装結果を示します。
Faceの実装には、二つのドキュメントを参考にしました。
また、顔認識には、opencvのHarr-like検出器を使用しています。

参考文献:
Computer Vision の REST API と Python を使用してローカル画像を分析する:
https://docs.microsoft.com/ja-jp/azure/cognitive-services/computer-vision/quickstarts/python-disk
Face REST API と Python を使用して画像から顔を検出する:
https://docs.microsoft.com/ja-jp/azure/cognitive-services/Face/quickstarts/python
【入門者向け解説】openCV顔検出の仕組と実践(detectMultiScale):
https://qiita.com/FukuharaYohei/items/ec6dce7cc5ea21a51a82

Face_emotion.py
import requests
import json
import time
import numpy as np
import cv2
from datetime import datetime
import matplotlib.pyplot as plt
import pandas as pd

##初期設定
cap=cv2.VideoCapture(0) #0にするとmacbookのカメラ、1にすると外付けのUSBカメラにできる
csv_name = datetime.now().strftime('%Y%m%d_%H%M')#csvファイルとして保存するファイル名
data_name = ["anger","contempt","disgust","fear","happiness",'sadness','surprise']#保存データの系列
emotion_data =[0,0,0,0,0,0,0]#初期値
count = 0#撮影回数を示すカウンタ

##顔認識の設定
cascade_path =  '/**********************************/haarcascade_frontalface_alt.xml'# 顔判定で使うxmlファイルを指定する。(opencvのpathを指定)
cascade = cv2.CascadeClassifier(cascade_path)

##Faceの設定
subscription_key = '********************************'#ここに取得したキー1を入力
assert subscription_key
face_api_url = 'https://*************************face/v1.0/detect'#ここに取得したエンドポイントのURLを入力

##実行
while True:
    r, img = cap.read()
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#グレースケールに変換
    faces=cascade.detectMultiScale(img_gray, scaleFactor=1.1, minNeighbors=1, minSize=(100, 100))#顔判定 minSizeで顔判定する際の最小の四角の大きさを指定できる。(小さい値を指定し過ぎると顔っぽい小さなシミのような部分も判定されてしまう。)

    if len(faces) > 0: #顔を検出した場合
        for face in faces:
            now = datetime.now()#撮影時間
            filename = str(now)+'.jpg'#保存するfilename
            cv2.imwrite(filename, img)#画像の書き出し

            image_data = open(filename, "rb").read()#処理をする画像を選択
            headers = {'Ocp-Apim-Subscription-Key': subscription_key,
                       'Content-Type': 'application/octet-stream'}
            params = {
                'returnFaceId': 'true',
                'returnFaceLandmarks': 'false',
                'returnFaceAttributes': 'age,gender,headPose,smile,facialHair,glasses,emotion,hair,makeup,occlusion,accessories,blur,exposure,noise',
                }
            response = requests.post(face_api_url, headers=headers,
                                        params=params, data=image_data)#FaceAPIで解析

            response.raise_for_status()
            analysis = response.json()#json出力

            #faceのjsonから抽出する項目をピック
            result = [analysis[0]['faceAttributes']['emotion']['anger'],analysis[0]['faceAttributes']['emotion']['contempt'],
                        analysis[0]['faceAttributes']['emotion']['disgust'],analysis[0]['faceAttributes']['emotion']['fear'],
                        analysis[0]['faceAttributes']['emotion']['happiness'],analysis[0]['faceAttributes']['emotion']['sadness'],
                        analysis[0]['faceAttributes']['emotion']['surprise']]

            emotion_data = np.array(result) + np.array(emotion_data)

            df = pd.DataFrame({now:emotion_data},
                            index=data_name)#取得データをDataFrame1に変換しdfとして定義

            if count == 0:#初期
                print(df)
            else:
                df = pd.concat([df_past,df],axis = 1, sort = False)#dfを更新
                print(df)

            plt.plot(df.T)#dfの行列を反転
            plt.legend(data_name)#凡例を表示
            plt.draw()#グラフ描画
            plt.pause(4)#ウェイト時間(=Azure更新時間)
            plt.cla()#グラフを閉じる

            count = count + 1#撮影回数の更新
            df_past = df#df_pastを更新

            df.T.to_csv(csv_name+'.csv')#感情分析結果をcsvファイルとして書き出し

検証結果

上記を実行して、友人とzoomで会話したときの私の表情を分析してみました。

左側がzoomの画面、右側が私の表情を分析したものです。
友人との会話なので、hapinessが高くなっていることがわかります!
このときは、笑顔で話していたので、笑顔=hapinessと認識されるようです。

まとめ

今回はAzureのFaceを使ってリアルタイム感情分析を行いました。リモートワークが常態化し、web会議の活用が増えている中で、リモートハラスメントなんて言葉も生まれてきています。web会議中の自分の表情を分析することで、相手に不快な思いをさせていないか、なんてことに使えたら面白いかな、と思います。