Raspberry Pi のカメラモジュールとindicoで感情認識する

先日RasPiで顔識別をしてみましたが（[Raspberry Pi のカメラモジュールとFACE++で顔識別する - Qiita](http://qiita.com/PonDad/items/4d4352fa16c92a16ac3d)）引き続き、写真の感情認識を試してみます。

機械学習サービス[indico](https://indico.io/)のAPIを利用します。

![indico.gif](https://qiita-image-store.s3.amazonaws.com/0/47128/1df68ea3-6d69-98a2-96b2-d55510e410a1.gif)

## 環境

![IMG_8880.jpg](https://qiita-image-store.s3.amazonaws.com/0/47128/e8bea88a-5a40-e55e-a227-a5d0604194bf.jpeg)

- Raspberry Pi3 (RASPBIAN JESSIE WITH PIXEL 4.4　/ node.js v6.8.1)
- Raspberry Pi カメラモジュール Raspberry Pi Camera Board
- Raspberry Pi カメラモジュール専用　Blackケース
- パルプ・フィクション/ サミュエル・L・ジャクソン ジュールス・ウィンフィールド 13インチ トーキングフィギュア
- パルプ・フィクション/ ジョン・トラボルタ ビンセント・ベガ 13インチ トーキングフィギュア

## indico

アメリカ製の機械学習サービスです。こちらに詳しく解説されています。

[3行のソースコードを入れるだけで機械学習できると噂のindicoをNode.jsで使って機械学習入門してみる - Qiita](http://qiita.com/n0bisuke/items/868b43411078bc2018fb)

10,000リクエスト/月まで無料で利用することが出来ます。

今回は「Facial Emotion Recognition（顔感情認識）」のメソッドを利用します。`fs`から画像ファイルを読み込むサンプルはこの様にあります。

```js:sample.js
var indico = require('indico.io');
var fs = require('fs');

indico.apiKey = "YOUR_API_KEY";

var filename = "PATH_TO_YOUR_IMAGE";

fs.readFile(filename, function(err, data) {
  base64data = new Buffer(data).toString('base64');
  indico.fer(base64data)
  .then(function(res) {
    console.log(res);
  }).catch(function(err) {
    console.warn(err);
  });
});
```

レスポンスのjsonはこの様に返ります。

```js
{
    'happy': 0.00004324968926091062,
    'sad': 0.007702528578033991,
    'angry': 0.0002575132225946431,
    'fear': 0.2071775132225946431,
    'surprise': 0.008160047807935744,
    'neutral': 0.00015069427192724994
}
```

## jsonデータの抽出

![QT 2016-11-20 11-18-54.png](https://qiita-image-store.s3.amazonaws.com/0/47128/f57e47e1-e9c7-0b25-8983-714266250c77.png)

![QT 2016-11-20 11-19-29.png](https://qiita-image-store.s3.amazonaws.com/0/47128/e4b768d4-34ab-0464-7abc-a80c1cd84052.png)

感情認識で一番数値の高いものをクライアントサイドに送信させてみます。

```js:app.js
var indico = require('indico.io');

var response = function(res) {
            console.log(res);
            var emotion =[res.Angry, res.Sad, res.Neutral, res.Surprise, res.Fear, res.Happy];
            var max_emotion = Math.max.apply(null, emotion);
            if(max_emotion === res.Angry){
              console.log('angry : '+ res.Angry);
              io.sockets.emit('msg2', 'angry : '+ res.Angry);
            }else if(max_emotion === res.Sad){
              console.log('sad : '+ res.Sad);
              io.sockets.emit('msg2', 'sad : '+ res.Sad);
            }else if(max_emotion === res.Neutral){
              console.log('neutral : '+ res.Neutral);
              io.sockets.emit('msg2', 'neutral : '+ res.Neutral);
            }else if(max_emotion === res.Surprise){
              console.log('surprise : '+ res.Surprise);
              io.sockets.emit('msg2', 'surprise : '+ res.Surprise);
            }else if(max_emotion === res.Fear){
              console.log('fear : ' + res.Fear);
              io.sockets.emit('msg2', 'fear : '+ res.Fear);
            }else if(max_emotion === res.Happy){
              console.log('happy : '+ res.Happy);
              io.sockets.emit('msg2', 'happy : '+ res.Happy);
            }
          }
          var logError = function(err) { console.log(err); }

          var filename = "./public/images/face_2.jpg";

          fs.readFile(filename, function(err, data) {
            base64data = new Buffer(data).toString('base64');
            indico.fer(base64data)
            .then(response)
            .catch(logError);
          });
```
連想配列のバリューが一番大きいものを、キーとバリューセットで抽出したい。

というだけなんですけど、forループで上手くデータの抽出が出来ず、この様に冗長になってしましました。（いい加減であまり参考にならず申し訳ありません。）

一度バリューを配列に入れて、`Math.max.apply()`メソッドで最大値を取り出し、キー毎に条件分岐した後`io.sockets.emit()`でクライアントサイドにバリューを送信します。

## 感情認識する

フィギュアでは表情に変化がないので、タブレットに表示したネット上の画像を撮影してみました。（添付画像はモザイクをかけています）

![Angry.png](https://qiita-image-store.s3.amazonaws.com/0/47128/f67cd779-6ad9-29f6-79f9-eb9b5fc31bac.png)

映画「パルプ・フィクション」の中で、サミュエル・L・ジャクソンが聖書の一節を唱えながら裏切り者を処刑するシーンの表情です。

「怒り」を読み取りました。

![surprize.png](https://qiita-image-store.s3.amazonaws.com/0/47128/793889e0-997a-06e2-be09-24737bf51aac.png)

同じ場面で、怒りマックスのシーンなのですが、こちらは「驚き」と判断されました。

口を大きく開けると「驚き」と判断されるケースが多い様です。

![happy.png](https://qiita-image-store.s3.amazonaws.com/0/47128/e5bc2fa8-6b3b-d57b-2124-d8b39c3513dc.png)

こちらは、サミュエル・L・ジャクソンが偶然出くわした強盗犯に銃を突きつける場面です。冷徹な目つきで相手を見据える場面。どちらかというとニュートラルに近い表情に見えます。

これは「喜び」と判断されました。サミュエル・L・ジャクソン演じるジュールスのふてぶてしさが際立つ場面なので、あながち間違いではないかもしれませんが。

![neytral.png](https://qiita-image-store.s3.amazonaws.com/0/47128/a70810bc-9cb3-7a76-1215-553cecc369cd.png)

こちらはジョン・トラボルタが反撃に出た裏切り者を処刑するシーン。こちらも冷酷な殺し屋らしく無表情です。

「ニュートラル」と判別されました。

## まとめ

今回のサンプル画像は映画のワンシーンからとったこともあり、必ずしも画面から受ける様な感情を認識しているとは限りませんでした。

映画を見せて感情認識させようと思ったら、機械学習などを使って物語の前後関係から推測させないとダメなのかな。なんてことも思います。

ともあれ、感情認識による条件分岐が出来たので、「笑顔に反応するロボット」みたいなものは簡単に作れそうな気がします。ではまた。