Raspberry Pi のカメラモジュールとFACE++で顔識別する

先日RaspberryPiにOpecCVをインストールしてみました。（[Raspberry Pi のカメラモジュールとOpen CV3で顔認識する - Qiita](http://qiita.com/PonDad/items/8f255b0dbec25c591cd6)） 

OpenCVの特徴分類器を利用すれば顔識別も出来るようなのですが、少々難易度が高そうなので、今回は中国製API、[FACE++](http://www.faceplusplus.com/) を利用しました。

![face++.gif](https://qiita-image-store.s3.amazonaws.com/0/47128/681d59be-33cd-27e8-3d8d-679c33203f8d.gif)

## 環境

![IMG_8879.jpg](https://qiita-image-store.s3.amazonaws.com/0/47128/f98d1e56-ede5-d07f-a960-89d56a9b58c3.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インチ トーキングフィギュア

## FACE++

結構以前に話題になったWebサービスで、APIも公開されています。パラメーターはこちらで詳しく解説されています。

[Face++のAPIを使って、PHPで顔認証をする方法 - Syncer](https://syncer.jp/how-to-use-faceplusplus-api)

さらに認識した顔をグルーピングし、識別するAPIがありました。こちらで詳しく解説されています。

[無料API「face++」で顔から双子を判別 - FaMirror Project](http://famirror.hateblo.jp/entry/2015/12/17/180000)

OpenCVを利用してRasPiから顔画像の抽出が出来るようになったので、それを利用します。今回読み込む画像は各10枚づつにしました。

![Jules face 1 - Cloudup 2016-11-19 19-59-40.png](https://qiita-image-store.s3.amazonaws.com/0/47128/caf50517-7372-c7af-f4c7-a4c0edb8a954.png)

ちなみに画像をGETリクエストするとこんなレスポンスが得られます。

```json
{
    "face": [
        {
            "attribute": {
                "age": {
                    "range": 7, 
                    "value": 42
                }, 
                "gender": {
                    "confidence": 99.9747, 
                    "value": "Male"
                }, 
                "race": {
                    "confidence": 97.8304, 
                    "value": "Black"
                }, 
                "smiling": {
                    "value": 0.734695
                }
            }, 
            "face_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 
            //略
}
```

黒人男性で42歳前後、と識別されました。

![Vincent face 1 - Cloudup 2016-11-19 20-00-09.png](https://qiita-image-store.s3.amazonaws.com/0/47128/318f99a1-919c-5b24-f104-7f5adac6dd53.png)

```json
{
    "face": [
        {
            "attribute": {
                "age": {
                    "range": 7, 
                    "value": 34
                }, 
                "gender": {
                    "confidence": 96.3747, 
                    "value": "Male"
                }, 
                "race": {
                    "confidence": 99.90299999999999, 
                    "value": "White"
                }, 
                "smiling": {
                    "value": 9.09543
                }
            }, 
            "face_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 
            //略
}
```
白人男性で34歳前後と出ました。

これらのidを名前毎に分類し、さらにグループとして登録します。今回はサミュエル・L・ジャクソン、ジョン・トラボルタ、私の３名を登録しました。

登録したグループに撮影した写真をGETリクエストするとこのようなレスポンスが得られます。

![face.jpg](https://qiita-image-store.s3.amazonaws.com/0/47128/6b2696cb-d7c4-78ba-1784-82be5a35335a.jpeg)

```json
{
    "face": [
        {
            "candidate": [
                {
                    "confidence": 75.559387, 
                    "person_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 
                    "person_name": "samuel", 
                    "tag": ""
                }, 
                {
                    "confidence": 2.307938, 
                    "person_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 
                    "person_name": "travolta", 
                    "tag": ""
                }, 
                {
                    "confidence": 0.967236, 
                    "person_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 
                    "person_name": "pon_dad", 
                    "tag": ""
                }
            ], 
            "face_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 
            //略
    "session_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
```

`samuel`の確率７５.5％、`travolta`の確率2.3％、`pon_dad`の確率0.9％と出ました。まあ、間違いなく私では無いということが分かります。

## curl -X POST コマンドでリクエスト

RasPiとOpenCVで撮影・保存した画像をGETリクエストで送信するのにrequestモジュールを使おうと思ったのですが、上手くいかなかったのでここは`child_prosess`と`curl -X POST `コマンドを利用することにしました。

```js:app.js

//略 OpenCVで顔画像を切り出して保存
im2.save('./public/images/face_2.jpg')；

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

var facepp = exec('curl -X POST "https://apicn.faceplusplus.com/recognition/identify?api_key=<API KEY>e&api_secret=<API Secret>&group_name=pulp_fiction" -F "img=@' + filename + '"'）

```

## jsonデータの抽出

配列と連想配列が混在したjsonデータを抽出すると訳が分からなくなるのですが、こちらの記事（[【js】jsonデータの配列を取得して取り出すお話 - 仕事の事やプライベートな事を適当に書いていくブログ](http://wims-tea.hatenablog.jp/entry/2015/06/10/141614)）を参考になんとか欲しいデータを抽出することが出来ました。

こんな感じです。

```js:app.js

//略 OpenCVで顔画像を切り出して保存
im2.save('./public/images/face_2.jpg')；

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

var facepp = exec('curl -X POST "https://apicn.faceplusplus.com/recognition/identify?api_key=<API KEY>e&api_secret=<API Secret>&group_name=pulp_fiction" -F "img=@' + filename + '"',
        function (error, res) {
          console.log(res)
          var identify = JSON.parse(res)
          var a = identify.face
          var b = []
          var c = []

          for (var i = 0; i < a.length; i++) {
          	for (var j = 0; j < a[i]["candidate"].length; j++) {
          	  b.push(a[i]["candidate"][j].person_name);
              c.push(a[i]["candidate"][j].confidence);
          	};
          };
          if(c[0] > 60){
          console.log(b[0])
          console.log(c[0])
          io.sockets.emit('msg', b[0] + ' ' + c[0]);
          }
```

今回のグループは３人なので、レスポンスデータの３人を配列に入れて一番最初の人名と割合を抽出しました。

今回は確率が６０％以上で本人と判断し、本人に場合は`socket.io`の`emit`を利用してクライアントサイドにその内容を送信します。

![QT 2016-11-19 15-21-37.png](https://qiita-image-store.s3.amazonaws.com/0/47128/773b5f75-599d-5beb-20c8-66e0dc6d2a16.png)

![QT 2016-11-19 15-22-21.png](https://qiita-image-store.s3.amazonaws.com/0/47128/0e15935b-6a81-1b9f-8a12-852413e36d44.png)

##　まとめ

今回は対象がフィギュアということもあり、またRasPiのカメラを使って正面から撮影しているので、学習させる写真が10枚位でも判別がつきました。（本人認証を思いつきで60%以上と設定したのですが、根拠は特ありません。一般的には何％位で本人だと判断するのでしょうか…。）

スナップ写真やインターネット上の画像などを利用する際は、トリミングの加工・変形などもう少し工夫が必要だと思います。

ロボットに取り付けた固定式のカメラなどでは、結構使えるかもしれませんね。ではまた。