AWS入門ブログリレー2024〜Amazon Rekognition編〜

Amazon Rekognitionについて2024年時点の情報をまとめてみました。AWSサービス入門記事として是非ご活用下さい。
2024.04.12

当エントリは弊社AWS事業本部による『AWS 入門ブログリレー 2024』の18日目のエントリです。
このブログリレーの企画は、普段 AWS サービスについて最新のネタ・深い/細かいテーマを主に書き連ねてきたメンバーの手によって、 今一度初心に返って、基本的な部分を見つめ直してみよう、解説してみようというコンセプトが含まれています。
AWS をこれから学ぼう!という方にとっては文字通りの入門記事として、またすでに AWS を活用されている方にとっても AWS サービスの再発見や 2024 年のサービスアップデートのキャッチアップの場となればと考えておりますので、ぜひ最後までお付合い頂ければ幸いです。

では、さっそくいってみましょう。今回のテーマは『Amazon Rekognition』です。

Amazon Rekognition とは

Amazon Rekognitionとは、画像・動画認識のためのフルマネージドな機械学習サービスです。
特徴を簡単にまとめると、下記です。

  • AWSが予め用意している機械学習モデルを利用できる
  • 自分で用意したデータを学習させ、独自のモデルを作成することも可能
  • 従量課金制(※Rekognitionと連携するサービスの料金は別途かかる)

Rekognitionが具体的にどのようなことができるかの詳細については、中村さんの「Amazon Rekognitionをアルティメット完全理解する」を読んでいただければと思います。完全理解できます。

Rekognition を使用するときの入力画像・動画の推奨設定について

Rekognitionに入力する画像・動画の推奨設定ついては、公式のドキュメントにまとめられています。

センサー、入力イメージ、ビデオのベストプラクティス - Amazon Rekognition

本ブログでのやってみたでは、カスタムコレクションに登録した顔と画像ファイルを比較するのですが、推奨事項を見ると証明写真のイメージがベストなのかなと思いました。
ただし、今回は証明写真を何パターンも用意できていないので、背景に完全に何も無い状態ではない画像も交えて検証しています。

また、Rekognitionで使用する入力画像について、S3バケットに格納されている画像データ、もしくは、画像をBase64でエンコードした文字列であることが必要です。
EC2などに配置された画像ファイルはそのまま使用できない点に注意しましょう。

カスタムコレクションについて

Rekognitionは、入力された顔のイメージをデータ化し、顔ベクトルというデータとして保存することができます。
また、顔ベクトルを紐づけるユーザーもユーザーベクトルと呼ばれ、顔ベクトルをユーザーベクトルに紐づけることで一人の人間の顔データが完成します。
カスタムコレクションは、顔ベクトルとユーザーベクトルを保存する箱です。

カスタムコレクションに登録したデータの保存先はAWS管理のフルマネージドサーバーのため、ユーザーが管理サーバーに対して直接操作することはできません。
保存されている顔ベクトル・ユーザーベクトルについて、コレクションごと複製することも現時点ではできません。
他のアカウントに同じデータを持つカスタムコレクションが必要な場合は、同じデータを使用してカスタムコレクションを再作成する必要があります。


また、犯罪の防止や、行方不明者の追跡など、司法領域を含む公共の安全支援に関わるユースケースの場合は、Rekognitionに関するベストプラクティスの適応が必須となります。 限られたユースケースだとは思いますが、どのようなユースケースが対象になるのか等、一度目を通しておくことをおすすめします。 (ドキュメントの修正が発生する場合があるので、本記事では内容をまとめません。直接、公式ドキュメントを参照してください。)
公共安全に関係するユースケース - Amazon Rekognition

コストについて

  • API実行の料金

1コマンドで複数画像を処理できるAPIの場合、発生する料金は処理される画像の数に依存します。

