Amazon Rekognition+lambdaで顔認識した結果をS3にログファイルとして書き出す

前置き

S3に画像がアップロードされたら、それをトリガにしてLambdaがRekognitionを叩いて、帰ってきた判定結果を同じS3にテキストファイルとして出力するやつをpythonで作りました。やったね。

やることの整理

  • S3にファイルがアップロードされたのをlambdaで検知する。
  • lambdaがアップロードされたファイルをRekognitionに投げて顔認識処理する。
  • Rekognitionの判定結果をS3にテキストファイル形式で保存する。

ポリシーの設定

IAMコンソールで適当なロールを作り、下記のポリシーをアタッチする。

  • AmazonRekognitionFullAccess ※多分つけ過ぎなので適宜削ってほしい
  • S3はアクションの読み込みと書き込みができればOK

S3の設定

特別やることはないですが、忘れがちなのがバケットポリシーのリソースのところ。
Resourceのところは、使うS3のarnをそのまま書くだけではなく、/*をつけたのものも列挙します。

たとえば、arn:aws:s3:::ABCDBUCKETのバケットを使う場合はこう。

{
    "Version": "2012-10-17",
    "Id": "Policy123456789",
    "Statement": [
        {
            "Sid": "1234567890",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::ABCDBUCKET",
                "arn:aws:s3:::ABCDBUCKET/*"
            ],
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "XXX.XXX.XXX.XXX/YY",
                    ]
                }
            }
        }
    ]
}

Lambda functionの作成

AWSコンソールTOP>lambda>関数の作成>設計図>下の検索フォームで「get」で検索をかけると、「s3-get-object-python3」が出てくるのでそれを選択。

1.png

基本的な情報

  • 名前:任意(今回はgetImageAndRekにしました)
  • ロール:さっき作ったポリシーのアタッチされたロールをプルダウンから選択

s3

  • バケット:画像を置きたいバケットの名前をプルダウンから選択
  • イベントタイプ:PUT
  • プレフィックスとサフィックスは空でOK

トリガーの有効化

  • チェックしない。(あとで有効化するので)。

Lambda 関数のコード

  • 無視して「関数の作成」を押す。

a.png

こんな感じになっていれば良さみがすごいですね。

Lambda function(python3.6)の実装

下記のようなコードを関数コードエディタにソォイッってする。

lambdafunction
import json
import urllib.parse
import boto3
from datetime import datetime

REGION = お好きなリージョン名
REKOGNITION = boto3.client('rekognition')
AWS_S3_BUCKET_NAME = お好きなバケット名

def lambda_handler(event, context):
    #S3にデータがアップロードされたのをトリガーにしてバケット名とファイル名を取得
    s3 = boto3.client('s3')
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')

    try:
        response = s3.get_object(Bucket=bucket, Key=key)

        #画像処理をかけてレスポンスを受け取る
        return_faces = detect_faces(bucket,key)

        #ログファイル書き出し用のs3オブジェクトを作る
        #ログファイルに判定した画像名を入れたいが、ドットが邪魔なのでアンスコに置き換える
        newfilename = str(key).replace(".", "_")

        #ログファイルの名前をlog_(ファイル名)_(拡張子)_YYYY-MM-DD-HH-MM-SS.txtにして書き出し
        logfilename = 'log_' + newfilename + "_" + datetime.now().strftime('%Y-%m-%d-%H-%M-%S') + '.txt'
        object = boto3.resource('s3').Object(AWS_S3_BUCKET_NAME, logfilename)

        data = str(return_faces)
        #ファイルの書き出し
        object.put(Body=data)

        return return_faces

    except Exception as e:
        print(e)
        print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket))
        raise e



#顔認識
def detect_faces(b,i):
    try:
        response = REKOGNITION.detect_faces(
            Image={
                'S3Object': {
                    'Bucket': b,
                    'Name': i,
                }
            },
            Attributes=[
                'ALL',
            ]
        )
        return response
    except Exception as ex:
        raise ChaliceViewError("fail to detect faces. error = " + ex.message)

#ラベルの取得
def detect_labels(b,i):
    client=boto3.client('rekognition',REGION)
    response = client.detect_labels(Image={'S3Object':{'Bucket':b,'Name':i}})
    return response

年齢、性別、感情とか一切合切取得したかったので、AttributesをALLにしてます。
多すぎる場合はAttributesをDEFAULTにしてどうぞ。

写真のラベルが取りたい場合はreturn_faces = detect_faces(bucket,key)のところをreturn_faces = detect_labels(bucket,key)にすればそのまま動くと思います。

テスト

これで動くようにはなっていると思うので、テストします。
あらかじめ使うS3に画像をアップしておいてください。

画面上部「テスト」の左側のプルダウンを展開し、「テストイベントの設定」を選択。
書き換えるのは3箇所、「"key": 画像認識に食わせるs3上の画像ファイル名」「"arn": 利用するS3のarn」「"name": 利用するS3の名前」のところだけでよいです。

{
  "Records": [
    {
      "eventVersion": "2.0",
      "eventTime": "1970-01-01T00:00:00.000Z",
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "s3": {
        "configurationId": "testConfigRule",
        "object": {
          "eTag": "0123456789abcdef0123456789abcdef",
          "sequencer": "0A1B2C3D4E5F678901",
          "key": 画像認識に食わせるs3上の画像ファイル名,
          "size": 1024
        },
        "bucket": {
          "arn": 利用するS3のarn,
          "name": 利用するS3の名前,
          "ownerIdentity": {
            "principalId": "EXAMPLE"
          }
        },
        "s3SchemaVersion": "1.0"
      },
      "responseElements": {
        "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH",
        "x-amz-request-id": "EXAMPLE123456789"
      },
      "awsRegion": "ap-northeast-1",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "EXAMPLE"
      },
      "eventSource": "aws:s3"
    }
  ]
}

上記3箇所を修正したら「保存」ボタンを押して内容を保存。
POPUPが閉じたらプルダウンが「s3Put」になっていることを確認しつつ「テスト」ボタンを押下すると、お使いのS3にログファイルが吐かれているのではないでしょうか。

トリガーの有効化

デザイナーでinputのほうのS3を選んで、下の方に出てくるスイッチを「有効」にすると、S3にファイルをアップロードしたときのトリガーが有効化されます。

2.png

有効化したら、S3に適当な画像をアップロードしてみてください。
テストのときと同じようにログファイルが書き出されると思います。

判定結果の検証

それでは、ドキドキの出力結果を見てみましょう。
画像はみんなだいすきぱくたそさんからお借りしています。
ただ判定結果ぜんぶ載せるのしんどいのでそれっぽいやつだけにします。

笑顔

その1

smile1.jpg

項目名 判定値 判定確度
AgeRange 'Low': 20, 'High': 38 ---
Smile True 99.6939697265625
Gender Female 100.0
Emotions HAPPY 99.9847183227539
  ANGRY 0.6470799446105957
  CALM 0.3142118453979492

喜色満面って感じでしょうか。いい感じです。

その2

smile3.jpg

項目名 判定値 判定確度
AgeRange 'Low': 38, 'High': 57 ---
Smile True 99.44691467285156
Gender Male 93.27049255371094
Emotions HAPPY 99.80696105957031
  CONFUSED 1.0035932064056396
  DISGUSTED 0.35605430603027344

笑顔判定99.4%!

泣き顔

その1

sad1.jpg

項目名 判定値 判定確度
AgeRange 'Low': 35, 'High': 52 ---
Smile True 98.21951293945312
Gender Male 99.92884063720703
Emotions DISGUSTED 66.32764434814453
  ANGRY 9.717315673828125
  CONFUSED 7.103925704956055

笑顔…笑顔に見えなくも…ウーン…

その2

sad3.jpg

項目名 判定値 判定確度
AgeRange 'Low': 19, 'High': 36 ---
Smile False 84.64250183105469
Gender Female 100.0
Emotions SAD 75.01649475097656
  HAPPY 3.8781933784484863
  DISGUSTED 3.0288636684417725

ようやくSADが出た。
こういうやつ(海外の顔認識)あるあるですが、日本人の顔がうまく認識できてない可能性あるな…

驚き顔

その1

suprise2.jpg

項目名 判定値 判定確度
AgeRange 'Low': 26, 'High': 44 ---
Smile True 93.04144287109375
Gender Male 99.92268371582031
Emotions SURPRISED 16.004776000976562
  HAPPY 7.592551231384277
  ANGRY 2.5043487548828125

どうでもいいんですけど女性の場合は判定確度100%って言い切るのになんで男性のときだけちょっとだけ自信なくなるんですかね。
一応SUPRISEが一番高いけど確度が低いなぁ。

その2

suprise3.jpg

項目名 判定値 判定確度
AgeRange 'Low': 20, 'High': 38 ---
Smile True 89.03118896484375
Gender Female 100.0
Emotions HAPPY 73.7810287475586
  SURPRISED 8.159446716308594
  CONFUSED 3.975170850753784

こっちは笑顔判定で幸せとのこと。んー。
目を見開いて口を開けてると幸せカウントされてる?幸せと驚きは表裏一体ということでしょうか…

怒り顔

その1

angry4.jpg

項目名 判定値 判定確度
AgeRange 'Low': 45, 'High': 66 ---
Smile False 99.61791229248047
Gender Male 99.90088653564453
Emotions CONFUSED 54.16391372680664
  CALM 26.695775985717773
  SURPRISED 2.6121914386749268

困惑54%、落ち着き26%、驚き2%。怒りは湧いてこないようです。

その2
angry3.jpg

項目名 判定値 判定確度
AgeRange 'Low': 26, 'High': 43 ---
Smile True 88.80587005615234
Gender Female 100.0
Emotions SURPRISED 64.84302520751953
  HAPPY 25.200944900512695
  CALM 3.864133358001709

驚きかぁ。険しい表情の判別が苦手なのかな…

判定の所感

  • 年齢と性別は妥当性が高い。ただし年齢のレンジが広い…
  • 感情はポジティブなやつに関しては割と得意そう。怒りや悲しみはあまり理解できない。

(ネタ)こっちとしてもどんな気持ちなのかわかんない顔

「一体どんな気持ちなんだよこの顔」というやつを、AWSに判断してもらいます。

unknown1.jpg

項目名 判定値 判定確度
AgeRange 'Low': 26, 'High': 44 ---
Smile True 98.91413116455078
Gender Male 99.92724609375
Emotions HAPPY 95.3128890991211
  SURPRISED 3.6652445793151855
  ANGRY 3.0090630054473877

幸せらしい。

unknown2.jpg

項目名 判定値 判定確度
AgeRange 'Low': 35, 'High': 52 ---
Smile True 97.0233154296875
Gender Male 99.92681884765625
Emotions HAPPY 82.21611022949219
  SURPRISED 60.59770584106445
  CONFUSED 3.8815677165985107

幸せらしい。

unknown4.jpg

項目名 判定値 判定確度
AgeRange 'Low': 26, 'High': 44 ---
Smile True 88.28538513183594
Gender Male 99.92774963378906
Emotions SURPRISED 55.02965545654297
  ANGRY 18.620990753173828
  HAPPY 4.881137847900391

驚いているらしい。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.