Vue.js + Face APIで感情情報を取得する 

Microsoft AzureのFace APIを利用して感情情報を取得します。
今回はオープンソースのJavaScriptフレームワークであるVue.jsで実装してみます。

##Face APIとは?
Microsoft Azureの顔認識APIです。
年齢、感情、性別などの特徴を抽出することができます。

感情については、怒り、軽蔑、嫌悪感、恐怖、喜び、中立、哀しみ、驚きの
8種類を判定することができます。

詳細は以下のページに記載されています。
https://azure.microsoft.com/ja-jp/services/cognitive-services/face/

今回は、こちらのように感情情報をコンソール上に出力します。
![TFlXkreA.jpg](https://qiita-image-store.s3.amazonaws.com/0/227611/41e48b66-2452-6bcf-c345-475f09010603.jpeg)

##今回実施したこと
1.準備 (Vue.jsのプロジェクト環境構築)
2.カメラを起動し、リアルタイムの映像をvideoタグ内に表示する
3.感情分析する

##準備
vue-cliを使って、Vue.jsのプロジェクト環境を構築します。
こちらについては、以下のページの「プロジェクトを作成する」まで
進めて見てください。

[Vue.js を vue-cli を使ってシンプルにはじめてみる](https://qiita.com/567000/items/dde495d6a8ad1c25fa43)

##カメラを起動し、リアルタイムの映像をvideoタグに表示する
次に、PCのフロントカメラを起動し、リアルタイムの映像をvideoタグに表示します。
videoタグに表示された映像は、定期的に静止画としてcanvasに表示します。
(具体的な処理は「感情分析する」の項目を参照してください)

```vuejs:HelloWorld.vue
<template id="main">
  <div id="app">
    <canvas ref="canvas" id="emo_canvas" width="400" height="400"></canvas>
    <div>
      <video ref="video" id="video" width="400" height="400" playsinline muted autoplay></video>
    </div>
  </div>
</template>
```

```javascript:HelloWorld.vue

mounted() {
  //PCのフロントカメラを起動し、リアルタイムの映像をvideoタグに表示する
    this.video = this.$refs.video
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      navigator.mediaDevices.getUserMedia({audio: false, video: true})
      .then(stream => {
        this.video.srcObject = stream
        this.video.play()
      })
    }

    console.log(this.$refs.canvas)
    // console.log(this.$refs.canvas)

    this.canvas = this.$refs.canvas 
```


##感情分析する

以下のサイトで会員登録し、APIキーを取得します。

https://azure.microsoft.com/ja-jp/services/cognitive-services/face/

その後、canvasに静止画を表示し、画像データをbase64に
変換した上で(makeblobメソッドの処理を参照してください)、
AxiosでFace APIに画像データを送信します。

```javascript:HelloWorld.vue

this.testTimer = setInterval(() => {
      // console.log(this.$refs.canvas)
      let context = this.canvas.getContext("2d").drawImage(this.video, 0, 0, 400, 240)
　　　 //撮った画像をcaptures配列に格納する
      this.captures.push(this.canvas.toDataURL("image/png")) 
      let subscriptionKey = "取得したキーをここに入力する";
      let uriBase = "https://westcentralus.api.cognitive.microsoft.com/face/v1.0/detect";
      let params = {
        "returnFaceId": "true",
        "returnFaceLandmarks": "false",
        "returnFaceAttributes":
          "emotion"
      };
      //配列最後に追加された画像のフォーマットを変換し、imgURL変数に代入する
      const imgURL = this.makeblob(this.captures[this.captures.length - 1])
      //imgURLの画像をFaceAPIに送信
      Axios.post(
        uriBase + "?returnFaceId=true&returnFaceLandmarks=false&returnFaceAttributes=age,emotion",
        imgURL,
        {
          headers: {
            "Content-Type": "application/octet-stream",
            "Ocp-Apim-Subscription-Key": subscriptionKey,
          }
        },
      )
      .then(response => {
        console.log(response.data[0].faceAttributes.emotion)
        
      })
      .catch(error => {
        // console.log(error.response)
      });
    }, 5000);
```

Axiosで画像をPostするために、画像データをbase64に変換します。

```javascript:HelloWorld.vue

methods: {     
    makeblob: function (dataURL) {
      let BASE64_MARKER = ';base64,';
      if (dataURL.indexOf(BASE64_MARKER) == -1) {
        let parts = dataURL.split(',');
        let contentType = parts[0].split(':')[1];
        let raw = decodeURIComponent(parts[1]);
        return new Blob([raw], {type: contentType});
      }
      let parts = dataURL.split(BASE64_MARKER);
      let contentType = parts[0].split(':')[1];
      let raw = window.atob(parts[1]);
      let rawLength = raw.length;
      let uInt8Array = new Uint8Array(rawLength);
      for (let i = 0; i < rawLength; ++i) {
        uInt8Array[i] = raw.charCodeAt(i);
      }
      return new Blob([uInt8Array], {type: contentType})
    }
  }

```

axiosを利用するため、以下のコマンドでnpm installします。

```
npm install --save-dev vue-axios axios
```


##コード全体
今回コーディングしたコード全体は以下の通りです。

```vuejs:HelloWorld.vue


<template id="main">
  <div id="app">
    <canvas ref="canvas" id="emo_canvas" width="400" height="400"></canvas>
    <div>
      <video ref="video" id="video" width="400" height="400" playsinline muted autoplay></video>
    </div>
  </div>
</template>

<script>
import Vue from 'vue';
import Axios from 'axios';
import VueAxios from 'vue-axios'

Vue.use(VueAxios, Axios)

export default ({
  template: '#main',
  data() {
    return {
      video: {},
      canvas: {},
      captures: [],
      testTimer: '',
    };
  },
  mounted() {
  //カメラを起動し、映像をvideoタグ内に表示
    this.video = this.$refs.video
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      navigator.mediaDevices.getUserMedia({audio: false, video: true})
      .then(stream => {
        this.video.srcObject = stream
        this.video.play()
      })
    }

    console.log(this.$refs.canvas)
    // console.log(this.$refs.canvas)

    this.canvas = this.$refs.canvas 
    this.testTimer = setInterval(() => {
      // console.log(this.$refs.canvas)
      let context = this.canvas.getContext("2d").drawImage(this.video, 0, 0, 400, 240)
      this.captures.push(this.canvas.toDataURL("image/png")) //撮った画像をcaptures配列に格納する
      let subscriptionKey = "取得したキーをここに入力する";
      let uriBase = "https://westcentralus.api.cognitive.microsoft.com/face/v1.0/detect";
      let params = {
        "returnFaceId": "true",
        "returnFaceLandmarks": "false",
        "returnFaceAttributes":
          "emotion"
      };
      //配列最後に追加された画像のフォーマットを変換し、imgURL変数に代入する
      const imgURL = this.makeblob(this.captures[this.captures.length - 1])
      //imgURLの画像をFaceAPIに送信
      Axios.post(
        uriBase + "?returnFaceId=true&returnFaceLandmarks=false&returnFaceAttributes=age,emotion",
        imgURL,
        {
          headers: {
            "Content-Type": "application/octet-stream",
            "Ocp-Apim-Subscription-Key": subscriptionKey,
          }
        },
      )
      .then(response => {
        console.log(response.data[0].faceAttributes.emotion)
        
      })
      .catch(error => {
        // console.log(error.response)
      });
    }, 5000);
  },
  methods: {     
    makeblob: function (dataURL) {
      let BASE64_MARKER = ';base64,';
      if (dataURL.indexOf(BASE64_MARKER) == -1) {
        let parts = dataURL.split(',');
        let contentType = parts[0].split(':')[1];
        let raw = decodeURIComponent(parts[1]);
        return new Blob([raw], {type: contentType});
      }
      let parts = dataURL.split(BASE64_MARKER);
      let contentType = parts[0].split(':')[1];
      let raw = window.atob(parts[1]);
      let rawLength = raw.length;
      let uInt8Array = new Uint8Array(rawLength);
      for (let i = 0; i < rawLength; ++i) {
        uInt8Array[i] = raw.charCodeAt(i);
      }
      return new Blob([uInt8Array], {type: contentType})
    }
  }
});
```

</script>

##参考にさせて頂いたサイト
[FaceAPIを使った感情分析](https://qiita.com/kolife/items/0a76754077085f81f91a)
[Vue.js を vue-cli を使ってシンプルにはじめてみる](https://qiita.com/567000/items/dde495d6a8ad1c25fa43)
[Vue.jsでもjQueryと同じような ajax postを実装したい](http://www.kaasan.info/archives/4175)