API ~ 1,000,000 枚 1,000,001 ~ 4,000,000 枚 4,000,001~30,000,000 枚 35,000,000 枚 ~
AssociateFaces
CompareFaces
DisassociateFaces
IndexFaces
SearchFacesbyImage
SearchFaces
SearchUsersByImage
SearchUsers
0.0013USD 0.001USD 0.0008USD 0.0005USD
DetectFaces
DetectModerationLabels
DetectLabels
DetectText
RecognizeCelebrities
DetectPPE
0.0013USD 0.001USD 0.0008USD 0.0003125USD
Image Properties 0.000975USD 0.00075USD 0.0006USD 0.0002344USD
  • カスタムコレクションのストレージ料金

FaceIDとUserIDは別料金となります。

機能 料金
顔ベクトルストレージ 0.000013USD/1 か月あたりの顔メタデータ
ユーザーベクトルストレージ 0.000013USD/1 か月あたりの顔メタデータ

そのため、今回の検証で作成する1ユーザーと5枚分の顔データの月間の保存料は、0.000078 USDです。

参考:料金 - Amazon Rekognition | AWS

やってみる

Rekognitionの機能の一つ、顔の比較についてテストしてみましょう。
今回は、カスタムコレクションに5枚画像を登録しておき、別日に撮った写真との比較してみます。

構築手順は下記です。

  • 検証用S3バケットを作成する
  • Rekognitionのカスタムコレクションを作成する
  • 画像比較する

今回はさくっと検証したいため、AWS CLIをAWS CloudShellから叩く方法で検証してみます。
CloudShellには予めAWS CLIがプリインストールされているため、起動するだけで今回の検証をそのまま実行することができます。

CloudShellの使い方はこちら

RekognitionをAWS CLIで使用するときの制約として、入力する画像はS3に保存されている画像のみしか選択できません。

では、やっていきましょう。

本検証で必要なIAM権限

本検証では、実行するIAMユーザー・ロールに下記の権限があることが必要です。

  • AWSCloudShellFullAccess
  • AmazonS3FullAccess
  • AmazonRekognitionFullAccess

作成・画像登録とテストを同じロールで実行するため、S3とRekognitionに関してFullAccessが必要ですが、実際のワークロードでは最小権限に絞ることを強く推奨します。

検証用S3バケットを作成する

まず、画像をインプットする用のS3バケットを作成します。
今回はカスタムコレクションに登録する画像と比較に使用する画像を同じS3バケットに配置します。
S3バケットはすべてデフォルトの設定で作成しています。

わかりやすいように、カスタムコレクションに登録する画像はCollectionフォルダに分けていれました。

Rekognitionのカスタムコレクションを作成する

ここからCloudShellを使った操作になります。
CloudShellの使い方はこちら

1.コレクションを作成する

コレクションを作成します。

collection-id: 作成するコレクション名
region: コレクションを作成するリージョン名、S3バケットを作成したリージョンと同じにする必要がある

aws rekognition create-collection --region ap-northeast-1 --collection-id "test-rekognition-SearchUsers"

ステータスコードが200で返ってきたら、コレクションの作成が成功しています。
listコマンドを実行すると、作成したコレクションを確認することができます。

aws rekognition list-collections

2.コレクションに顔ベクトルを追加する

ここで追加したFaceIDはユーザーとの関連付けで使用するため、メモをしておいてください。

Bucket: S3バケット名
Name: カスタムコレクションに登録する画像ファイルのキー
collection-id: コレクション名
external-image-id: Rekognitionが検出した顔に対して"ExternalImageId"を任意で付加できる

ExternalImageIdは、ユーザーベクトルに関連付ける前の顔ベクトルの判別に使えるため、人物に対して一意でつけておくことを推奨します!
もしこの段階でFaceIDをメモし忘れても、ExternalImageIdを登録しておけばなんとかなります。
後述している登録結果を見ていただければわかるのですが、ExternalImageIdが付いていない場合、登録データのみでは顔ベクトルの持ち主が誰なのか判別がつかなくなります。

画像ファイルのキーはS3から確認できます。

