Actcast α の顔認識機能で取得した画像をS3に保存する

## はじめに

**2019年7月31日にActcast β版がリリースされ、α版の設定データが削除されました。最新版のActcast WriterでActcast エージェントのインストールとデバイスの再登録が必要になります。**

2018年12月、Idein株式会社から高度なセンシング技術を利用したIoTシステムを安価に構築・運用できるプラットフォームサービス[Actcastのα版がリリース](https://prtimes.jp/main/html/rd/p/000000005.000026271.html)されました。
Raspberry Piを使って手軽に画像認識が試せるので、 Face Tracking という Act 試してみました。([Actとは](https://actcast.io/docs/ja/GettingStarted/Introduction.html))

## 準備するもの
 - RaspberryPi 3 model B+
    - [アイ・オー・データ機器 UD-RP3BP Raspberry Pi メインボード（Bluetooth、Wi-Fi対応モデル） Raspberry Pi 3 Model B+](https://www.amazon.co.jp/dp/B07SDFCDYQ/)
   - ドキュメントによると、Raspberry Pi 1 Model B, 1 Model B+, 2 Model B, 3 Model B, 3 Model B+, Zero W, Zero WHが推奨されています。
 - 公式カメラモジュール
    - [Raspberry Pi Camera Module V2 カメラモジュール (Daylight - element14)](https://www.amazon.co.jp/dp/B01ER2SKFS/)
 - microSDカード (4GBから32GB)
    - Actcast エージェントのインストールには、4GBあれば足ります。
    - [サンディスク microSDHCメモリーカード 16GB Class10 UHS-I SDSDQUL-016G-J35A](https://www.amazon.co.jp/dp/B00JBSNRWK/)
 - HDMI接続のディスプレイ
    - もしくは、[Raspberry Pi 3 Model Bに Quimat 3.5インチタッチスクリーン HDMIモニタTFT LCDディスプレイ を接続](https://qiita.com/revsystem/items/e00ea1be9208dbb16aad)のようなRaspberry Pi用ディスプレイ

## 前提条件
 - [Getting Started with Actcast (α版)](https://actcast.io/docs/ja/GettingStarted/) に従い、 Actcast へのサインアップとデバイスの登録が完了していること。(GitHubのアカウントが必要)
 - AWSにアカウントがあり、LamdaがS3にアクセスするためのロールが設定されていること。
 - Node.jsのモジュールをAWS Lambdaにデプロイする環境が整っていること。

## 参考情報
 - [API Gateway の Lambda プロキシ統合をセットアップする](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html)
 - [[Node.js] Base64エンコードされたファイルデータをデコードして、S3にputObjectする](https://dev.classmethod.jp/server-side/node-js-server-side/node-js-base64-encoded-image-to-s3/)
 - [API Gateway の Lambda プロキシ統合でバイナリデータをレスポンスする(Python3.6)](https://qiita.com/inabajunmr/items/e49408ab356b6a63ea91)
 - [Lambda+API Gatewayで画像を返す時に気をつけるべきx個のこと](https://qiita.com/celeron1ghz/items/d7cac966dea04ce55c3c)
 - [API Gateway+Lambdaでバイナリデータを処理](https://qiita.com/akatsukaha/items/024fbabc9a321665cb23)
 - [文字列を変換する オンラインBase64でデコーダ](http://www.convertstring.com/ja/EncodeDecode/Base64Decode)
 - [AWS SDK for JavaScriptでS3でファイル操作](https://www.sevenspirals.net/2013/12/01/aws_javascript_sdk/)
 - [Node.jsドキュメントからbase64エンコーディングをおさらい](http://robin.hatenadiary.jp/entry/2017/01/08/225337)
 - [今年のうちに対応したい、Node.jsのBufferに潜む危険性](https://techblog.yahoo.co.jp/advent-calendar-2016/node_new_buffer/)
 - [AWSによるサーバーレスアーキテクチャ](https://amzn.to/2CYBlXx)

## 処理の流れ
![actcast001.png](https://qiita-image-store.s3.amazonaws.com/0/61809/216d71e3-e265-5546-c5ce-1f2af07b09d6.png)


Face Trackingで撮影された画像はBase64形式で出力されるため、バイナリ変換を行いS3に保存します。

## Actcastのセットアップ
### Actの作成
`New Act` ->`Demo` の順にクリック。
![actcast002.png](https://qiita-image-store.s3.amazonaws.com/0/61809/aef28f76-e4e4-3c8b-f761-cba5659ea8c2.png)
`Face Tracking`をクリック。
![actcast003.png](https://qiita-image-store.s3.amazonaws.com/0/61809/5e9f97b1-c9f6-23b8-211d-8ca7f7e6e6ec.png)
`Act Name`には、任意の名称を入力します。初期値は`Face Tracking`となっているので、とくに変更がなければこのままとします。
`display`にチェックを入れ、`Create New Act`をクリックしActを作成します。
![actcast004.png](https://qiita-image-store.s3.amazonaws.com/0/61809/e83ab80f-ee2a-3bf5-d77e-9c7dc0609301.png)
`Go to Device List`をクリックしてデバイス一覧に進みます。
![actcast005.png](https://qiita-image-store.s3.amazonaws.com/0/61809/560c99ac-9cd5-7744-4dcf-bd1f8c13ebdf.png)
デバイス一覧からActcastがインストールされたデバイス(Raspberry Pi)を選択し、`Actions`をクリック。ポップアップウィンドウ内の`Install "Face Tracking"`を選択すると、Face Trackingがインストールされます。
![actcast006.png](https://qiita-image-store.s3.amazonaws.com/0/61809/dcacd3b9-4148-108b-4180-0ff71c71beea.png)

### 動作確認
#### ディスプレイが接続されている場合
Face Trackingのセットアップを行った際にdisplayを有効にしていれば、ディスプレイに映像が映し出されているはずです。自分の顔にカメラを向けると、顔を認識し四角の枠が顔を追跡します。

#### ディスプレイが接続されていない場合
カメラモジュールで撮影した画像を設定画面上で確認する`Take Photo`機能で確認します。`Devices`をクリックしてデバイス一覧を表示し、デバイス名の右端にある`>`をクリック。
![actcast007.png](https://qiita-image-store.s3.amazonaws.com/0/61809/18c466ff-d3f8-b66d-19aa-de53dbbcd63d.png)
`Act Settings`にあるカメラアイコンをクリックし、`Take Photo`をクリックすることで顔認識に成功した際の画像が表示されます。
撮影した画像は`Show Photo`で参照することができます。
![actcast008.png](https://qiita-image-store.s3.amazonaws.com/0/61809/92f68b24-5094-7ace-5cc5-d17ae5e150a1.png)


## AWS環境のセットアップ
### IAMユーザのアクセス権限のセットアップ
AWSにLambda関数をデプロイするために、IAMユーザにアクセス権限を割り当てます。
AWSコンソールでIAMのページを開きIAMユーザを選択します。`アクセス権限`タブの`インラインポリシーの追加`をクリック。
![actcast009.png](https://qiita-image-store.s3.amazonaws.com/0/61809/900be4aa-c0c0-e315-a4ca-456304e30023.png)

`ビジュアルエディタ`タブを選択し、
* サービス : `Lambda`を選択します。
* アクション :
    * 読み込みグループ : `GetFunction`
    * 書き込みグループ : `UpdateFunctionCode` ,`UpdateFunctionConfiguration`
* リソース : `指定`をチェック
* `任意のリソースタイプ = function`の右にある`すべて`をチェック。

![actcast010.png](https://qiita-image-store.s3.amazonaws.com/0/61809/b6e26dd2-0a1d-e6f6-15c7-578fe08356e2.png)
`ポリシーの確認`をクリックし、名前に任意の名称をつけます。ここでは、Lambda-Update-Policyとしています。

### Lambda関数のIAMロールのセットアップ
Lambda関数にAWS S3とAWS CloudWatchの読み込み/書き込みの権限を付与します。
AWSコンソールでIAMの画面を開き、`ロール`画面を開きます。
`信頼されたエンティティの種類を選択`で、`AWSサービス`を選択し続いて`Lambda`を選択し`次のステップ: アクセス権限`をクリック。

![actcast011.png](https://qiita-image-store.s3.amazonaws.com/0/61809/899c1d58-8c8a-88da-fab5-3227929e8d06.png)

`AWSLambdaExecute`を選択し、`次のステップ:タグ`をクリック。
![actcast012.png](https://qiita-image-store.s3.amazonaws.com/0/61809/5606ad98-eaf7-acdd-bfce-8d05a611b991.png)

タグは任意に設定し`次のステップ:確認`をクリック。

* ロール名 : 任意の名称をつけます。 ここでは、`lambda-s3-execution-role`としています。

`ロールの作成`をクリックします。

### S3のバケット作成
Actcastで取得した画像を保存するS3バケットを作成します。
AWSコンソールからS3のページを開き`バケットを作成する`をクリック。
バケット名を入力し、リージョンを選択します。

* バケット名 : 任意の名称をつけます。
* リージョン : `us-east-1`

以降、Lambdaもus-east-1を使用します。

![actcast013.png](https://qiita-image-store.s3.amazonaws.com/0/61809/80e9843a-8aa2-8a2d-2d76-a61e8f489f01.png)

### Lambdaのセットアップ
Face Trackingで取得したBase64形式のデータをバイナリデータに変換するLambda関数を作成します。
AWSコンソールからLambdaのページを開き、`関数の作成`をクリック。
`一から作成`をクリックします。

* 名前 : 任意の名前を入力します。ここでは、actcast-decode-base64とします。
* ランタイム : `Node.js 8.10`
* ロール : `既存のロールを選択`
* 既存のロール : `lambda-s3-execution-role`
![actcast014.png](https://qiita-image-store.s3.amazonaws.com/0/61809/034dff07-0ca3-5d13-81f2-e55f206f14cb.png)

### Lambda関数のデプロイ

下記構成で`npm run deploy`を実行し、デプロイします。

```
.
├── index.js
└── package.json
```



```javascript:index.js

'use strict';

const aws = require('aws-sdk');
const s3 = new aws.S3();
const https = require('https');
const fs = require('fs');
const querystring = require('querystring');


exports.handler = function (event, context, callback) {

    // add logging level
    console.log = console.log.bind(null, '[LOG]');
    console.info = console.info.bind(null, '[INFO]');
    console.warn = console.warn.bind(null, '[WARN]');
    console.error = console.error.bind(null, '[ERROR]');

    // variables
    const response = {}

    // check event
    console.log(event);

    // get JSON body from actcast
    const body = JSON.parse(event.body);
    const fase = body.face;
    const timestamp = body.timestamp;

    // decode
    const decode = new Buffer.from(fase, 'base64');

    // save data in s3
    const params = {
        // 作成したバケット名
        Bucket: 'hogehoge',
        // S3にアップロードされるファイル名のサフィックス
        Key: timestamp + '-face.png',
        ContentType: 'image/png',
        Body: decode
    };

    s3.putObject(params, function (err, data) {
        if (err) {
            console.error(err);
            console.error('image upload failed');

            // create response
            response.statusCode = 400
            response.body = JSON.stringify({
                message: 'fail.'
            })
            callback(null, response);

        } else {
            console.log('image upload success');

            // create response
            response.statusCode = 200
            response.body = JSON.stringify({
                message: 'image has been uploaded to S3'
            })

            callback(null, response);
        }
    });
};
```

```javascript:package.json
{
  "name": "actcast-decode-base64",
  "version": "1.0.0",
  "description": "decode base64",
  "main": "index.js",
  "scripts": {
  # Lambda関数のarnを指定。 profileはaws credentialsで設定した値。
    "deploy": "aws lambda update-function-code --function-name arn:aws:lambda:us-east-1:xxxxxxxxxxxx:function:actcast-decode-base64 --zip-file fileb://Lambda-Deployment.zip --profile lambda",
    "predeploy": "zip -r Lambda-Deployment.zip * -x *.zip *.json *.log"
  },
  "dependencies": {
    "aws-sdk": "latest"
  },
  "devDependencies": {
    "run-local-lambda": "^1.1.1"
  }
}
```

### API Gatewayのセットアップ
#### APIを作成
API Gatewayを作成します。AWSコンソールからAPI Gatewayを開き`APIの作成`をクリック。
![actcast015.png](https://qiita-image-store.s3.amazonaws.com/0/61809/b3a1ce8c-9bbb-9e89-c0ef-b4d4b4d5d1b2.png)
`API名`はわかりやすいものを適当に入力し、`APIの作成`をクリック。ここでは、actcastとしました。
![actcast016.png](https://qiita-image-store.s3.amazonaws.com/0/61809/14936a47-d04e-e177-2d5d-c61d40fa11de.png)

#### リソースを作成
`アクション`メニューから`リソースの作成`をクリック。
![actcast017-0.png](https://qiita-image-store.s3.amazonaws.com/0/61809/ca7ed229-2952-b8b1-2d52-ec76ce96d321.png)


`リソース名`を入力すると`リソースパス`が自動的に表示されます。このパスがAPIのURIとなります。ここでは、FaceTrackingとしました。
入力したら`リソースの作成`をクリック。
![actcast017.png](https://qiita-image-store.s3.amazonaws.com/0/61809/d73b4251-0a88-9cb3-27cd-1afb8e107aea.png)


#### メソッドの作成
作成したリソースを選択し、`アクション`メニューから`メソッドの作成`をクリック。
続けて、メソッド選択のメニューから`POST`をクリック。
![actcast018.png](https://qiita-image-store.s3.amazonaws.com/0/61809/8d1ea57e-4213-ad9c-d635-1d6cde22e5b5.png)
POSTメソッドを作成するとAPI Gatewayと紐付けるLambda関数を選択する画面となります。選択、入力して`保存`をクリック。
`結合タイプ`: `Lambda関数`
`Lambdaプロキシ統合の使用`: チェック
`Lambdaリージョン` : Lambda関数を作成したリージョン。ここでは、`us-east-1`
`Lambda関数` : 作成したLambda関数。ここでは、`actcast-decode-base64`

![actcast019.png](https://qiita-image-store.s3.amazonaws.com/0/61809/f401f73b-1f66-32bf-1309-c478ad294f1d.png)

権限付与のダイアログが表示されるので、`OK`をクリック。
![actcast020.png](https://qiita-image-store.s3.amazonaws.com/0/61809/0edb7f7a-0711-2959-e391-23284dc7ae3e.png)

### APIのデプロイ
作成したリソースを選択し、`アクション`メニューから`APIのデプロイ`をクリック。
![actcast021.png](https://qiita-image-store.s3.amazonaws.com/0/61809/39ec771a-2bc3-4c36-914d-b4758414ebb5.png)

ここでは新規にステージを作成し、そこにデプロイします。
`ステージ名`に任意のステージ名を入力し`デプロイ`をクリック。ここで入力したステージ名はAPIのURIに含まれます。
![actcast022.png](https://qiita-image-store.s3.amazonaws.com/0/61809/1937ebae-d0dc-65c6-5014-d26e3516272c.png)

デプロイが完了すると、APIのエンドポイントURLが作成されます。
![actcast023.png](https://qiita-image-store.s3.amazonaws.com/0/61809/a140761a-b9ca-2695-e467-ee7c06899204.png)

## Actcastのセットアップ2
### Castの作成
FaceTrackingの`Casts`から`Make New Cast`をクリック。
![actcast024.png](https://qiita-image-store.s3.amazonaws.com/0/61809/163a12c3-bf3a-ed53-8e10-2dec8b93ed26.png)

`Cast Name`には任意の名称を入力します。`Target Devices`は、このCastを動作させるデバイスを選択します。
入力・選択後、`Configure Action`をクリック。
![actcast025.png](https://qiita-image-store.s3.amazonaws.com/0/61809/d78f20b4-a560-4326-cf52-f5395bd61d2b.png)

`Select Service`の`Webhook`をクリック。
![actcast026.png](https://qiita-image-store.s3.amazonaws.com/0/61809/624d45d8-ad39-59a7-74e3-71cad851d7a8.png)

最後に、Actcastから接続するエンドポイントURLや送信するjsonの内容を設定します。
`URL` : API Gatewayで作成したエンドポイントURL。
`Method` : `POST`
`Headers`: `Content-Type` `application/json`
`Body` :
`{
 "face": "{{ data.face }}",
 "timestamp":{{ data.timestamp }}
}`

{{ data.face }}はカメラで撮影した画像をBase64エンコードした値、{{ data.timestamp }} は撮影日時(unixtime)です。
![actcast027.png](https://qiita-image-store.s3.amazonaws.com/0/61809/76f063e5-1e7e-57ff-7a13-b5e8863192e4.png)

## 顔認識
Raspberry Piを起動すると、画面にカメラモジュールで撮影された画像が映し出されます。
FaceTrackingが顔を認識すると、四角の枠で追跡されます。
顔を認識すると、枠内の画像がS3上の指定したバケットに保存されます。


### サンプル画像
(顔画像なので、モザイク処理しました。)
カメラが傾いていたので、左に90度傾いた画像になっています。
顔を認識してから撮影されるまでタイムラグがあるため、撮影された画像には背景しか写っていないということも割とありました。
人間の顔のほかにも猫の顔も認識したので、ペットの観察にも使えるかも知れません。

![1550938446_374289-face.png](https://qiita-image-store.s3.amazonaws.com/0/61809/58af28a4-0c3e-e8ba-92ae-66bc5c3e516e.png)