LoginSignup
1
2
Qiita×Findy記事投稿キャンペーン 「自分のエンジニアとしてのキャリアを振り返ろう!」

ハッカソン参加時の備忘録 ~ 第6回:【Python】AWS LambdaとAWS Rekoginitionを連携させてみよう ~

Posted at

こんにちは。ITエンジニアのきゅうです。
前回、AWSのLambdaとAWSのS3(ストレージサービス)を連結させるサンプルを備忘録したので、今回は当方が良く使うAWSのAIサービスの一つであり、簡単に画像解析を行うことが出来るサービス『Rekoginition』と連結したいと思います。

今回作成するシステム構成図です。
image.png

※前回の備忘録に記載した『S3』が登場しております。
 これはRekognitionに読み込ませる画像をS3などに格納しておく必要がある為です。
 ですので、今回の記事では以下の記事の設定がしてあることが前提となります。

Amazon Rekoginitionについて

改めて、Rekognitionについておさらいします。
Amazon Rekognitionは、画像や動画の分析ができるAIクラウドサービスです。

2024年2月現在、主に以下の8つの機能が御座います。
※AWSは日々更新がかかっているため、将来的に機能拡張・縮小があるかもしれませんので、現時点での機能紹介になります。

画像分析(Amazon Rekognition Image)
1. シーンや物体の検出
2. 顔の検索や認証
3. 顔の検出や分析
4. 有名人の認識
5. 安全でないコンテンツの検出
6. テキストの検出
7. カスタムラベル
8. 個人用保護具(PPE)の検出
動画分析(Amazon Rekognition Video)
1. リアルタイムのストリーミングビデオイベントの分析
 1.1. ラベル検出
 1.2. 顔検索
2. 保存されたビデオ分析
 2.1. 物体、シーン、アクティビティ検出
 2.2. コンテンツのモデレーション
 2.3. テキスト検出
 2.4. 有名人の認識
 2.5. 顔検出と分析
 2.6. 顔検索
 2.7. 人物の動線の検出
Amazon Rekognitionカスタムラベル
1. ビジネスニーズに合わせた画像の物体やシーンの特定
Amazon Rekognition Face Liveness
1. カメラを出し抜くなりすましを検出

上記の主な使い方は後で記載していきますので、まずはRekognitionへの接続をしていきましょう。

Rekoginitionの利用料金について

Rekognitionの上記の内、『Image』と『Video』の料金について見ていきます。

画像分析(Amazon Rekognition Image)
無料利用枠が適応されます。その間、1 か月あたり 5,000 画像を分析可能。
それ以上、利用する場合は画像数やリージョンにより異なった料金がかかります。
動画分析(Amazon Rekognition Video)
無料利用枠が適応されます。その間、1 か月あたり 1,000分の動画を分析可能。
それ以上、利用する場合は画像数やリージョンにより異なった料金がかかります(アーカイブ動画の場合1分につき0.13USD、ライブ動画の場合、1分につき0.15USD)。

Amazon Rekognitionに接続してみよう。

では、早速Rekognitionを使ってみましょう!!

1.Rekognitionは事前設定不要

使用するサービス:Rekognition
使用する機能:―

ということで、下記がRekognitionのコンソール画面です。
S3と違って、『●●を作成』のようなボタンがありません。
そうなんです!!
「カスタムモデレーション」や「一括分析」を使わない限りは、事前にRekognitionで設定する必要はないんです。

image.png

2.ロールの作成

使用するサービス:IAM
使用する機能:ロール

『ロールを作成』ボタンを押下

image.png

ユースケースで「Lambda」を選択

「信頼されたエンティティタイプ」で「AWS のサービス」を、
「ユースケース」で「Lambda」を選択し、「次へ」を押下
image.png

許可ポリシーでRekognitionを選択

「許可ポリシー」の検索ボックスに「Rekoginition」と入力し、
「AmazonRekognitionFullAccess」と「AWSLambdaRekognitionReadOnlyAccessExecutionRole」、「AWSLambdaRekognitionWriteOnlyAccessExecutionRole」を選択します。
image.png