aws rekognition index-faces --image '{"S3Object":{"Bucket":"test-rekognition-input","Name":"Correction/Correction_1.jpg"}}' \
--collection-id "test-rekognition-SearchUsers" \
--max-faces 1 --quality-filter "AUTO" --detection-attributes "ALL" \
--external-image-id "tanuki" --region ap-northeast-1

実行すると、画像の顔がデータとして登録されます。
登録時に分析したデータが出てくるので、見てみると面白いかと思います。

実行結果は長いので畳んでおきます。
表情の分析もしてくれるのですが、CALM(穏やか)の数値が高いので穏やかな人間性が出たようです。嬉しいですね。

登録結果
{
    "FaceRecords": [
        {
            "Face": {
                "FaceId": "1eee925c-57d7-4cf6-bb74-d6e3f5891dc9",
                "BoundingBox": {
                    "Width": 0.4379235506057739,
                    "Height": 0.49999481439590454,
                    "Left": 0.2859043478965759,
                    "Top": 0.14187107980251312
                },
                "ImageId": "c742b014-8825-3f23-b505-fcec8f5c321d",
                "ExternalImageId": "tanuki",
                "Confidence": 99.9988784790039
            },
            "FaceDetail": {
                "BoundingBox": {
                    "Width": 0.4379235506057739,
                    "Height": 0.49999481439590454,
                    "Left": 0.2859043478965759,
                    "Top": 0.14187107980251312
                },
                "AgeRange": {
                    "Low": 13,
                    "High": 21
                },
                "Smile": {
                    "Value": false,
                    "Confidence": 99.89401245117188
                },
                "Eyeglasses": {
                    "Value": false,
                    "Confidence": 99.99591064453125
                },
                "Sunglasses": {
                    "Value": false,
                    "Confidence": 100.0
                },
                "Gender": {
                    "Value": "Female",
                    "Confidence": 99.99857330322266
                },
                "Beard": {
                    "Value": false,
                    "Confidence": 99.93783569335938
                },
                "Mustache": {
                    "Value": false,
                    "Confidence": 100.0
                },
                "EyesOpen": {
                    "Value": false,
                    "Confidence": 94.70735168457031
                },
                "MouthOpen": {
                    "Value": false,
                    "Confidence": 90.90394592285156
                },
                "Emotions": [
                    {
                        "Type": "CALM",
                        "Confidence": 92.87109375
                    },
                    {
                        "Type": "SAD",
                        "Confidence": 3.228759765625
                    },
                    {
                        "Type": "ANGRY",
                        "Confidence": 0.29735565185546875
                    },
                    {
                        "Type": "DISGUSTED",
                        "Confidence": 0.006473064422607422
                    },
                    {
                        "Type": "CONFUSED",
                        "Confidence": 0.002008676528930664
                    },
                    {
                        "Type": "FEAR",
                        "Confidence": 0.000286102294921875
                    },
                    {
                        "Type": "HAPPY",
                        "Confidence": 0.0
                    },
                    {
                        "Type": "SURPRISED",
                        "Confidence": 0.0
                    }
                ],
                "Landmarks": [
                    {
                        "Type": "eyeLeft",
                        "X": 0.39731690287590027,
                        "Y": 0.3498865067958832
                    },
                    {
                        "Type": "eyeRight",
                        "X": 0.6099745631217957,
                        "Y": 0.34791654348373413
                    },
                    {
                        "Type": "mouthLeft",
                        "X": 0.4155678451061249,
                        "Y": 0.5226225852966309
                    },
                    {
                        "Type": "mouthRight",
                        "X": 0.5925053358078003,
                        "Y": 0.521000325679779
                    },
                    {
                        "Type": "nose",
                        "X": 0.49771183729171753,
                        "Y": 0.4362296164035797
                    },
                    {
                        "Type": "leftEyeBrowLeft",
                        "X": 0.3193196952342987,
                        "Y": 0.3116532564163208
                    },
                    {
                        "Type": "leftEyeBrowRight",
                        "X": 0.43887853622436523,
                        "Y": 0.29607662558555603
                    },
                    {
                        "Type": "leftEyeBrowUp",
                        "X": 0.3781270384788513,
                        "Y": 0.28840017318725586
                    },
                    {
                        "Type": "rightEyeBrowLeft",
                        "X": 0.5608415007591248,
                        "Y": 0.2948722243309021
                    },
                    {
                        "Type": "rightEyeBrowRight",
                        "X": 0.6894477605819702,
                        "Y": 0.30813825130462646
                    },
                    {
                        "Type": "rightEyeBrowUp",
                        "X": 0.6237778067588806,
                        "Y": 0.2860446572303772
                    },
                    {
                        "Type": "leftEyeLeft",
                        "X": 0.35991743206977844,
                        "Y": 0.3498852252960205
                    },
                    {
                        "Type": "leftEyeRight",
                        "X": 0.4391413629055023,
                        "Y": 0.35089540481567383
                    },
                    {
                        "Type": "leftEyeUp",
                        "X": 0.3961242735385895,
                        "Y": 0.34081926941871643
                    },
                    {
                        "Type": "leftEyeDown",
                        "X": 0.3980405628681183,
                        "Y": 0.3572733998298645
                    },
                    {
                        "Type": "rightEyeLeft",
                        "X": 0.5675939321517944,
                        "Y": 0.34969934821128845
                    },
                    {
                        "Type": "rightEyeRight",
                        "X": 0.6481897234916687,
                        "Y": 0.3472321033477783
                    },
                    {
                        "Type": "rightEyeUp",
                        "X": 0.609714925289154,
                        "Y": 0.33882376551628113
                    },
                    {
                        "Type": "rightEyeDown",
                        "X": 0.6086876392364502,
                        "Y": 0.35532957315444946
                    },
                    {
                        "Type": "noseLeft",
                        "X": 0.46121904253959656,
                        "Y": 0.45791110396385193
                    },
                    {
                        "Type": "noseRight",
                        "X": 0.5400300621986389,
                        "Y": 0.4572204351425171
                    },
                    {
                        "Type": "mouthUp",
                        "X": 0.5003513693809509,
                        "Y": 0.498134046792984
                    },
                    {
                        "Type": "mouthDown",
                        "X": 0.5014365911483765,
                        "Y": 0.5505503416061401
                    },
                    {
                        "Type": "leftPupil",
                        "X": 0.39731690287590027,
                        "Y": 0.3498865067958832
                    },
                    {
                        "Type": "rightPupil",
                        "X": 0.6099745631217957,
                        "Y": 0.34791654348373413
                    },
                    {
                        "Type": "upperJawlineLeft",
                        "X": 0.2792222797870636,
                        "Y": 0.3599644899368286
                    },
                    {
                        "Type": "midJawlineLeft",
                        "X": 0.32310718297958374,
                        "Y": 0.5449314117431641
                    },
                    {
                        "Type": "chinBottom",
                        "X": 0.504771888256073,
                        "Y": 0.6416449546813965
                    },
                    {
                        "Type": "midJawlineRight",
                        "X": 0.7005577087402344,
                        "Y": 0.5416383147239685
                    },
                    {
                        "Type": "upperJawlineRight",
                        "X": 0.7440629005432129,
                        "Y": 0.3556252121925354
                    }
                ],
                "Pose": {
                    "Roll": -1.772121787071228,
                    "Yaw": -3.259683132171631,
                    "Pitch": 4.63896369934082
                },
                "Quality": {
                    "Brightness": 95.20903015136719,
                    "Sharpness": 98.564208984375
                },
                "Confidence": 99.9988784790039,
                "FaceOccluded": {
                    "Value": false,
                    "Confidence": 99.87215423583984
                },
                "EyeDirection": {
                    "Yaw": -1.3044263124465942,
                    "Pitch": -14.332473754882812,
                    "Confidence": 99.13139343261719
                }
            }
        }
    ],
    "FaceModelVersion": "7.0",
    "UnindexedFaces": []
}
(END)

