Firebaseでfrontendの構築- カメラでEmotionAPI

GYAOのtsです。
我々のチームは、オールパブリッククラウドで、[Microservice Architecture](http://microservices.io/patterns/microservices.html)を採用した次期バックエンドを設計中です。

# 経緯

ずっとバックエンドのことばかり書いてきて、frontendのことを何も書いていなかったので、書いてみる。
バックエンドを構築していくチームだが、構築した複数のAPIを使用して、

ひらめき→実装→リリース→測定
のライフサイクルを短い時間で回していくのが目標なので、楽にfrontendを構築できるようにインフラ構築とナレッジを蓄積していく。
ちょうど、別プロジェクトでカメラ画像→emotionAPIのプロト作成があったのでちょうどいい
今回やることは

1. firebase hostingを使用
1. HTML5でカメラの起動と撮影
1. 定期的にカメラ映像のキャプチャを取ってemotionAPIにpost
1. 結果を画面に表示

# Firebase
今回は[Firebase](https://firebase.google.com/)を使用する。
storage・remote config・hosting、reporting等々、様々な機能が提供されている。
基本的なアプローチとしてはHTML5　+　jsのページをhostingして、js経由で様々なアセット（storage等）にアクセスする。

## Firebase CLI

- install

CLI等のtoolをインストールします。

```shell-session
sudo npm install -g firebase-tools
```

- login

まずはlogin

```shell-session
firebase login
```
- init

プロジェクトディレクトリを作成してinitします。

```shell-session
firebase init
```
今回は1ページのみで構成されたサイトを作ってみた。
<img width="364" alt="スクリーンショット 2016-12-05 19.39.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/114329/497de819-187b-677c-215f-5abb865b60b5.png">
あとはこれを修正してdeployするのみ

- deploy

```shell-session
firebase deploy
```

# カメラ起動

```js
	function startVideo() {
		navigator.mediaDevices.getUserMedia({video: true, audio: false})
 		// success
		.then(function (stream) {
			localStream = stream;
			localVideo.src = window.URL.createObjectURL(localStream);
			setInterval("capture()", 10000);
		})
		// error
		.catch(function (error) { 
			console.error("navigator.mediaDevices.getUserMedia() error:", error);
			return;
		});
	}
```


# capture
```js
	function capture() {
		capture_canvas = document.getElementById('capture_canvas');
		var ctx = capture_canvas.getContext('2d');
		var w = localVideo.offsetWidth;
		var h = localVideo.offsetHeight;
		capture_canvas.setAttribute("width", w);
		capture_canvas.setAttribute("height", h);
		ctx.drawImage(localVideo, 0, 0, w, h);
		sendImage();
	}
```

# emotionAPI
画像データを一旦バイナリに変換しなければならないのでこうなった。。。

```js
	makeblobdata = 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 });
    }

	var api_key = 'xxxxxxxxxxx'; 
	function sendImage() {
        var request =  $.ajax({
        url: "https://api.projectoxford.ai/emotion/v1.0/recognize",
        processData: false,
        beforeSend: function(xhrObj){
            // Request headers
            xhrObj.setRequestHeader("Content-Type","application/octet-stream");
            xhrObj.setRequestHeader("Ocp-Apim-Subscription-Key", api_key);
        },
        type: "POST",
        // Request body
        data: makeblobdata(capture_canvas.toDataURL())
    })
    .done(function(data) {
		var res = document.getElementById('res');
		res.innerHTML = JSON.stringify(data);
    })
    .fail(function() {
        alert("error");
    });
	}
```

# result
汚いが、プロトだからまあいいか。下記のように出力できる。

<img width="989" alt="スクリーンショット 2016-12-05 20.12.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/114329/52f7f1fe-110a-9e06-ed6d-a5a7b8b5d9e9.png">

上がカメラ撮影その下がキャプチャ
<img width="794" alt="スクリーンショット 2016-12-05 20.15.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/114329/2faa9006-e6ba-3c43-138b-b144da01b965.png">