許可ポリシーでS3を選択

「許可ポリシー」の検索ボックスから「Rekoginition」を消して「S3」と入力し、
「AmazonS3FullAccess」と「AWSLambdaS3ExecutionRole」を選択し。
「次へ」を押下します。
image.png

S3の時も記載したが、基本的にハッカソンであれば「FullAccess」「Execution」が付くポリシーをチェックすれば問題ない。
※通常のプロジェクトでFullAccessとかつけてはダメです。。。

ここではRekognitionのロールだけでなく、S3のロールも設定している。
理由としては、Rekognitionで分析する画像をS3に格納しておく想定だからだ。
なので、分析する画像がS3に格納しない場合、S3のロールは不要です。

ロール名を任意でつける

ロール名を任意でつけて、一番下の『ロールを作成』を押下
image.png

3.関数を作成する

使用するサービス:Lambda
使用する機能:関数

関数の設定をする

Lambdaのコンソールから『関数の作成』をクリックし、
設定画面に画面キャプチャーのように登録する。
そして、一番下の「関数の作成」をクリックします。

デフォルトの実行ロールの変更では「既存のロールを使用する」を選択し、上で選択したロールを選びます。

image.png

4.S3に画像ファイルをアップロードしましょう。

事前にS3にバケットを作成し、画像ファイルをアップロードしましょう。
ここでは割愛しますが、当方が記載した以下の記事を参考にしていただけると幸いです。

5.Lambdaのソースをコーディングする

使用するサービス:Lambda
使用する機能:関数

ソースコードのコピペ

-import json
-
-def lambda_handler(event, context):
-    # TODO implement
-    return {
-        'statusCode': 200,
-        'body': json.dumps('Hello from Lambda!')
-    }

+import json
+import boto3
+
+def lambda_handler(event, context):
+    
+    ### AWSサービスのインスタンス化 ###
+    rekognition = boto3.client('rekognition')
+    s3 = boto3.resource('s3')
+    
+    image = None
+    try:
+        bucket = 'test-s3-2197-bucket'
+        key = 'test3.png'
+        s3_object = s3.Object(bucket, key)
+        image = s3_object.get()['Body'].read()
+        
+        response = rekognition.detect_labels(Image={'Bytes': image})
+        lambda_response = {
+            "statusCode": 200,
+            "body": json.dumps(response)
+        }
+        
+        labels = [label['Name'] for label in response['Labels']]
+        print("Labels found:")
+        print(labels)
+    
+    except Exception as e:
+        error_message = "Couldn't analyze image: " + e.response['Error']['Message']
+        lambda_response = {
+           'statusCode': 400,
+           'body': {
+               "Error": e.response['Error']['Code'],
+               "ErrorMessage": error_message
+           }
+       }
+    
+    return {
+        'statusCode': 200,
+        'body': json.dumps('Hello from Lambda!')
+    }

↓↓↓ コピー用です ↓↓↓

import json
import boto3

def lambda_handler(event, context):
    
    ### AWSサービスのインスタンス化 ###
    rekognition = boto3.client('rekognition')
    s3 = boto3.resource('s3')
    
    image = None
    try:
        bucket = 'test-s3-2197-bucket'
        key = 'test3.png'
        s3_object = s3.Object(bucket, key)
        image = s3_object.get()['Body'].read()
        
        response = rekognition.detect_labels(Image={'Bytes': image})
        lambda_response = {
            "statusCode": 200,
            "body": json.dumps(response)
        }
        
        labels = [label['Name'] for label in response['Labels']]
        print("Labels found:")
        print(labels)
    
    except Exception as e:
        error_message = "Couldn't analyze image: " + e.response['Error']['Message']
        lambda_response = {
           'statusCode': 400,
           'body': {
               "Error": e.response['Error']['Code'],
               "ErrorMessage": error_message
           }
       }
    
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

