はじめに
AWS初心者です。AWSの基本知識をキャッチアップすべく、色々触りながら学習中です。
今回は生成AIのサービスであるAmazon Rekognitionを触ってみました。初心者向けの内容となっておりますので、温かい目で見て頂ければ幸いです。
想定読者
- AWSの生成AIサービスに興味があり、手っ取り早く機能を試してみたい方
概要
本ページでは、Amazon Rekognitionの機能を利用するための手順を一から記載しています。本チュートリアル通りに実施すれば10分程度で実行結果まで確認できるようになっています。興味がある方は、ぜひお試しください。
システム構成
今回構築した構成です。
ローカル上のプログラム(Python)からAWS CLIコマンドでAmazon Rekognitionの機能を利用しています。解析する画像はS3に格納して行います。
試したRekognitionの機能
- 顔検出
- 顔比較
- テキスト検出
事前準備
※AWSアカウントが必要になります。私は12ヵ月無料枠の環境を利用しました。
VS Codeのインストール
VS Codeインストーラページからインストーラを入手し、インストールします。お使いのOS環境に合わせてインストーラを選択してください。
Pythonのインストール
python公式ページからPythonのインストーラを入手し、インストールします。バージョンは3.13.0にします(試した時点での最新バージョン)。
参考ページ:Windows版Pythonのインストール
AWS CLIのインストール
AWS公式ドキュメントからインストーラを入手し、インストールします。お使いのOS環境に合わせてインストーラを選択してください。
コマンドプロンプトを起動し、"aws --version"と入力します。
以下のように返ってくれば問題なくインストールされています。
C:> aws --version
aws-cli/2.22.3 Python/3.13.0 Windows/11 exe/AMD64
アクセスキー/シークレットキーの発行と取得
AWS CLIへの設定
コマンドプロンプトを起動し、"aws configure"と入力します。
前述で作成したアクセスキーとシークレットアクセスキーを入力します。
※リージョンはお使いのリージョンを入力してください。東京リージョンの場合は、"ap-northeast-1"です。default output formatは任意で構いません。
AWS Access Key ID [None]: アクセスキーを入力
AWS Secret Access Key [None]: シークレットキーを入力
Default region name [None]: ap-northeast-1
Default output format [None]: json
補足:AWS CLIのConfigファイルは、「C:\Users\ユーザー名.aws」配下に保存されています。
S3へ画像をアップロード
Rekognitionに解析してもらう画像ファイルを事前にS3へアップロードします。
バケット名や画像のファイル名は任意で構いません。
コードの実装
任意の作業フォルダーを用意し、VS Codeを起動します。
外部ライブラリのインストール
VS Code以下のコマンドを実行し、pythonからAWSのサービスを操作する便利なライブラリである「boto3」をインストールします。
pip install boto3 -t .
顔検出のサンプルコード
任意の名前でpyファイルを作成し、以下のサンプルコードを貼り付けます。
import boto3
import json
import subprocess
def detect_faces(bucket_name, object_key, region):
try:
# AWS CLIコマンドを構築
command = [
"aws", "rekognition", "detect-faces",
"--image", f"S3Object={{Bucket={bucket_name},Name={object_key}}}",
"--attributes", "ALL",
"--region", region
]
# コマンドを実行
result = subprocess.run(command, capture_output=True, text=True, check=True)
# 結果をパース
output = json.loads(result.stdout)
return output
except subprocess.CalledProcessError as e:
print(f"Error occurred: {e.stderr}")
return None
if __name__ == "__main__":
# 顔検出する画像情報
bucket_name = "your-bucket-name"
object_key = "your-image-file"
region = "your-region"
# 顔検出の実行
response = detect_faces(bucket_name,object_key,region)
# 結果を表示
if response:
print("顔検出Start--------------------------------------")
print(json.dumps(response, indent=4))
顔検出の実行結果
自分の顔写真を素材に実行した結果が以下です。
各分析項目の結果が出力されていることが分かります。"confidence"は信頼度です。年齢が10歳以上若い結果となり、ちょっと嬉しかったです(笑)
顔検出Start--------------------------------------
{
"FaceDetails": [
{
"BoundingBox": {
"Width": 0.2230978012084961,
"Height": 0.5002204775810242,
"Left": 0.46507692337036133,
"Top": 0.16522598266601562
},
"AgeRange": {
"Low": 20,
"High": 26
},
"Smile": {
"Value": false,
"Confidence": 99.18939208984375
},
"Eyeglasses": {
"Value": true,
"Confidence": 99.99813079833984
},
"Sunglasses": {
"Value": false,
"Confidence": 99.79135131835938
},
"Gender": {
"Value": "Male",
"Confidence": 99.98809814453125
},
"Beard": {
"Value": false,
"Confidence": 75.10460662841797
},
"Mustache": {
"Value": false,
"Confidence": 98.32788848876953
},
"EyesOpen": {
"Value": true,
"Confidence": 99.99903869628906
},
"MouthOpen": {
"Value": false,
"Confidence": 95.95695495605469
},
"Emotions": [
{
"Type": "SURPRISED",
"Confidence": 90.26863861083984
},
{
"Type": "CALM",
"Confidence": 13.5302734375
},
{
"Type": "CONFUSED",
"Confidence": 9.546915054321289
},
{
"Type": "DISGUSTED",
"Confidence": 0.5580902099609375
},
{
"Type": "ANGRY",
"Confidence": 0.14734268188476562
},
{
"Type": "SAD",
"Confidence": 0.13952255249023438
},
{
"Type": "HAPPY",
"Confidence": 0.04507700353860855
},
{
"Type": "FEAR",
"Confidence": 0.015079975128173828
}
],
"Landmarks": [
{
"Type": "eyeLeft",
"X": 0.5325515270233154,
"Y": 0.33781003952026367
},
{
"Type": "eyeRight",
"X": 0.6265299916267395,
"Y": 0.33709716796875
},
{
"Type": "mouthLeft",
"X": 0.5391842126846313,
"Y": 0.5301750898361206
},
{
"Type": "mouthRight",
"X": 0.6175442337989807,
"Y": 0.5294550061225891
},
{
"Type": "nose",
"X": 0.5827585458755493,
"Y": 0.4313817024230957
},
{
"Type": "leftEyeBrowLeft",
"X": 0.49577420949935913,
"Y": 0.29806384444236755
},
{
"Type": "leftEyeBrowRight",
"X": 0.5533677935600281,
"Y": 0.2786509692668915
},
{
"Type": "leftEyeBrowUp",
"X": 0.5253135561943054,
"Y": 0.27044862508773804
},
{
"Type": "rightEyeBrowLeft",
"X": 0.6074922680854797,
"Y": 0.2783825099468231
},
{
"Type": "rightEyeBrowRight",
"X": 0.6594663858413696,
"Y": 0.2968357801437378
},
{
"Type": "rightEyeBrowUp",
"X": 0.63421231508255,
"Y": 0.26986587047576904
},
{
"Type": "leftEyeLeft",
"X": 0.5150701999664307,
"Y": 0.3384076654911041
},
{
"Type": "leftEyeRight",
"X": 0.550912618637085,
"Y": 0.33978036046028137
},
{
"Type": "leftEyeUp",
"X": 0.5323578119277954,
"Y": 0.3278612196445465
},
{
"Type": "leftEyeDown",
"X": 0.532738447189331,
"Y": 0.3464629054069519
},
{
"Type": "rightEyeLeft",
"X": 0.6077018976211548,
"Y": 0.33937618136405945
},
{
"Type": "rightEyeRight",
"X": 0.6425749063491821,
"Y": 0.33744096755981445
},
{
"Type": "rightEyeUp",
"X": 0.6268326044082642,
"Y": 0.32717594504356384
},
{
"Type": "rightEyeDown",
"X": 0.625895619392395,
"Y": 0.34573185443878174
},
{
"Type": "noseLeft",
"X": 0.5621199011802673,
"Y": 0.45764070749282837
},
{
"Type": "noseRight",
"X": 0.5971183776855469,
"Y": 0.4574120044708252
},
{
"Type": "mouthUp",
"X": 0.5797541737556458,
"Y": 0.5020350813865662
},
{
"Type": "mouthDown",
"X": 0.5787949562072754,
"Y": 0.5609168410301208
},
{
"Type": "leftPupil",
"X": 0.5325515270233154,
"Y": 0.33781003952026367
},
{
"Type": "rightPupil",
"X": 0.6265299916267395,
"Y": 0.33709716796875
},
{
"Type": "upperJawlineLeft",
"X": 0.4695703089237213,
"Y": 0.3562681972980499
},
{
"Type": "midJawlineLeft",
"X": 0.48854678869247437,
"Y": 0.5592750906944275
},
{
"Type": "chinBottom",
"X": 0.576548159122467,
"Y": 0.6626726984977722
},
{
"Type": "midJawlineRight",
"X": 0.6558901071548462,
"Y": 0.5582041144371033
},
{
"Type": "upperJawlineRight",
"X": 0.675373375415802,
"Y": 0.35490894317626953
}
],
"Pose": {
"Roll": 2.1099250316619873,
"Yaw": 2.4682259559631348,
"Pitch": 9.40732479095459
},
"Quality": {
"Brightness": 61.10771942138672,
"Sharpness": 89.85481262207031
},
"Confidence": 99.99971008300781,
"FaceOccluded": {
"Value": false,
"Confidence": 99.98025512695312
},
"EyeDirection": {
"Yaw": -2.760011911392212,
"Pitch": -2.4524102210998535,
"Confidence": 99.99853515625
}
}
]
}
顔比較のサンプルコード
任意の名前でpyファイルを作成し、以下のサンプルコードを貼り付けます。
import boto3
import json
import subprocess
def compare_faces(bucket_name, source_image, target_image, region):
try:
# AWS CLIコマンドを構築
command = [
"aws", "rekognition", "compare-faces",
"--source-image", f"S3Object={{Bucket={bucket_name},Name={source_image}}}",
"--target-image", f"S3Object={{Bucket={bucket_name},Name={target_image}}}",
"--region", region
]
# コマンドを実行
result = subprocess.run(command, capture_output=True, text=True, check=True)
# 結果をパース
output = json.loads(result.stdout)
return output
except subprocess.CalledProcessError as e:
print(f"Error occurred: {e.stderr}")
return None
if __name__ == "__main__":
# 比較する画像情報
bucket_name = "your-bucket-name"
source_image = "your-source-image-file"
target_image = "your-target-image-file"
region = "your-region"
# 顔比較の実行
response = compare_faces(bucket_name,source_image, target_image,region)
# 結果を表示
if response:
print("顔比較Start--------------------------------------")
print(json.dumps(response, indent=4))
顔比較の実行結果
自分の顔写真を二つ指定実行した結果が以下です。
FaceMatchesには同一人物と判断した顔の情報が設定されるため、同一人物と判断した結果を得ることができました。
顔比較Start--------------------------------------
{
"SourceImageFace": {
"BoundingBox": {
"Width": 0.2230978012084961,
"Height": 0.5002204775810242,
"Left": 0.46507692337036133,
"Top": 0.16522598266601562
},
"Confidence": 99.99971008300781
},
"FaceMatches": [
{
"Similarity": 99.99568176269531,
"Face": {
"BoundingBox": {
"Width": 0.2408052384853363,
"Height": 0.553041398525238,
"Left": 0.4423290193080902,
"Top": 0.19057489931583405
},
"Confidence": 99.9998550415039,
"Landmarks": [
{
"Type": "eyeLeft",
"X": 0.5220925211906433,
"Y": 0.4021090567111969
},
{
"Type": "eyeRight",
"X": 0.619428277015686,
"Y": 0.3885900676250458
},
{
"Type": "mouthLeft",
"X": 0.5397889018058777,
"Y": 0.6160306930541992
},
{
"Type": "mouthRight",
"X": 0.6208064556121826,
"Y": 0.6044422388076782
},
{
"Type": "nose",
"X": 0.5855122804641724,
"Y": 0.5041868686676025
}
],
"Pose": {
"Roll": -2.925478935241699,
"Yaw": 7.480830669403076,
"Pitch": 1.7374693155288696
},
"Quality": {
"Brightness": 63.523780822753906,
"Sharpness": 83.14741516113281
}
}
}
],
"UnmatchedFaces": []
}
テキスト検出のサンプルコード
任意の名前でpyファイルを作成し、以下のサンプルコードを貼り付けます。
import boto3
import json
import subprocess
def detect_text(bucket_name, object_key, region):
try:
# AWS CLIコマンドを構築
command = ["aws", "rekognition", "detect-text", "--region", region]
command.extend([
"--image", f"S3Object={{Bucket={bucket_name},Name={object_key}}}"
])
# コマンドの実行
result = subprocess.run(command, capture_output=True, text=True, check=True)
# 結果をJSON形式に変換して返す
output = json.loads(result.stdout)
return output
except subprocess.CalledProcessError as e:
print(f"Error occurred: {e.stderr}")
return None
if __name__ == "__main__":
# テキスト検出する画像情報
bucket_name = "your-bucket-name"
object_key = "your-image-file"
region = "your-region"
# テキスト検出の実行
response = detect_text(bucket_name,object_key,region)
# 結果を表示
if response:
print("テキスト検出Start--------------------------------")
print(json.dumps(response, indent=4))
テキスト検出の実行結果
タバコのパッケージ画像を素材に実行した結果が以下です。
日本語の説明部分は抽出されませんでしたが、それ以外は検出されました。
テキスト検出Start--------------------------------
{
"TextDetections": [
{
"DetectedText": "LUCKY",
"Type": "LINE",
"Id": 0,
"Confidence": 99.98216247558594,
"Geometry": {
"BoundingBox": {
"Width": 0.11612434685230255,
"Height": 0.08524186909198761,
"Left": 0.6166348457336426,
"Top": 0.14485891163349152
},
"Polygon": [
{
"X": 0.6166348457336426,
"Y": 0.16158674657344818
},
{
"X": 0.7295476198196411,
"Y": 0.14485891163349152
},
{
"X": 0.7327591776847839,
"Y": 0.21337294578552246
},
{
"X": 0.6198464632034302,
"Y": 0.23010078072547913
}
]
}
},
{
"DetectedText": "STRIKE",
"Type": "LINE",
"Id": 1,
"Confidence": 99.90182495117188,
"Geometry": {
"BoundingBox": {
"Width": 0.12843266129493713,
"Height": 0.09297739714384079,
"Left": 0.6150137186050415,
"Top": 0.21373677253723145
},
"Polygon": [
{
"X": 0.6150137186050415,
"Y": 0.23376454412937164
},
{
"X": 0.7397400140762329,
"Y": 0.21373677253723145
},
{
"X": 0.7434463500976562,
"Y": 0.28668639063835144
},
{
"X": 0.6187199950218201,
"Y": 0.30671417713165283
}
]
}
},
{
"DetectedText": "DARK",
"Type": "LINE",
"Id": 2,
"Confidence": 99.78257751464844,
"Geometry": {
"BoundingBox": {
"Width": 0.06374196708202362,
"Height": 0.06048346683382988,
"Left": 0.5189576745033264,
"Top": 0.4192069470882416
},
"Polygon": [
{
"X": 0.5189576745033264,
"Y": 0.4314168691635132
},
{
"X": 0.5796256065368652,
"Y": 0.4192069470882416
},
{
"X": 0.5826996564865112,
"Y": 0.46748051047325134
},
{
"X": 0.5220316648483276,
"Y": 0.47969043254852295
}
]
}
},
{
"DetectedText": "for glo hyper",
"Type": "LINE",
"Id": 3,
"Confidence": 99.21173095703125,
"Geometry": {
"BoundingBox": {
"Width": 0.11426039040088654,
"Height": 0.05783861130475998,
"Left": 0.6430965662002563,
"Top": 0.437660813331604
},
"Polygon": [
{
"X": 0.6430965662002563,
"Y": 0.45954430103302
},
{
"X": 0.7551349401473999,
"Y": 0.437660813331604
},
{
"X": 0.7573570013046265,
"Y": 0.47361594438552856
},
{
"X": 0.6453186273574829,
"Y": 0.4954994320869446
}
]
}
},
{
"DetectedText": "TOBACCO",
"Type": "LINE",
"Id": 4,
"Confidence": 98.97401428222656,
"Geometry": {
"BoundingBox": {
"Width": 0.06173715740442276,
"Height": 0.04313506931066513,
"Left": 0.5227578282356262,
"Top": 0.47395646572113037
},
"Polygon": [
{
"X": 0.5227578282356262,
"Y": 0.4865482747554779
},
{
"X": 0.5824565887451172,
"Y": 0.47395646572113037
},
{
"X": 0.584494948387146,
"Y": 0.5044997334480286
},
{
"X": 0.524796187877655,
"Y": 0.5170915126800537
}
]
}
},
{
"DetectedText": ".......",
"Type": "LINE",
"Id": 5,
"Confidence": 24.066303253173828,
"Geometry": {
"BoundingBox": {
"Width": 0.2355966567993164,
"Height": 0.11057349294424057,
"Left": 0.5456517934799194,
"Top": 0.7508428692817688
},
"Polygon": [
{
"X": 0.5456517934799194,
"Y": 0.8122059106826782
},
{
"X": 0.7771207094192505,
"Y": 0.7508428692817688
},
{
"X": 0.7812484502792358,
"Y": 0.8000532984733582
},
{
"X": 0.5497795939445496,
"Y": 0.8614163994789124
}
]
}
},
{
"DetectedText": "MOJGE",
"Type": "LINE",
"Id": 6,
"Confidence": 31.495227813720703,
"Geometry": {
"BoundingBox": {
"Width": 0.08697931468486786,
"Height": 0.05036112293601036,
"Left": 0.07779115438461304,
"Top": 0.9496389031410217
},
"Polygon": [
{
"X": 0.07779115438461304,
"Y": 0.9672706723213196
},
{
"X": 0.16209307312965393,
"Y": 0.9496389031410217
},
{
"X": 0.1647704690694809,
"Y": 0.9900972843170166
},
{
"X": 0.08046855032444,
"Y": 1.0
}
]
}
},
{
"DetectedText": "LUCKY",
"Type": "WORD",
"Id": 7,
"ParentId": 0,
"Confidence": 99.98216247558594,
"Geometry": {
"BoundingBox": {
"Width": 0.11612434685230255,
"Height": 0.08524186909198761,
"Left": 0.6166348457336426,
"Top": 0.14485891163349152
},
"Polygon": [
{
"X": 0.6166348457336426,
"Y": 0.16158674657344818
},
{
"X": 0.7295476198196411,
"Y": 0.14485891163349152
},
{
"X": 0.7327591776847839,
"Y": 0.21337294578552246
},
{
"X": 0.6198464632034302,
"Y": 0.23010078072547913
}
]
}
},
{
"DetectedText": "STRIKE",
"Type": "WORD",
"Id": 8,
"ParentId": 1,
"Confidence": 99.90182495117188,
"Geometry": {
"BoundingBox": {
"Width": 0.12843266129493713,
"Height": 0.09297739714384079,
"Left": 0.6150137186050415,
"Top": 0.21373677253723145
},
"Polygon": [
{
"X": 0.6150137186050415,
"Y": 0.23376451432704926
},
{
"X": 0.7397400140762329,
"Y": 0.21373677253723145
},
{
"X": 0.7434463500976562,
"Y": 0.28668642044067383
},
{
"X": 0.6187199950218201,
"Y": 0.30671417713165283
}
]
}
},
{
"DetectedText": "DARK",
"Type": "WORD",
"Id": 9,
"ParentId": 2,
"Confidence": 99.78257751464844,
"Geometry": {
"BoundingBox": {
"Width": 0.06374196708202362,
"Height": 0.06048346683382988,
"Left": 0.5189576745033264,
"Top": 0.4192069470882416
},
"Polygon": [
{
"X": 0.5189576745033264,
"Y": 0.4314168691635132
},
{
"X": 0.5796256065368652,
"Y": 0.4192069470882416
},
{
"X": 0.5826996564865112,
"Y": 0.46748051047325134
},
{
"X": 0.5220316648483276,
"Y": 0.47969043254852295
}
]
}
},
{
"DetectedText": "for",
"Type": "WORD",
"Id": 10,
"ParentId": 3,
"Confidence": 99.12591552734375,
"Geometry": {
"BoundingBox": {
"Width": 0.03056039847433567,
"Height": 0.036781564354896545,
"Left": 0.643120527267456,
"Top": 0.4539688229560852
},
"Polygon": [
{
"X": 0.643120527267456,
"Y": 0.4599340260028839
},
{
"X": 0.6716416478157043,
"Y": 0.4539688229560852
},
{
"X": 0.6736809015274048,
"Y": 0.48478516936302185
},
{
"X": 0.6451598405838013,
"Y": 0.49075037240982056
}
]
}
},
{
"DetectedText": "glo",
"Type": "WORD",
"Id": 11,
"ParentId": 3,
"Confidence": 99.29743194580078,
"Geometry": {
"BoundingBox": {
"Width": 0.031035995110869408,
"Height": 0.03989512100815773,
"Left": 0.6733825206756592,
"Top": 0.449692040681839
},
"Polygon": [
{
"X": 0.6733825206756592,
"Y": 0.45560595393180847
},
{
"X": 0.7022129893302917,
"Y": 0.449692040681839
},
{
"X": 0.7044185400009155,
"Y": 0.48367321491241455
},
{
"X": 0.6755880117416382,
"Y": 0.4895871579647064
}
]
}
},
{
"DetectedText": "hyper",
"Type": "WORD",
"Id": 12,
"ParentId": 3,
"Confidence": 99.21185302734375,
"Geometry": {
"BoundingBox": {
"Width": 0.05207843706011772,
"Height": 0.04427964985370636,
"Left": 0.7052717208862305,
"Top": 0.43908944725990295
},
"Polygon": [
{
"X": 0.7052717208862305,
"Y": 0.4489516317844391
},
{
"X": 0.7551990747451782,
"Y": 0.43908944725990295
},
{
"X": 0.7573501467704773,
"Y": 0.4735069274902344
},
{
"X": 0.7074228525161743,
"Y": 0.4833691120147705
}
]
}
},
{
"DetectedText": "TOBACCO",
"Type": "WORD",
"Id": 13,
"ParentId": 4,
"Confidence": 98.97401428222656,
"Geometry": {
"BoundingBox": {
"Width": 0.06173715740442276,
"Height": 0.04313506931066513,
"Left": 0.5227578282356262,
"Top": 0.47395646572113037
},
"Polygon": [
{
"X": 0.5227578282356262,
"Y": 0.4865482747554779
},
{
"X": 0.5824565887451172,
"Y": 0.47395646572113037
},
{
"X": 0.584494948387146,
"Y": 0.5044997334480286
},
{
"X": 0.524796187877655,
"Y": 0.5170915126800537
}
]
}
},
{
"DetectedText": ".......",
"Type": "WORD",
"Id": 14,
"ParentId": 5,
"Confidence": 24.066303253173828,
"Geometry": {
"BoundingBox": {
"Width": 0.17558260262012482,
"Height": 0.09466434270143509,
"Left": 0.6056658625602722,
"Top": 0.7508423924446106
},
"Polygon": [
{
"X": 0.6056658625602722,
"Y": 0.796296238899231
},
{
"X": 0.7771206498146057,
"Y": 0.7508423924446106
},
{
"X": 0.7812484502792358,
"Y": 0.8000528216362
},
{
"X": 0.6097937226295471,
"Y": 0.8455067276954651
}
]
}
},
{
"DetectedText": "MOJGE",
"Type": "WORD",
"Id": 15,
"ParentId": 6,
"Confidence": 31.495227813720703,
"Geometry": {
"BoundingBox": {
"Width": 0.08697931468486786,
"Height": 0.05036112293601036,
"Left": 0.07779115438461304,
"Top": 0.9496389031410217
},
"Polygon": [
{
"X": 0.07779115438461304,
"Y": 0.9672706723213196
},
{
"X": 0.16209307312965393,
"Y": 0.9496389031410217
},
{
"X": 0.1647704690694809,
"Y": 0.9900972843170166
},
{
"X": 0.08046855032444,
"Y": 1.0
}
]
}
}
],
"TextModelVersion": "3.0"
}
後始末
アクセスキー/シークレットキーは放置したくなかったので、無効化しておきました。
おわりに
今回、Amazon Rekognitionの一部機能を使ってみて使用方法や結果のイメージや精度を概ねキャッチアップすることができました。出力情報を組み合わせて、いい感じのフロント画面を実装すればそれっぽくなりそうなので試してみよう思います。