これを登録したい枚数分、繰り返します。

3.コレクションにユーザーベクトルを作成する

ここで追加したUserIDは先程登録した顔と紐付けるため、メモをしておいてください。
トークンについては今回手動で固定の値をつけていますが、複数ユーザーを作成する場合などはそれぞれ別の文字列を付けてあげてください。

collection-id: コレクション名
user-id: 作成する一意のユーザーID
client-request-token: 同じリクエストが複数処理されないようにつける、任意文字列(^[a-zA-Z0-9-_]+$)のトークン

aws rekognition create-user --user-id user0 --collection-id test-rekognition-SearchUsers --region ap-northeast-1 \
--client-request-token test000

実行後は特に実行結果が出ないため、リストコマンドで確認してみます。

aws rekognition list-users --collection-id test-rekognition-SearchUsers

4.ユーザーベクトルと顔ベクトルのIDを関連付ける

まず、FaceIDを確認します。
ここで先ほど付けることを推奨したExternalImageIdを使用します。

aws rekognition list-faces --collection-id test-rekognition-SearchUsers | jq '.Faces[] | select( .ExternalImageId == "tanuki") | .FaceId'

あとは出力されたFaceIDとUserIDを紐付けてあげるだけです。
face-idsはスペース区切りで一度に複数指定することができます。