テストの実行

image.png

タイムアウトエラーが発生!!

多分、以下のようなエラーが発生します。
理由は、Lambdaのタイムアウト設定がデフォルト3秒になっているからです。
image.png

なので、タイムアウト設定を延長させましょう。

使用するサービス:Lambda
使用する機能:関数
タブ:設定
リスト:一般設定
⇒『基本設定を編集』が開ければOKです。

以下の画面より「タイムアウト」を1分に設定してみましょう。

image.png

そして、再度実行します。
はい、今度は成功しました!!
『Label found』で画像のLabelが取れてきてますね!!

image.png

ソースコードの解説

それではソースコードの解説に入っていきたいと思いますが、
以下で話した内容は省きますので、「S3」や「boto3.client」、「boto3.resource」について知りたい方は以下の記事を参照ください。

今回は以下のような写真をRekognitionに読み込ませている。

それを下記のソースがRekognitionのImageの内、『detect_labels』メソッドの呼び出しとなる。

rekognition.detect_labels(Image={'Bytes': image})

Rekognitionのメソッドの種類については、他の備忘録でまとめたいと思います。

すると、以下のようなJSONデータがresponseデータに入ってくる。

{'Labels': [{'Name': 'Face', 'Confidence': 99.99978637695312, 'Instances': [], 'Parents': [{'Name': 'Head'}, {'Name': 'Person'}], 'Aliases': [], 'Categories': [{'Name': 'Person Description'}]}, {'Name': 'Head', 'Confidence': 99.99978637695312, 'Instances': [], 'Parents': [{'Name': 'Person'}], 'Aliases': [], 'Categories': [{'Name': 'Person Description'}]}, {'Name': 'Person', 'Confidence': 99.99978637695312, 'Instances': [{'BoundingBox': {'Width': 0.44205889105796814, 'Height': 0.4994780719280243, 'Left': 0.556955099105835, 'Top': 0.0010741690639406443}, 'Confidence': 99.36341857910156}, {'BoundingBox': {'Width': 0.7352738976478577, 'Height': 0.49544477462768555, 'Left': 0.17851921916007996, 'Top': 0.5045552253723145}, 'Confidence': 98.16604614257812}, {'BoundingBox': {'Width': 0.5990684628486633, 'Height': 0.5018318295478821, 'Left': 0.021479398012161255, 'Top': 0.001611444284208119}, 'Confidence': 97.99974822998047}], 'Parents': [], 'Aliases': [{'Name': 'Human'}], 'Categories': [{'Name': 'Person Description'}]}, {'Name': 'Photography', 'Confidence': 99.99978637695312, 'Instances': [], 'Parents': [], 'Aliases': [{'Name': 'Photo'}], 'Categories': [{'Name': 'Hobbies and Interests'}]}, {'Name': 'Portrait', 'Confidence': 99.99978637695312, 'Instances': [], 'Parents': [{'Name': 'Face'}, {'Name': 'Head'}, {'Name': 'Person'}, {'Name': 'Photography'}], 'Aliases': [], 'Categories': [{'Name': 'Hobbies and Interests'}]}, {'Name': 'Accessories', 'Confidence': 99.78221893310547, 'Instances': [], 'Parents': [], 'Aliases': [{'Name': 'Accessory'}], 'Categories': [{'Name': 'Apparel and Accessories'}]}, {'Name': 'Glasses', 'Confidence': 99.78221893310547, 'Instances': [{'BoundingBox': {'Width': 0.3125612139701843, 'Height': 0.15536221861839294, 'Left': 0.11858267337083817, 'Top': 0.15961448848247528}, 'Confidence': 98.89239501953125}], 'Parents': [{'Name': 'Accessories'}], 'Aliases': [], 'Categories': [{'Name': 'Apparel and Accessories'}]}, {'Name': 'Boy', 'Confidence': 99.36341857910156, 'Instances': [{'BoundingBox': {'Width': 0.44205889105796814, 'Height': 0.4994780719280243, 'Left': 0.556955099105835, 'Top': 0.0010741690639406443}, 'Confidence': 99.36341857910156}], 'Parents': [{'Name': 'Male'}, {'Name': 'Person'}], 'Aliases': [], 'Categories': [{'Name': 'Person Description'}]}, {'Name': 'Child', 'Confidence': 99.36341857910156, 'Instances': [{'BoundingBox': {'Width': 0.44205889105796814, 'Height': 0.4994780719280243, 'Left': 0.556955099105835, 'Top': 0.0010741690639406443}, 'Confidence': 99.36341857910156}], 'Parents': [{'Name': 'Person'}], 'Aliases': [{'Name': 'Kid'}], 'Categories': [{'Name': 'Person Description'}]}, {'Name': 'Male', 'Confidence': 99.36341857910156, 'Instances': [{'BoundingBox': {'Width': 0.44205889105796814, 'Height': 0.4994780719280243, 'Left': 0.556955099105835, 'Top': 0.0010741690639406443}, 'Confidence': 99.36341857910156}, {'BoundingBox': {'Width': 0.5990684628486633, 'Height': 0.5018318295478821, 'Left': 0.021479398012161255, 'Top': 0.001611444284208119}, 'Confidence': 97.99974822998047}], 'Parents': [{'Name': 'Person'}], 'Aliases': [], 'Categories': [{'Name': 'Person Description'}]}, {'Name': 'Body Part', 'Confidence': 98.52674102783203, 'Instances': [], 'Parents': [], 'Aliases': [], 'Categories': [{'Name': 'Person Description'}]}, {'Name': 'Finger', 'Confidence': 98.52674102783203, 'Instances': [], 'Parents': [{'Name': 'Body Part'}, {'Name': 'Hand'}, {'Name': 'Person'}], 'Aliases': [], 'Categories': [{'Name': 'Person Description'}]}, {'Name': 'Hand', 'Confidence': 98.52674102783203, 'Instances': [], 'Parents': [{'Name': 'Body Part'}, {'Name': 'Person'}], 'Aliases': [], 'Categories': [{'Name': 'Person Description'}]}, {'Name': 'Happy', 'Confidence': 98.24759674072266, 'Instances': [], 'Parents': [{'Name': 'Face'}, {'Name': 'Head'}, {'Name': 'Person'}], 'Aliases': [], 'Categories': [{'Name': 'Expressions and Emotions'}]}, {'Name': 'Adult', 'Confidence': 98.16604614257812, 'Instances': [{'BoundingBox': {'Width': 0.7352738976478577, 'Height': 0.49544477462768555, 'Left': 0.17851921916007996, 'Top': 0.5045552253723145}, 'Confidence': 98.16604614257812}, {'BoundingBox': {'Width': 0.5990684628486633, 'Height': 0.5018318295478821, 'Left': 0.021479398012161255, 'Top': 0.001611444284208119}, 'Confidence': 97.99974822998047}], 'Parents': [{'Name': 'Person'}], 'Aliases': [], 'Categories': [{'Name': 'Person Description'}]}, {'Name': 'Female', 'Confidence': 98.16604614257812, 'Instances': [{'BoundingBox': {'Width': 0.7352738976478577, 'Height': 0.49544477462768555, 'Left': 0.17851921916007996, 'Top': 0.5045552253723145}, 'Confidence': 98.16604614257812}], 'Parents': [{'Name': 'Person'}], 'Aliases': [], 'Categories': [{'Name': 'Person Description'}]}, {'Name': 'Woman', 'Confidence': 98.16604614257812, 'Instances': [{'BoundingBox': {'Width': 0.7352738976478577, 'Height': 0.49544477462768555, 'Left': 0.17851921916007996, 'Top': 0.5045552253723145}, 'Confidence': 98.16604614257812}], 'Parents': [{'Name': 'Adult'}, {'Name': 'Female'}, {'Name': 'Person'}], 'Aliases': [], 'Categories': [{'Name': 'Person Description'}]}, {'Name': 'Man', 'Confidence': 97.99974822998047, 'Instances': [{'BoundingBox': {'Width': 0.5990684628486633, 'Height': 0.5018318295478821, 'Left': 0.021479398012161255, 'Top': 0.001611444284208119}, 'Confidence': 97.99974822998047}], 'Parents': [{'Name': 'Adult'}, {'Name': 'Male'}, {'Name': 'Person'}], 'Aliases': [], 'Categories': [{'Name': 'Person Description'}]}, {'Name': 'People', 'Confidence': 93.36598205566406, 'Instances': [], 'Parents': [{'Name': 'Person'}], 'Aliases': [], 'Categories': [{'Name': 'Person Description'}]}, {'Name': 'Smile', 'Confidence': 88.35812377929688, 'Instances': [], 'Parents': [{'Name': 'Face'}, {'Name': 'Happy'}, {'Name': 'Head'}, {'Name': 'Person'}], 'Aliases': [], 'Categories': [{'Name': 'Expressions and Emotions'}]}, {'Name': 'Laughing', 'Confidence': 70.95403289794922, 'Instances': [], 'Parents': [{'Name': 'Face'}, {'Name': 'Happy'}, {'Name': 'Head'}, {'Name': 'Person'}], 'Aliases': [{'Name': 'Laugh'}], 'Categories': [{'Name': 'Expressions and Emotions'}]}, {'Name': 'Art', 'Confidence': 57.981483459472656, 'Instances': [], 'Parents': [], 'Aliases': [], 'Categories': [{'Name': 'Hobbies and Interests'}]}, {'Name': 'Collage', 'Confidence': 57.981483459472656, 'Instances': [], 'Parents': [{'Name': 'Art'}], 'Aliases': [], 'Categories': [{'Name': 'Hobbies and Interests'}]}, {'Name': 'Clothing', 'Confidence': 57.55129623413086, 'Instances': [], 'Parents': [], 'Aliases': [{'Name': 'Apparel'}], 'Categories': [{'Name': 'Apparel and Accessories'}]}, {'Name': 'T-Shirt', 'Confidence': 57.55129623413086, 'Instances': [], 'Parents': [{'Name': 'Clothing'}], 'Aliases': [], 'Categories': [{'Name': 'Apparel and Accessories'}]}, {'Name': 'Mouth', 'Confidence': 55.66556930541992, 'Instances': [], 'Parents': [{'Name': 'Body Part'}, {'Name': 'Person'}], 'Aliases': [{'Name': 'Lip'}], 'Categories': [{'Name': 'Person Description'}]}, {'Name': 'Teeth', 'Confidence': 55.66556930541992, 'Instances': [], 'Parents': [{'Name': 'Body Part'}, {'Name': 'Mouth'}, {'Name': 'Person'}], 'Aliases': [], 'Categories': [{'Name': 'Person Description'}]}], 'LabelModelVersion': '3.0', 'ResponseMetadata': {'RequestId': '9190209d-53af-44f2-99e4-7971e7a0c7ad', 'HTTPStatusCode': 200, 'HTTPHeaders': {'x-amzn-requestid': '9190209d-53af-44f2-99e4-7971e7a0c7ad', 'content-type': 'application/x-amz-json-1.1', 'content-length': '6655', 'date': 'Tue, 20 Feb 2024 14:20:55 GMT'}, 'RetryAttempts': 0}}

このJSONデータを

labels = [label['Name'] for label in response['Labels']]
        print("Labels found:")
        print(labels)

が、JSONデータをループ分で回しながら、'Name'ラベルだけを抽出しているのである。

はい。
ということで、今回はRekognitionの概要と使い方を見てきました。
次はRekognitionのメソッドの種類をまとめていきたいと思います。

最後までお読み頂きありがとうございました。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2