JavaScriptで顔認識 - サンタさんを捉える！

この記事は [J2complexed Advent Calendar 2016](http://qiita.com/advent-calendar/2016/j2complexed) の24日目です。

もうすぐサンタさんがやってきます。もう一度言いますが、サンタさんが来ます。
でも、せっかくサンタさんが来てくれたのに、寝ていたら申し訳がないです。
健康的なエンジニアにとって夜更かしはできませんので、サンタさんが来たらメッセージを表示したいと思います。

顔認識のライブラリであるtracking.jsを使えば、簡単に作れちゃいます。
[tracking.js](https://trackingjs.com/)

# 作り方
顔認識というと少し機械学習的な気がしますが、すでに顔認識のデータが準備されているので、何もすることはありません。

## ライブラリの準備
まずは公式のサイトにあるDownloadボタンで一式を取得してください。
その中の `build/tracking-min.js` が本体です。
普通にscriptタグで読み込みます。
あと顔認識するために `build/data/face-min.js` も読み込んでください。
これは機械学習で言うところの学習データです。他にも `eye` `mouth` がありますが、今回はサンタを認識できればいいので使いませんでした。


## Scriptを書く
ほとんどサンプルを利用してます。なので、簡単に説明だけ。

```js
var tracker = new tracking.ObjectTracker('face')
```
これで顔認識をするオブジェクトができます。

```js
tracker.setInitialScale(4)
tracker.setStepSize(2)
tracker.setEdgesDensity(0.1)
```
今回は設定はサンプルのままで使いましたが、特にいじらなくても問題ありませんでした。

`setInitialScale` 特徴量の初期サイズを指定するそうなのですが、変更してもとくに変化を感じませんでした。なんなんでしょうか？

`setStepSize` 物体検出を行う頻度でしょうか？変更をすると、検出をチラチラと行っているように感じました。

`setEdgesDensity` はエッジの密度を設定するそうです。あまり物体認識について詳しくないのですが、画像の明るさの差が大きいところを物体の境界としていて、それをエッジと言って、これは密になっていると認識する閾値みたいです。

```js
tracking.track('#video', tracker, { camera: true });
```
トラッキングする対象と、顔認識をするオブジェクトを渡しておきます。

```js
tracker.on('track', function(event) {
  if (event.data.length === 0) {
    // 何も検知できない
  } else {
    // 検知した
  }
}
```
オブジェクトが検知した際にイベントを待機させておきます。

## 困ったこと
わかっていたことですが、やはり精度があまりよくありませんでした。テスト段階で謎に背後の壁に対して顔を検出したりと怖かったので、ある程度連続して検出したときのみ、検出したと認識することにしました。

# 完成
以下がそのコードです。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>santa - tracking</title>
  <script src="js/tracking-min.js"></script>
  <script src="data/face-min.js"></script>

  <style>
  .xmas {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    z-index: -1;
    transition-duration: 0.8s;
    opacity: 0;
  }
  .merry .xmas {
    opacity: 1;
  }
  #tracking_level {
    background: red;
    height: 5px;
    position: fixed;
    top: 0;
    width: 0;
    left: 0;
    z-index: 10;
  }
  video, canvas {
    margin-left: 230px;
    margin-top: 120px;
    position: absolute;
    bottom: 50px;
    right: 50px;
  }
  </style>
</head>
<body>
  <div id="tracking_level"></div>
  <div class="xmas"><img src="assets/xmas.svg" alt="santa"></div>
  <video id="video" width="320" height="240" preload autoplay loop muted></video>
  <canvas id="canvas" width="320" height="240"></canvas>
  <script>
    window.onload = function() {
      var video = document.getElementById('video');
      var canvas = document.getElementById('canvas');
      var context = canvas.getContext('2d');
      var tracker = new tracking.ObjectTracker('face');
      var trackingLevel = document.getElementById('tracking_level');
      var trackCounter = 0.0;
      tracker.setInitialScale(4);
      tracker.setStepSize(2);
![santa.gif](https://qiita-image-store.s3.amazonaws.com/0/109382/250c7263-da6d-5a29-a9ee-991ff3f7b166.gif)
      tracker.setEdgesDensity(0.1);
      tracking.track('#video', tracker, { camera: true });
      tracker.on('track', function(event) {

        if (event.data.length === 0) {
          trackCounter = Math.max(0, trackCounter - 0.25)
        } else {
          trackCounter = Math.min(100, trackCounter + 1)
        }
        trackingLevel.style.width = trackCounter + '%'

        document.body.classList = trackCounter > 20 ? 'merry' : ''

        context.clearRect(0, 0, canvas.width, canvas.height);
        event.data.forEach(function(rect) {
          context.strokeStyle = '#ffffff';
          context.strokeRect(rect.x, rect.y, rect.width, rect.height);
        });
      });
    };
  </script>
</body>
</html>
```

で、以下が動作したデモです。

![santa.gif](https://qiita-image-store.s3.amazonaws.com/0/109382/5fa481d8-f973-16ca-b7b6-c827d27bc3e6.gif)

これでサンタさんがいつ来ても大丈夫ですね！それでは、おやすみなさい。