Amazon Web Services ブログ
S+Camera と Amazon Rekognition を組み合わせて来客分析を行う
本記事では、IoT@Loft #9 で ソラコム様にご紹介いただいた、S+Camera Basic (以下 S+Camera) と Amazon Rekognition や Amazon Elasticsearch Service などを組み合わせたデモを実現する方法を説明します。このデモでは、店舗の入り口に設置した S+Camera で顔を検出、検出した顔を Amazon S3 にアップロード、Amazon Rekognition を用いて分析することで、顧客の年齢や性別などの分析や、お得意様来店時の通知などを行うことができます。
Amazon Rekognition では、機械学習の専門知識を必要とせずに、実績のある高度にスケーラブルな深層学習テクノロジーを使用して、アプリケーションに画像およびビデオ分析を簡単に追加できるようになります。Amazon Rekognition を使用すると、画像と動画の物体、人物、テキスト、シーン、活動を特定し、不適切なコンテンツを検出できます。また、Amazon Rekognition カスタムラベルを使用することで、ビジネスニーズに合わせた画像の物体やシーンを特定できます。
S+Camera は SIM を搭載しているため、店舗外やイベント会場などのWi-Fiが整っていない環境でもAWSと連携し、来客数の分析などのPoCをスピーディに実施することができます。また、常時画像や動画をアップロードするかわりに顔検出のアルゴリズムをカメラ側で実行することで、必要な場合に限ってクラウド側へデータをアップロードし通信量の節約が可能です。そして、検出したい顔のコレクションをクラウド側に持たせてクラウドで個人を認識することで、カメラ側のアルゴリズムのアップデートを行わなくとも、認識したい人の設定を柔軟に変更することができます。
本記事で構築するデモのアーキテクチャは下図の通りです。ソラコム様の記事では、S+Cameraでの顔検出のアルゴリズム実施やAWSとの連携方法をご紹介されています(下図水色の点線)。本記事では S+Camera から Amazon S3 にアップロードされた画像に対して分析や通知機能を実装する方法を扱います(下図オレンジ色の点線)。
なお、本記事で扱う内容に関して、顔の画像データのプライバシーやセキュリティなどに十分に注意して実装いただく必要があります。Amazon Rekognition の FAQ のデータプライバシーや、Amazon S3 の FAQ のセキュリティについてもあわせてご参照ください。
それでは早速、デモの構築方法を紹介いたします。
事前準備
- AWS CLI および pip3 がインストール済みの PC
Amazon Rekognition のコレクション作成
来店時に通知を行いたいお得意様を登録するためのコレクションを Amazon Rekognition に作成します。あらかじめ、コレクションに登録したいお得意様の顔写真を 名前.jpg (例: tanaka.jpg) のようなファイル名で準備しておきます。ファイル名の 名前 の部分を、お得意様(VIP)検知時の通知内容として利用します。
まずは、コレクション用の S3 バケットを作成し、画像をアップロードします。
- Amazon S3 のコンソール を開きます
- 左側のメニューの バケットを選び、右側のバケットを作成ボタンをクリックします
- 任意の名前でバケットを作成します (例: vip-bucket-20200701)
- バケットの一覧から、作成したバケットをクリックします
- 先ほど保存した画像ファイルをすべて選択して、この画面にドラッグ&ドロップし、 アップロードをクリックします
続いて、 Amazon Rekognition で顔認識を行うための、コレクションを作成します。
AWS CLI をインストールした PC のターミナル から以下のコマンドを実行します。
export COLLECTION_ID=vip-collection
aws rekognition create-collection --collection-id $COLLECTION_ID以下のような出力が返ってくれば、vip-collection という名前のコレクションが作成されています。
{
    "StatusCode": 200,
    "CollectionArn": "aws:rekognition:your-region:012345678901:collection/vip-collection",
    "FaceModelVersion": "4.0"
}続いて、このコレクションにお得意様の顔を登録していきます。先ほど作成した S3 バケット名を環境変数に登録します。vip-collection-20200701の部分は自身が作成されたものに書き換えてください。
export BUCKET_NAME="vip-bucket-20200701"以下のコマンドを実行して、先ほど S3 バケットにアップロードした全画像をコレクションに登録します。
for key in $(aws s3 ls s3://$BUCKET_NAME/ | awk '{print $4}'); do
  name=$(echo $key | sed 's/\.[^\.]*$//')
  echo "index: $key"
  aws rekognition index-faces --collection-id $COLLECTION_ID \
  --image "S3Object={Bucket=$BUCKET_NAME,Name=$key}" \
  --external-image-id $name \
  --max-faces=1
doneコレクションに登録された顔の数を確認します。
aws rekognition describe-collection --collection-id $COLLECTION_ID以下のような結果が出力されるので、 FaceCount の値を確認します。以下の場合は2つの顔がコレクションに登録されていることが分かります。
{
    "FaceCount": 2,
    "FaceModelVersion": "4.0",
    "CollectionARN": "arn:aws:rekognition:your-region:012345678901:collection/vip-collection",
    "CreationTimestamp": 1592111200.081
}以上でコレクションの作成は完了です。
お得意様来店通知用の Amazon SNS の設定
お得意様の来店を検知した際にEメールで通知が飛ぶように、Amazon SNS の設定を行います
- Amazon SNS のコンソール を開きます
- 左側メニューの トピックをクリックし、右側のトピックの作成をクリックします
- 名前に vip-notification-topicを入力し、トピックの作成をクリックします
- 作成されたトピックの画面が表示されるので、詳細のARNの値をひかえておきます(後のAmazon Lambdaの手順で利用します)
- 画面の中央付近の、サブスクリプションの作成をクリックします
- プロトコル: Eメールを選択し、エンドポイントに通知先のメールアドレスを入力、サブスクリプションの作成をクリックします
- 確認用のメールが届くので、“Confirm subscription” のリンクをクリックしてメールアドレスの確認を行います
この手順では通知先としてEメールを利用しましたが、Amazon SNSを利用すると、Eメールの他にSMSやプッシュ通知、HTTPSによるウェブフック経由での通知などを行うことも可能です。
顔認識用の AWS Lambda の作成
続いて、カメラから Amazon S3 に画像がアップロードされた際に Amazon Rekognition で顔認識を実行し、認識結果を Amazon Elasticsearch Service に送信したり、お得意様来店検知時に Amazon SNS へ通知したりする AWS Lambda の関数を作成します。
まず自分の PC のお好きな場所にフォルダを作成し、以下のコードを lambda_function.py として保存します。
import json
import logging
import os
import uuid
import boto3
import requests
from requests_aws4auth import AWS4Auth
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
reko = boto3.client("rekognition")
sns = boto3.client("sns")
# Get settings from environmental variables
es_url = os.getenv("ES_URL")
region = os.getenv("REGION")
topic_arn = os.getenv("TOPIC_ARN")
# Get credentials
credentials = boto3.Session().get_credentials()
aws_auth = AWS4Auth(credentials.access_key, credentials.secret_key, region, 'es', session_token=credentials.token)
def process_record(record):
    """ Submit face recognition result to Elasticsearch service and SNS """
    bucket = record["s3"]["bucket"]["name"]
    key = record["s3"]["object"]["key"]
    timestamp = record["eventTime"]
    logger.info(f"bucket: {bucket}, key: {key}")
    res = reko.detect_faces(
        Image={
            'S3Object': {
                'Bucket': bucket,
                'Name': key,
            }
        },
        Attributes=["ALL"]
    )
    
    # Post detected faces to Elasticsearch
    for face in res["FaceDetails"]:
        data = {
            "timestamp": timestamp,
            "AgeLow": face["AgeRange"]["Low"],
            "AgeHigh": face["AgeRange"]["High"],
            "Smile": face["Smile"]["Confidence"],
            "Gender": face["Gender"]["Value"],            "Emotions": {d["Type"]: d["Confidence"] for d in face["Emotions"]}
        }
        response = requests.post(f"{es_url}/record/face/{uuid.uuid4()}",
                                 auth=aws_auth,
                                 headers={"Content-Type": "application/json"},
                                 data=json.dumps(data))
        logger.info(f"result: code={response.status_code}, response={response.text}")
    res = reko.search_faces_by_image(
        CollectionId='vip-collection',
        Image={
            'S3Object': {
                'Bucket': bucket,
                'Name': key,
            }
        },
        MaxFaces=10,
        FaceMatchThreshold=95,
        QualityFilter='AUTO'
    )
    # Send a message if VIP arrived
    if res["FaceMatches"]:
        msg = "VIP arrived: "
        msg += ", ".join(face["Face"]["ExternalImageId"] for face in res["FaceMatches"])
        sns.publish(TopicArn=topic_arn, Message=msg, Subject="VIP notification")
        logger.info(f"Notify {msg}")
def lambda_handler(event, context):
    for record in event['Records']:
        process_record(record)
    return {"result": "ok"}次に PC のターミナルを開き、lambda_function.py を作成したフォルダに移動し、以下のコマンドを実行し、lambda.zip ファイルを作成します。
cd 作成したフォルダ
pip3 install requests-aws4auth -t .
zip lambda.zip -r ./続いて、ブラウザから AWS Lambda の作成を行います。
- AWS Lambda のコンソール を開きます
- 左側のメニューの 関数を選択し、右側の関数の作成をクリックします
- 以下の情報を入力して、 関数の作成をクリックします- 一から作成
- 関数名: rekognition-lambda
- ランタイム: Python 3.8
- 実行ロール: AWSポリシーテンプレートから新しいロールを作成- ロール名: rekognition-lambda-role
- ポリシーテンプレート: 以下のポリシーを選択 
              - Amazon S3 オブジェクトの読み取り専用アクセス権限
- Elasticsearch のアクセス権限
- Amazon SNS 発行ポリシー
 
 
- ロール名: 
 
- 作成された Lambda 関数のコンソールの 関数コードで以下の操作をします- 右側の アクションから.zip ファイルをアップロードを選択
- 先ほど作成した lambda.zipを選択
- 画面右上の 保存をクリック
 
- 右側の 
来客分析用の Amazon Elasticsearch Service のセットアップ
本記事では来客分析のダッシュボードとして Amazon Elasticsearch Service の Kibana を利用します。なお、以下の手順では Amazon Elasticsearch Service のアクセス元のIPアドレスによって接続元を制限することで Elasticsearch のドメインを保護しておりますが、接続元のIPアドレスが変わりうるようなユースケースでは Amazon Cognito によるユーザ認証などの方法をご検討ください。
- Amazon Elasticsearch Service のコンソール を開きます
- 左側のメニューの ダッシュボードを選択し、新しいドメインの作成をクリックします
- 以下の設定を行なっていきます 
          - Step 1: デプロイタイプの選択 
            - デプロイタイプの選択: 開発およびテスト
- Elasticsearch のバージョン: 7.4
- 次へをクリック
 
- デプロイタイプの選択: 
- Step 2: ドメインの設定 
            - Elasticsearch ドメイン名: rekognition-domain
- データノード 
              - インスタンスタイプ: t2.small.elasticseasrch
 
- インスタンスタイプ: 
- 上記以外はデフォルト設定のまま 次へをクリック
 
- Elasticsearch ドメイン名: 
- Step 3: アクセスとセキュリティの設定 
            - ネットワーク構成: パブリックアクセス
- アクセスポリシー: カスタムアクセスポリシーよりポリシーを2つ追加します- 1つ目 
                - タイプを選択: IPv4アドレス
- プリンシパルを入力: IPアドレスチェッカーで調べた、接続元の IPv4 アドレスを入力 (例: 123.1.23.45)
- アクションを選択: 許可
 
- タイプを選択: 
- 2つ目 
                - タイプを選択: IAM ARN
- プリンシパルを入力: arn:aws:iam::アカウントID:role/service-role/rekognition-lambda-role- 上記の アカウントIDの部分は、利用している AWS のアカウントIDに置き換えます
- アカウントIDは アカウント設定 のページで確認できます
 
- 上記の 
- アクションを選択: 許可
 
- タイプを選択: 
 
- 1つ目 
                
- 上記以外はデフォルト設定のまま 次へをクリック
 
- ネットワーク構成: 
- Step 4: 確認 
            - 設定内容に問題がなければ 確認をクリック
 
- 設定内容に問題がなければ 
 
- Step 1: デプロイタイプの選択 
            
Amazon Elasticsearch Service のドメインが作成されるまで10分程度かかります。ドメインのステータスが アクティブ になるまで待ちましょう。
ドメインのステータスが変わったら、概要タブの エンドポイント の URL をコピーしておきます
 (例: https://search-rekognition-domain-xxxx.your-region.es.amazonaws.com)。
AWS Lambda の設定
続いて、 Lambda 関数に環境変数を設定するために、先ほど作成した Lambda 関数の画面に戻ります。
- 画面下部の 環境変数>環境変数を管理をクリック
- 環境変数の追加をクリック
- 以下の2つの環境変数を入力し、保存をクリック- 1つ目 
            - キー:- ES_URL
- 値: 先ほどコピーした URL
 
- 2つ目 
            - キー:- REGION
- 値: 利用しているリージョン (例:- ap-northeast-1)
 
- 3つ目 
            - キー:- TOPIC_ARN
- 値: 先ほどコピーした Amazon SNS トピックのARN
 
 
- 1つ目 
            
また、Lambda の実行ロールに権限を付与します。
- 画面上部の アクセス権限>実行ロール>rekognition-lambda-roleをクリックします
- IAM ロールの画面が開くので アクセス権限>ポリシーをアタッチしますをクリックします
- AmazonRekognitionReadOnlyAccessにチェックを入れて、右下の- ポリシーのアタッチをクリックします
そして、S3 への画像アップロード時に Lambda が実行されるようにトリガーの設定を行います。
- 画面上部の 設定>Designer>+トリガーを追加をクリック
- トリガーの設定で- S3を選択し、以下の設定を入力し- 追加をクリックします- バケット: 顔画像のアップロード用のバケット(ソラコム様の記事の手順で作成していない場合は、別途ご作成ください)
- イベントタイプ:- すべてのオブジェクト作成イベント
- サフィックス:- .jpg(アップロードされる画像の拡張子にあわせてご変更ください)
 
以上で、S3 に画像がアップロードされた際に Lambda 関数が実行、認識結果が Elasticsearch Service に書き込まれ、お得意様の来店検知時にメールで通知が飛ぶようになります。なお、S3 にアップロードされた画像の保存が不要な場合は、上記の Lambda 関数の最後に画像を削除する処理を追加する、もしくは S3 バケットのライフサイクルポリシーを設定するなどの方法で、自動で画像を削除することができます。
Kibana の設定
最後に Amazon Elasticsearch Service の Kibana を用いて、顔認識した結果を分析するためのダッシュボードを作成します。
まず、Kibana のダッシュボードを開きます。
- 新しいタブでAmazon Elasticsearch Service のコンソール を開きます
- 左側のメニューのドメインから rekognition-domainを選択します
- 概要 > Kibana の URL をクリックします
Kibana の画面が開いたら、まずはインデックスパターンの設定を行います。
- 左側の歯車のアイコンをクリック
- 左側の Index Patternsを選択し、右側のCreate index patternをクリックします
- Step 1 of 2: Define index pattern 
          - Index pattern: recordと入力し、Next stepをクリックします
 
- Index pattern: 
- Step 2 of 2: Configure settings 
          - Time Filter field name: timestampを選択
- Create index patternをクリックします
 
- Time Filter field name: 
- 以下のような画面が表示され、レコードの型が分析されているのが分かります
Kibana でレコードの確認
次に、Kibana で Amazon Elasticsearch Service に登録されたレコードを確認します。
Kibana での可視化
最後に、認識結果をグラフを用いて可視化してみます。例えば、来店者の年齢推定結果を分析します。
- 左側のグラフのアイコンをクリックし、画面中央の Create new visualizationをクリックします
- Horizontal Barをクリックします
- New Data Table / Choose a source では record*をクリックします
- 左側の Metrics > Y-Axis に以下の設定をします 
          - Aggregation:- Countを選択
 
- Buckets > + Addをクリックし、X-axisを選択します- Aggregation:- Histogramを選択
- Field:- AgeLowを入力 (推定された年齢のレンジの最小値です)
- Minimum interval:- 10を入力
 
- Metrics の上にある再生ボタンをクリックし、変更を適用します
以下のようなグラフが表示され、来客の年齢の傾向を可視化することができました。
最後に、画面上部の Save をクリックし、グラフを保存します。
同様に、性別や表情から読み取れる感情についても Visualization を作成し1つのダッシュボード上に表示することで、下図のように来客の傾向をリアルタイムに分析するためのダッシュボードを作成することができます。
さいごに
この記事では、S+Camera と Amazon Rekognition, Amazon Elasticsearch Service, Amazon SNS などのAWSサービスを組み合わせて、Wi-Fiが整っていない環境でもカメラの画像から来客の傾向分析やお得意様の来店通知などを実現する方法を紹介しました。Amazon Rekognition の詳細な機能についてはドキュメントもご参照ください。冒頭でも述べたとおり、本記事で扱う内容に関して、顔の画像データのプライバシーやセキュリティなどには、十分にご注意の上ご実装ください。
また、本記事では紹介しませんでしたが、Amazon Kinesis Video Streams を用いることで、Wi-Fiや有線ネットワーク環境で常時カメラからの動画をストリーミングし、再生や分析に利用することができます。こちらについてはハンズオンも提供しているため、お客様のネットワーク環境や分析の目的に応じてより良い方法を選択して下さい。
著者について
ソフトウェアエンジニアとして会話AIやロボット開発を経験しました。AWS では IoT スペシャリストソリューションアーキテクトとして、お客様の IoT 関連案件を支援しています。





