「笑顔が素敵かどうか」をAIに診断してもらうアプリを作ってみた（Emotion API）

この記事は

* [Cogbot Advent Calendar](https://qiita.com/advent-calendar/2017/cogbot) の21日目の記事
* [Monaca Advent Calendar](https://qiita.com/advent-calendar/2017/monaca) の21日目の記事

です。


人に会う仕事、接客する仕事に携わる人たちは、表情にとても気を使っています。
毎日鏡に向かって笑顔を作り、良い笑顔ができているかどうか、チェックする人も多いと聞きます。

しかし、自分が作った笑顔が魅力的かどうか、自分自身ではなかなか判断がつかないもの。

そこで、[MS Azure](https://azure.microsoft.com) の [Cognitive Services](https://azure.microsoft.com/ja-jp/services/cognitive-services/) の一つ、[Emotion API](https://azure.microsoft.com/ja-jp/services/cognitive-services/emotion/) を利用して

「あなたの笑顔が魅力的かどうか、AIに判断してもらおう！」

という (お遊び) アプリを作ってみました。

## 実際の挙動

フォームから、自分の表情を写真で撮影し、アプリにアップロードします。
(ここではサンプルの写真を利用。[「ぱくたそ」](https://www.pakutaso.com/)、[「フォト蔵」](http://photozou.jp/)さんからお借りしました。)

### サンプル１

![sc_1.jpg](https://qiita-image-store.s3.amazonaws.com/0/83767/1877ee2f-fa23-caf7-6730-53e90e6959f6.jpeg)

最初の写真。とても素敵な笑顔です。

![sc_2.jpg](https://qiita-image-store.s3.amazonaws.com/0/83767/57fe535a-80be-ee8c-0339-1fbc4b9bbe1c.jpeg)

AIも、「最高の笑顔です」と認識してくれました。


### サンプル2

![sc_3.jpg](https://qiita-image-store.s3.amazonaws.com/0/83767/6e5d869a-8d89-d8c3-96e8-11a8d6b29bbf.jpeg)


ややアルカイックスマイル気味ですが、素敵な笑顔です。

![sc_4.jpg](https://qiita-image-store.s3.amazonaws.com/0/83767/269e8636-ab93-90b7-d461-90d071b6785f.jpeg)



やはり良い笑顔と判定してくれたようです。



### サンプル3

![sc_5.jpg](https://qiita-image-store.s3.amazonaws.com/0/83767/722566a2-7c88-4944-98f1-f3f7c66a4014.jpeg)


どうすれば人々は苦しみから開放されるのか、悩んでいるのでしょうか。
憂いを秘めた表情です。


![sc_6.jpg](https://qiita-image-store.s3.amazonaws.com/0/83767/c8d5149d-5c74-2eb5-1b5d-f3e1a93a73fe.jpeg)


AIは「お疲れのご様子ですね」と判断したようです。


このように、素敵な笑顔を作れているかどうか、アプリ が診断してくれます。



## 仕様

Emotion API に対して、人間の表情が写っている写真を投げると、JSONのレスポンスデータが返って来ます。

![emotion_top.png](https://qiita-image-store.s3.amazonaws.com/0/83767/63087293-b67c-a730-adaf-4be9b90f5bde.png)


画像は、Emotion API のページからキャプチャしたものです。サンプルの写真と、写真に対する返り値のJSONが表示されています。このJSONデータには、表情から感情を読み取り、各種の感情をパラメーター化した結果が含まれています。

```
 JSON:
 [
  {
    "faceRectangle": {
      "top": 114,
      "left": 212,
      "width": 65,
      "height": 65
    },
    "scores": {
      "anger": 1.0570484E-08,
      "contempt": 1.52679547E-09,
      "disgust": 1.60232943E-07,
      "fear": 6.00660363E-12,
      "happiness": 0.9999998,
      "neutral": 9.449728E-09,
      "sadness": 1.23025981E-08,
      "surprise": 9.91396E-10
    }
  },
```

レスポンスデータの「scores」には、怒り(anger)、恐れ(fear)、幸せ(hapiness)などの各種パラメーターが、最低値0-最高値1の間の値で付与されています。

今回のスマイルチェッカーでは

* 良い笑顔 = 幸せ度数が高い笑顔

と定義して、返り値のJSONから「hapiness」の値を抽出。hapiness値が高い= 素敵な笑顔、として判断するようにしました。


### Emotion API の準備

まず最初に、Emotion API を利用できるよう、Azure からAPIキーを発行します。Azure のダッシュボードから
「新規」を選び、検索から「emotion」と入力。Emotion API を選択します。

![emo_1.png](https://qiita-image-store.s3.amazonaws.com/0/83767/ddcb9d62-e9ca-5547-bcd1-8ba9ccbfe813.png)


Emotion API を選択して、作成ボタンをクリックします。

![emo_2.png](https://qiita-image-store.s3.amazonaws.com/0/83767/ccc74270-27f0-5565-d6e4-50027661ea3e.png)


各設定項目を入力、選択します。

![emo_3.png](https://qiita-image-store.s3.amazonaws.com/0/83767/776ab824-b7dd-3ffb-8132-50824c870580.png)

Emotions API がデプロイされたら、APIキーを取得します。

![emo_4.png](https://qiita-image-store.s3.amazonaws.com/0/83767/7e65ac07-6fca-bfcc-eeff-da96e3f0431e.png)

Emotion API の使い方は以下に詳しく記述されています。

* https://docs.microsoft.com/en-us/azure/cognitive-services/emotion/home
* https://westus.dev.cognitive.microsoft.com/docs/services/5639d931ca73072154c1ce89/operations/563b31ea778daf121cc3a5fa


## 実際のコード

先程取得したAPIキーを利用して、コードを書きます。

今回のサンプルではシンプルに

* html5 のFile APIで、写真を取得(撮影)
* 取得した写真を、Emotion API に送信
* 返り値からhapinessの値を取得
* 条件分岐で、出力するメッセージを変更

としました。Azure への画層送信やサムネイルの表示など、画像の各種処理には jQueryを使ってます。

**※サンプル実装ということで、APIキーを直接html内に書く形にしましたが、APIキーが視認できる状態は危険ですのでご注意ください。第三者に勝手に使われてしまいます** 。


```
<!DOCTYPE html>
<html lang="ja">
<head>
  <title>スマイルチェッカー</title>
  <meta charset="utf-8">
  <meta content="width=device-width, initial-scale=1" name="viewport">
</head>
<body>
  <div class="container" id="container">
    <h2>スマイルチェッカー</h2>
    <div id="message"></div>
    <div id="content">
      <p><input id="fileToUpload" name="upload" type="file"></p>
    </div>
  </div>
  <script src="https://code.jquery.com/jquery-2.1.4.min.js">
  </script> 
  <script>

       var html = '';

    $(function() {

     $('input[type=file]').after('<span id=\"image\"><\/span>');

     $('input[type=file]').change(function() {

       var file = $(this).prop('files')[0];

       if (!file.type.match('image.*')) {
         $(this).val('');
         $('span').html('');
         return;
       }

       var reader = new FileReader();

       reader.onload = function() {
         var img_src = $('<br><img height=\"300\">').attr('src', reader.result);
         $('span').html(img_src);
       }
       reader.readAsDataURL(file);


         $.ajax({
           url: "https://westus.api.cognitive.microsoft.com/emotion/v1.0/recognize",
           beforeSend: function(xhrObj){
               // Request headers
               xhrObj.setRequestHeader("Content-Type","application/octet-stream");
               xhrObj.setRequestHeader("Ocp-Apim-Subscription-Key","APIキーを記述する");
           },
           type: "POST",
           data: file,
           processData: false,
       })
       .done(function(data) {

           var smileScore = data["0"].scores.happiness;

          if(smileScore > 0.99) {
            var message = "最高の笑顔です！あなたの笑顔はすべての人を幸せにすることでしょう。";
          }

          if(0.7 <= smileScore && smileScore <= 0.99) {
            var message = "良い笑顔です！あなたの笑顔はみんなをホッとさせることでしょう。";
          }

          if(smileScore < 0.7) {
            var message = "疲れているのかな？早めに睡眠を取って、明日に備えましょう。";
          }

        alert(message);

       })
       .fail(function() {
           alert("error");
       });
     });
    });
  </script>
</body>
</html>
```

## Monacaでスマホアプリ用にアレンジする

上記のコードを元に、[Monaca](https://ja.monaca.io/)でスマホアプリ用に整形します。

Monacaは、スマートフォンアプリ開発のフレームワークです。


![sc_7.png](https://qiita-image-store.s3.amazonaws.com/0/83767/bef941f1-a3da-39e8-773c-9e2f304dc3ff.png)


ちょっとだけ Onsen UI を使いました。

```
<!DOCTYPE html>
<html>
<head>
  <title>スマイルチェッカー</title>
</head>
<body>
  -
  <meta charset="utf-8">
  <meta content="width=device-width, initial-scale=1" name="viewport">
  <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
  <meta content="default-src * data: gap: https://ssl.gstatic.com; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'" http-equiv="Content-Security-Policy">
  <script src="components/loader.js">
  </script> 
  <script src="lib/onsenui/js/onsenui.min.js">
  </script>
  <link href="components/loader.css" rel="stylesheet">
  <link href="lib/onsenui/css/onsenui.css" rel="stylesheet">
  <link href="lib/onsenui/css/onsen-css-components.css" rel="stylesheet">
  <link href="css/style.css" rel="stylesheet">
  <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" rel="stylesheet">
  <div class="center">
    スマイルチェッカー
  </div>
  <div class="container" id="container">
    <div id="message"></div>
    <div id="content">
      <div class="form-group">
        <input class="form-control-file" id="diletoUpload" name="upload" type="file">
      </div>
    </div>
  </div>
  <script src="https://code.jquery.com/jquery-2.1.4.min.js">
  </script> 
  <script>


    $(function() {

     $('input[type=file]').after('<span id=\"image\"><\/span>');

     $('input[type=file]').change(function() {

       var file = $(this).prop('files')[0];

       if (!file.type.match('image.*')) {
         $(this).val('');
         $('span').html('');
         return;
       }

       var reader = new FileReader();

       reader.onload = function() {
         var img_src = $('<br><img width=\"300\">').attr('src', reader.result);
         $('span').html(img_src);
       }
       reader.readAsDataURL(file);


         $.ajax({
           url: "https://westus.api.cognitive.microsoft.com/emotion/v1.0/recognize",
           beforeSend: function(xhrObj){
               // Request headers
               xhrObj.setRequestHeader("Content-Type","application/octet-stream");
               xhrObj.setRequestHeader("Ocp-Apim-Subscription-Key","APIキーを記述する");
           },
           type: "POST",
           data: file,
           processData: false,
       })
       .done(function(data) {

           var smileScore = data["0"].scores.happiness;

          if(smileScore > 0.99) {
            var message = "最高の笑顔です！あなたの笑顔はすべての人を幸せにすることでしょう。";
          }


          if(0.7 <= smileScore && smileScore <= 0.99) {
            var message = "良い笑顔です！あなたの笑顔はみんなをホッとさせることでしょう。";
          }


          if(smileScore < 0.7) {
            var message = "疲れているのかな？早めに睡眠を取って、明日に備えましょう。";
          }

        alert(message);


       })
       .fail(function() {
           alert("error");
       });

     });

    });


  </script>
</body>
</html>
```

## 応用

今回は単純にEmotion API の「hapiness」の値だけを使って条件分岐しました。より複雑に判断するなら、angerなど、他のパラメーターも考慮して判断するのも良いかもしれません。

また、写真と同時に、一言コメントを記述して、[Movable Type](https://www.sixapart.jp/movabletype/) やmBaaS系のサービスに笑顔+コメント付きで投稿する「笑顔日記」的なサービスも面白いかも。

他にもビジネスになりそうなアイディアはいくつかあるのですが、事業化をご希望の方はご連絡ください(嘘)