aws rekognition associate-faces --user-id user0 \
--face-ids 1eee925c-57d7-4cf6-bb74-d6e3f5891dc9 66e782a1-ce11-4475-9f9f-c74521c70e3d e66169aa-8810-43fa-af6c-e7aacf088ad5 8b51aa43-7522-40b7-9cb2-c1e99a12e7d1 73d9ecf7-999f-404c-8a0b-77fbbb2f4a87 \
--collection-id test-rekognition-SearchUsers --region ap-northeast-1

FaceIdの一覧を出すことで、UserIDと紐付けができているかを確認します。

aws rekognition list-faces --collection-id test-rekognition-SearchUsers

これでカスタムコレクションの準備は完了です。
私の顔データを持つuser0が誕生しました。

テスト

比較写真用の写真を使用し、カスタムコレクション内のユーザー検索をしてみます。

使用するAPIは、SearchUsersByImageです。
このAPIは指定された画像の顔ベクトルが、カスタムコレクションに登録されている顔ベクトルとの一致率が高い順にUserIDを返します。

Searching for users (image) - Amazon Rekognition

コレクションに追加した顔ベクトルはすべて同じ人間の写真ですが、髪型がそれぞれ違う画像を使用しています。

Name: 顔を検索する画像ファイルのキー

aws rekognition search-users-by-image --image '{"S3Object":{"Bucket":"test-rekognition-input","Name":"test.jpg"}}' \
--collection-id test-rekognition-SearchUsers --region ap-northeast-1

user0との一致度が"99.9984359741211"でした。
99%を超えていたら、高い精度で本人と言えるでしょう。

お片付け

APIについては使用した分だけですが、カスタムコレクションはそのまま残すとストレージ料金がかかるので、今後使う予定がない場合は削除しておきましょう。

aws rekognition delete-collection --collection-id test-rekognition-SearchUsers

ステータスコードが200で返ってきたら削除完了です。

終わりに

本記事を読んで、Amazon Rekognitionに詳しくなれたでしょうか?
中村さんのアルティメット理解の記事が出たときは、アルティメット理解過ぎて入門ブログで書くことがないかもな……と思っていたのですが、ギリギリ書ける部分があって良かったです。
なかなか触れる機会が少ないサービスかもしれませんが、できることが多いので是非遊んでみてください。

以上、『AWS 入門ブログリレー 2024』の18日目のエントリ『Amazon Rekognition』編でした。 次回、2024/04/12 は弊社 しばた による「AWS App Runner 編」の予定です!