[Python] FaceAPIを使った感情認識

# はじめに
沼津高専アドベントカレンダー、技術要素が少なすぎるので少しガチで書きます。
近年話題の機械学習ですが、難易度が高いと勘違いしている人が多いと思います。
というのも、もちろん自分でモデルを作るのはめんどくさいのですが、遊ぶだけであれば何も自分で作る必要なんてないんです！そんな素敵なサービスを使った簡単なアプリケーションを紹介します！

#そもそも機械学習って？
機械学習（きかいがくしゅう、英: machine learning）とは、人工知能における研究課題の一つで、人間が自然に行っている学習能力と同様の機能をコンピュータで実現しようとする技術・手法のことである。
[Wikipedia 機械学習](https://ja.wikipedia.org/wiki/%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92) より

...なるほどわからない。

まあちゃんとやる方法はあるんですけどまずは導入編！WebAPIを使って遊んでみましょう！！

# FaceAPI(Microsoft Azure)で遊んでみる！
概要は [こちら](https://azure.microsoft.com/ja-jp/services/cognitive-services/face/)

今回使うのはFaceAPIというかEmotionAPIです...
"怒り","悔しい","嫌","恐怖","幸せ","悲しい","驚く"
の7つの中から合う感情を返すプログラムを作成します！！
##### ソースコードについて

今回のプログラムはすべてGoogleColab上で実行できるようにしておきました！
[こちら](https://colab.research.google.com/drive/1UQxApaztDjkWwjbwkd7i0hKpoBAELVOC)を開けばうまくいくと思います！
もちろん[Github](https://github.com/taisuke0812/FaceAPI)にもソースコードは載せていますので参考にしてください。


#### FaceAPIを実際に動かしてみる
要はHTTPリクエストで、顔検出・感情認識・顔識別などができるようになっているということです！
(Microsoftさんマジですごい...)

実行画面です。Colabでやった場合ですね。これをどんどん作っていきます！！
![file_emotion.PNG](https://qiita-image-store.s3.amazonaws.com/0/212639/26c89098-4168-af0d-13b2-bbc8bd66aba6.png)



というわけでこれで遊んでみます！
だいたい何をするときもそうですが、基本的には[公式ドキュメント](https://docs.microsoft.com/ja-jp/azure/cognitive-services/emotion/quickstarts/csharp)を見ることをおすすめします。
今回は日本語で書いてあることが多いのでうれしいですね！
機械学習の基礎となるライブラリなどの公式ドキュメントは日本語で書かれていることが多いので安心です！
(例)[keras](https://keras.io/ja/)、[FaceAPI](https://docs.microsoft.com/ja-jp/azure/cognitive-services/emotion/quickstarts/csharp)、[AWS](https://docs.aws.amazon.com/ja_jp/machine-learning/latest/dg/what-is-amazon-machine-learning.html)

まあ公式ドキュメント通りに進めていきますよ！
## API Keyとファイル名を格納
まずは取得したAPIのkeyを保存する変数を用意しておきますよ！
あと、ファイル名も保存しておきましょう！後々楽になります！

```
#APIkey
API_KEY = "XXXXXXXXX"
#FileName
file = "XXX.jpg"
```

## 前準備
importするライブラリをまとめておきました。

```
import http.client, urllib.request, urllib.parse, urllib.error, base64
import json
import numpy as np
import cv2
import matplotlib.pyplot as plt
```

## APIの仕様に合わせた準備
次にヘッダーと取得したい値について書きます！

```
headers = {
    # Request headers
    'Content-Type': 'application/octet-stream',
    'Ocp-Apim-Subscription-Key': API_KEY,
}

params = urllib.parse.urlencode({
    # Request parameters
    'returnFaceId': 'false',
    'returnFaceLandmarks': 'false',
    'returnFaceAttributes': 'emotion'
})
```
ここで重要なのは、FaceAPIでは、デフォルトでFaceIdとFaceLandmarksがもともとついてきてしまうのでしっかりfalseと設定しておくことです。まあtrueにしておいても困りはしません。いらないデータがついてくるだけです。

## 画像の読み込み・HTTPリクエスト
次に、実際に画像を読み込み、APIに投げてみます。[

```
try:
    conn = http.client.HTTPSConnection('japaneast.api.cognitive.microsoft.com')
    conn.request("POST", "/face/v1.0/detect?%s" % params, open(file,"rb"), headers)
    response = conn.getresponse()
    data = response.read()
    data = json.loads(data)
    emotion = RaadJson(data)
    print(emotion)
    pic = cv2.imread(file)
    pic = cv2.cvtColor(pic, cv2.COLOR_BGR2RGB)
    #herokuでのOpenCVのインストールがめんどくさかったので、デプロイする場合はOpenCVを使わない方がいいです。
    plt.imshow(pic)
    Recognize(emotion)
    conn.close()
except Exception as e:
    print("[Errno {0}] {1}".format(e.errno, e.strerror))
```

まず、HTTPリクエストですので例外処理についても考えます。ここで例外処理を用意しておくと、403などのエラーが出てきたとき容易に対応できるのでおすすめです。
まず、[japaneast.api.cognitive.microsoft.com](japaneast.api.cognitive.microsoft.com) に接続することに注意してください。もし、FaceAPI登録時に東日本以外を選んでしまっていた場合、それに対応するURLを記載してください。

あとは基本的に[公式ドキュメント](https://docs.microsoft.com/ja-jp/azure/cognitive-services/emotion/quickstarts/csharp)通りにやっています。
返ってくる値はjson形式ですのでjson.loads()を使ってあげるときれいにできます。


ReadJson()関数とRecognize()関数は自分で定義した関数です。それについてはこれから説明します。

## jsonの読み込み
json形式のファイル、pythonで読み込むとなると少しめんどうですよね。
そこでこんな感じで関数を作ってみたらどうでしょう！という提案です。

```
def RaadJson(datas):
  emotion = []
  emo = ["anger","contempt","disgust","fear","happiness","sadness","surprise"]
  #anger, contempt, disgust, fear, happiness, sadness and surprise.
  for data in datas:
    #
    f = data["faceAttributes"]
    d = f["emotion"]
    for name in emo:
      emotion.append(d[name])
  return emotion
```
こんな形にするときれいに結果をリストに格納できます。

## 感情認識
次に、実際に感情を決定する関数を作ります。

```
 def Recognize(emotion):
    data = np.array(emotion)
    emo = np.array(["怒り","悔しい","嫌","恐怖","幸せ","悲しい","驚く"])
    num = np.argmax(data)
    pred = "この写真は「" +emo[num] + "」という感情です"
    print(pred)
```

今回は簡単にこのように実装しました。おそらくsoftmax関数のような形で結果を出力しているので、その配列内で一番大きい値のインデックスを取得すれば大丈夫です！

## 実際に公開してみる！
やる気のないページでごめんなさい。
[感情認識をしてみよう！](https://taisuke.herokuapp.com/)に載せてみました！！
画像ファイルをアップロードしてあげると感情を返してくれます。
herokuへのデプロイの方法等は自分で調べてください。僕にはちょっと難しかったです。

![kekka.PNG](https://qiita-image-store.s3.amazonaws.com/0/212639/5b32281c-8368-4547-77a8-7a9a6606c6d0.png )




とは言え、これダサくないですか？
ということで
## LINEbotにしてみる！！
ということで進めていきたいと思います。
ここではまた、Azureに登場してもらいます！
イメージ図を書きました。
![app_discription.png](https://qiita-image-store.s3.amazonaws.com/0/212639/8b7f44e1-20b1-2894-c8b2-ad273fa8248b.png)
ということで実際に書くのはLINE Message APIとFaceAPIをつなぐところだけなんですよね。
とっても簡単です。
さてどうやって作るかという話なんですが、あまりにも良すぎる記事があるのでそれを貼らせてください。
[[python] 画像認識AIでLINEの文字起こし君を作ってみた](https://qiita.com/mikan3rd/items/f0b6ff57dbcedc79a22d)
[Daiさん](https://twitter.com/never_be_a_pm)のnoteとかを参考にするとさらに簡単だと思います。
今回は動かした先の話をさせてください笑

こんな感じで動きます。

<img src = "https://qiita-image-store.s3.amazonaws.com/0/212639/fc5c3b61-2903-a2aa-fa27-75a6425d7f63.jpeg" width= "40%">

ということで友達追加してくれると嬉しいです！！
↓追加はこちらから！
![qr_linebot.PNG](https://qiita-image-store.s3.amazonaws.com/0/212639/1d46c9f2-d98f-8a24-de44-8b1a3fa76da5.png)


# まとめ
FaceAPI簡単すぎる。
kerasでモデルを作るとかちょっと嫌になりますね。
ちなみにChainerのが好きです。
アドベントカレンダーに余裕があったらChainerを使ったVGG16(有名な画像認識モデル)の実装とか紹介したいですね。


#最後に
※この記事を書いているときは松岡茉優さんの画像でテストをしていました。
水曜日の朝、熱愛報道が出てびっくりしましたね。幸せになってくれるといいですね！！！