目的 & コレクション機能の概要
学校の集合写真で、誰が写っているのかを認識するために、顔比較の機能を実装したく、AWS Rekognitionの機能を目につけた。
意外と公式ドキュメント以外の資料があんまり為、ここで自分で整理した。
実すごく使いやすい、単純な処理なので、画像認識やAWSに興味がある初心者でもぜひ試してみましょう!
AWSの権限設定は面倒なことがあるかもしれないですが、今回の内容とあんまり直接の関係がないため割愛させてください。
16名の学生があるクラス(イメージ)
これが元データとして、コレクションに事前に一枚一枚顔を登録する
登録する際に、ラベルを付ける
認識した顔に付けたラベルの結果として返してくる。
(例)「山田」「田中」「石井」「高橋」「鈴木」「山下」「木村」「上村」
アーキテクチャ
S3
選んだ理由
1.Lambdaからそのままファイル読み込みやすい
2.Lambdaのトリガーとして起こすため
3.Rekognition機能もS3からファイル直接読むことができる
機能
Webサーバでアップロードした際、Lambdaのトリガーになり、写真ファイルの読み込み
Lambda
選んだ理由
Rekognition、DynamoDBとセットとして使う
機能
1.アップロードした写真データをRekognitionに渡す
2.Rekognitionの処理結果をDynamoDBに書き込む
Rekognition
機能
1.顔の元データを登録
2.検知した顔のラベル付け、結果をLambdaに返す
DynamoDB
選んだ理由
LambdaとRDS(MySQL)は相性が良くない,なぜなら、RDSの同時接続に耐えられないから
参考記事
Lambda+RDSはアンチパターン
機能
Rekognitionの処理結果を格納する
事前準備
この部分はaws-cliを使った
コレクションの作成
> aws rekognition create-collection --collection-id "[自分のコレクション名]"
成功した結果
{
"StatusCode": 200,
"CollectionArn": "aws:rekognition:ap-northeast-1:570955322844:collection/[自分のコレクション名]",
"FaceModelVersion": "4.0"
}
作成したコレクションの一覧を見る
> aws rekognition list-collections
{
"CollectionIds": [
"sample",
"[自分のコレクション名]"
],
"FaceModelVersions": [
"4.0",
"4.0"
]
}
FaceModelVersionsにある"4.0"は現在使っている学習済みのモデルバージョンです。
その他のコマンド
コレクションに登録した顔の一覧
aws rekognition list-faces --collection-id "[自分のコレクション名]"
コレクションに登録した顔を削除 (間違いて登録する際にはこれで削除できる)
aws rekognition delete-faces
コレクションに顔を登録 (これはlambdaでやる)
aws rekognition index-faces --image
メイン処理
S3のファイルのパスをチェックし、それぞれです応じて処理を分ける
- コレクションに顔を登録処理 collectionフォルダーの物はindex_facesを呼ぶ
- 顔識別処理 collection以外の処理はsearch_faces_by_image
- search_faces_by_imageの結果insertDynamoDBを呼ぶ
from __future__ import print_function
import boto3
from boto3.dynamodb.conditions import Key, Attr
from decimal import Decimal
import json
import urllib.parse
rekognition = boto3.client('rekognition')
dynamodb = boto3.resource('dynamodb')
def lambda_handler(event, context):
# bucketから取り出し
bucket = event['Records'][0]['s3']['bucket']['name']
key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf8')
# パスからフォルダー名を取り出す
folder_str = key.split("/", 1)
# コレクションのフォルダーにアップロードしたものであれば、コレクション登録ロジックに入る
# 僕の場合に'collection'というフォルダーにした
if folder_str[0] == 'collection': # <--- フォルダー名
str = folder_str[1].split("_", 1)
# コレクションに顔を登録処理
index_faces(bucket, key, str[0])
return
# 顔識別処理
response = search_faces_by_image(bucket, key)
str = key.split("_", 1)
faces = response['FaceMatches']
# DynamoDBに書き込む
table = dynamodb.Table('[DYNAMODBのテーブル名]')
for face in faces:
# ExternalImageIdは自分で事前登録したラベル名
insertDynamoDB(table, str[0], face['Face']['ExternalImageId'])
return
コレクションに顔を登録処理
rekognition.index_faces()でS3のファイルを登録する
S3にアップロードしたファイルはこの子にしてみる
アップロードファイルパス
collection/青山らら.png として
ラベルは「青山らら」にする
def index_faces(bucket, key, labelId):
response = rekognition.index_faces(CollectionId="[コレクションID]",
Image={"S3Object": {"Bucket": bucket, "Name": key}},
ExternalImageId=labelId,
MaxFaces=1,
QualityFilter="AUTO",
DetectionAttributes=['ALL'])
return response
レスポンス
一部抜粋
{
"FaceId": "262a08f3-b7ba-474d-b1a2-6530073ece54", <-- 登録成功したID
"BoundingBox": {
"Width": 0.2804960012435913,
"Height": 0.5630229711532593,
"Left": 0.41263899207115173,
"Top": 0.23804500699043274
},
"ImageId": "f3b8191a-27ff-326d-ac21-1c49d18642b1",
"ExternalImageId": "青山らら", <-- ラベル
"Confidence": 100.0
}
顔識別処理
rekognition.search_faces_by_image()でS3のファイルを検知する
アップロードしたファイルはこちら「青山らら」ちゃんと誰か知らない人
パスは特にフォルダー付けない、 ******.pngで大丈夫
def search_faces_by_image(bucket, key):
threshold = 70 #類似度、 これより低い結果をレスポンスに含めない、 デフォルトは80
maxFaces = 15 #最大検知可能な顔数
response = rekognition.search_faces_by_image(CollectionId="[コレクションID]",
Image={"S3Object": {"Bucket": bucket, "Name": key}},
FaceMatchThreshold=threshold,
MaxFaces=maxFaces)
return response
レスポンス
一部抜粋
{
"SearchedFaceBoundingBox": {
"Width": 0.15396510064601898,
"Height": 0.27054980397224426,
"Left": 0.704308032989502,
"Top": 0.18812672793865204
},
"SearchedFaceConfidence": 100,
"FaceMatches": [ <--- この配列に一致した顔の結果が帰ってくる
{
"Similarity": 74.76554870605469, <--- 類似度、この場合は74%
"Face": {
"FaceId": "262a08f3-b7ba-474d-b1a2-6530073ece54", <-- 先程の登録した顔のID
"BoundingBox": {
"Width": 0.2804960012435913,
"Height": 0.5630229711532593,
"Left": 0.41263899207115173,
"Top": 0.23804500699043274
},
"ImageId": "f3b8191a-27ff-326d-ac21-1c49d18642b1",
"ExternalImageId": "青山らら", <--- 登録したラベルの内容
"Confidence": 100
}
}
],
"FaceModelVersion": "4.0",
}
類似度に関して、ドキュメントには多くの法律執行のユースケースでは、偶発的な誤認識を減らすため、99% 以上の高いしきい値を使用することをお勧めします。
DynamoDB
LambdaからDynamoDBに書き込むもすごく簡単 put_itemを呼ぶだけ
def insertDB(table, student_id, name):
table.put_item(
Item = {
"student_id": student_id, <-- 学生のID
"name": name <-- ExternalImageId、登録したラベルの内容、この場合は「青山らら」
}
)
全体のコード
おまけ、顔認識に関しての話
こちらの去年年末の記事ですが、グーグル、顔認識APIの提供を当面控えると言明
https://japan.cnet.com/article/35130142/
AWSの顔認識のAPIの提供も控えろうという声もあると聞いたことがある。
その日になると、こちらのAPIを使ってみようか
最近AI分野に力に入れている中国、非常に強力なAPIを無料公開している
顔、ボティ、ジェスチャーなの幅広いの画像認識機能がある
Face++ API
https://www.faceplusplus.com/