FaceAPIを使った感情分析

#はじめに
FaceAPIを使った顔認証から感情分析を行ってみました。
FaceAPIを使い、画像を送信するとこのように8種類の感情をJSON形式で取得する事が出来ます。
<img width="944" alt="Screen Shot 2018-07-15 at 18.26.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/200669/405dd7d8-24c6-e791-ad3d-086ff6162024.png">

今回はカメラから取得した画像を送信して、感情をチャートで表示するものを作りました。
![IMG_0748.JPG](https://qiita-image-store.s3.amazonaws.com/0/200669/cb242b80-594a-5247-f9a0-a1bc8d733f26.jpeg)

#導入
##APIキー取得
まずはこちらでAPIキーを取得します。
無料版は1週間有効
https://azure.microsoft.com/ja-jp/try/cognitive-services/?api=face-api

##サンプルアプリ動作確認
クイックスタート内のサンプルアプリで簡単な顔認識が出来ることをまず確認します。
これは画像のURLを指定すると感情分析してくれるものです。
https://docs.microsoft.com/ja-jp/azure/cognitive-services/face/quickstarts/javascript
<img width="752" alt="Screen Shot 2018-07-15 at 18.45.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/200669/826eae76-52cb-475c-e016-b049220ce1b2.png">


#FaceAPIを使った感情分析
##今回実装したもの
・カメラ起動
・カメラのスクリーンショット取得
・取得したイメージ画像をFaceAPIに送信
・返ってきたJSONを元に顔枠線処理(Canvasに表示)
・感情分析処理(Chartjsを使って表示)

今回カメラ起動、スクリーンショット取得は割愛します。


#取得したイメージ画像をFaceAPIに送信

```face.js
function processImage() {
    // Replace <Subscription Key> with your valid subscription key.
    var subscriptionKey = "<Subscription Key>";

    // NOTE: You must use the same region in your REST call as you used to
    // obtain your subscription keys. For example, if you obtained your
    // subscription keys from westus, replace "westcentralus" in the URL
    // below with "westus".
    //
    // Free trial subscription keys are generated in the westcentralus region.
    // If you use a free trial subscription key, you shouldn't need to change 
    // this region.
    var uriBase =
        "https://westcentralus.api.cognitive.microsoft.com/face/v1.0/detect";

    // Request parameters.
    var params = {
        "returnFaceId": "true",
        "returnFaceLandmarks": "false",
        "returnFaceAttributes":
            "age,gender,headPose,smile,facialHair,glasses,emotion," +
            "hair,makeup,occlusion,accessories,blur,exposure,noise"
    };

    // Display the image.
    var sourceImageUrl = document.getElementById("inputImage").value;
    document.querySelector("#sourceImage").src = sourceImageUrl;

    // Perform the REST API call.
    $.ajax({
        url: uriBase + "?" + $.param(params),

        // Request headers.
        beforeSend: function(xhrObj){
            xhrObj.setRequestHeader("Content-Type","application/json");
            xhrObj.setRequestHeader("Ocp-Apim-Subscription-Key", subscriptionKey);
        },

        processData: false,
        type: "POST",

        // Request body.
        // data: '{"url": ' + '"' + sourceImageUrl + '"}',
        data: makeblob(sourceImageUrl),
    })

    .done(function(data) {
        // Show formatted JSON on webpage.
        $("#responseTextArea").val(JSON.stringify(data, null, 2));

        var emodata =(data[0].faceAttributes.emotion);
        
    })
    
};

```

postするデータをURLではなく、画像データにするために
`data: makeblob(sourceImageUrl),`とします。

送信するためには画像データをbase64にエンコードする必要があります。(ここが一番ハマりました)

makeblobはこちら

```face.js
makeblob = function (dataURL) {
    var BASE64_MARKER = ';base64,';
    if (dataURL.indexOf(BASE64_MARKER) == -1) {
        var parts = dataURL.split(',');
        var contentType = parts[0].split(':')[1];
        var raw = decodeURIComponent(parts[1]);
        return new Blob([raw], { type: contentType });
    }
    var parts = dataURL.split(BASE64_MARKER);
    var contentType = parts[0].split(':')[1];
    var raw = window.atob(parts[1]);
    var rawLength = raw.length;

    var uInt8Array = new Uint8Array(rawLength);

    for (var i = 0; i < rawLength; ++i) {
        uInt8Array[i] = raw.charCodeAt(i);
    }

    return new Blob([uInt8Array], { type: contentType });
    }
```

エンコードについてはこちらに詳しく書いてます。
https://stackoverflow.com/questions/34047648/how-to-post-an-image-in-base64-encoding-via-ajax/34064793#34064793

##顔枠線処理(Canvasに表示)
次に顔の枠線処理です。

```
"faceRectangle": {
      "top": 131,
      "left": 177,
      "width": 162,
      "height": 162
    },
```

JSONではこのように座標が返って来るのでこれをcanvasに描画します。

```face.js
var ary = data;

        for(let i=0; i<ary.length; i++){
            
            //------------- 顔枠線処理  -------------//
            var faceRe =(data[i].faceRectangle);
            console.log(faceRe);
            var top = JSON.stringify(faceRe.top);
            var left = JSON.stringify(faceRe.left);
            var width = JSON.stringify(faceRe.width);
            var height = JSON.stringify(faceRe.height);

            var canvas = document.getElementById('emo_canvas');
            var img = document.getElementById('emo_img');
            //canvasの描画モードを2sに
            var ctx = canvas.getContext('2d');
            ctx.lineWidth = 2;
            ctx.strokeStyle = "rgb(196, 6, 249)";
            ctx.strokeRect(left, top, width, height);
            ctx.fillStyle = "rgb(196, 6, 249)";
            ctx.font = "bold 20px 'Arial'";
            ctx.fillText(i+1,left,top,);
            //imgにpng形式で書き出し
            img.src = canvas.toDataURL('image/png');
```

##感情分析処理
枠線処理に続けて感情分析処理をかいていきます。

```face.js
//------------- 感情分析処理  -------------//
            var emodata =(data[i].faceAttributes.emotion);

            var anger = JSON.stringify(emodata.anger);
            var contempt = JSON.stringify(emodata.contempt);
            var disgust = JSON.stringify(emodata.disgust);
            var fear = JSON.stringify(emodata.fear);
            var happiness = JSON.stringify(emodata.happiness);
            var neutral = JSON.stringify(emodata.neutral);
            var sadness = JSON.stringify(emodata.sadness);
            var surprise = JSON.stringify(emodata.surprise);

            
            var str ="";
            var count = i + 1;
           
            str += '<div class=chart>'
            str += '<p>'+ count +'<p>'
            str += '<canvas id="myChart'+ i +'" class="emo_chart"></canvas>'
            str += '</div>'
            $("#chart_box").append(str);

            //appendした動的DOMに対しての処理
            $("myChart"+i).ready(function() {
                var ctx = document.getElementById("myChart"+ i).getContext('2d');
                console.log(ctx);
                ctx.canvas.width = 300;
                ctx.canvas.height = 100;


                var myChart = new Chart(ctx, {
                    type: 'pie',
                    options: {
                        legend: {
                            display: false,
                            labels: {
                                fontColor: 'rgb(255, 255, 255)'
                            }
                        }
                        
                    },
                    data: {
                        labels: ["anger 怒り","contempt 軽蔑","disgust 嫌悪","fear 恐れ","happiness 幸福","neutral 中性","sadness 悲しみ","surprise 驚き"],
                        datasets: [{
                        backgroundColor: [
                            "#ff0000",
                            "#2f4f4f",
                            "#808080",
                            "#b22222",
                            "#ffff00",
                            "#fafad2",
                            "#191970",
                            "#ffa500"

                        ],
                        data: [anger,contempt,disgust,fear,happiness,neutral,sadness,surprise]
                        }]
                    }
                });
            });
        }  

    })
```

charjsを使用する際はライブラリの読み込みが必要です。

```index.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.4/Chart.min.js"></script>
```

これで主なコードは終わりです。
idやclassを適当に書き換えてhtmlかいてもらえれば
画像データから顔枠線表示、感情分析が出来るはずです。


#終わりに
このような顔認識のAPIが手軽に使えるようになってるので色々な用途に応用出来そうです。
ただ通信する必要があるので、リアルタイムの顔認証には向いてません。
精度が落ちますが、リアルタイムの顔認識ならこちらもオススメです。
[フェイストラッキング](https://ics.media/entry/13038)

#参考にさせて頂きました
[Quickstart: Detect faces in an image using JavaScript](https://docs.microsoft.com/ja-jp/azure/cognitive-services/face/quickstarts/javascript)
[chart.jsドキュメント](https://misc.0o0o.org/chartjs-doc-ja/)