AWS Rekognitionの#search_faces_by_image サーバーレスな本人確認をローカルで
2021/11/26
やりたいこと
デバイスのカメラとブラウザを利用してユーザーの顔写真を撮影し、AWSのサーバーレス環境でその顔写真をAWS Rekognitionに問い合わせて本人確認をしたいです。具体的には、予め何人かの顔写真のプールがあって、その中に渡した顔写真と同じ顔があるかという問い合わせをします。
顔写真のプール(コレクション)は既にある状態から進めていきます。 顔のコレクションを作成したい場合はこちらのドキュメントを。(リンク)
今回はS3に画像ファイルを置いておくことにしました。そして、それをLambdaで取得し(getObject)、Rekognitionにこの写真の人物が顔写真プールの中にあるかどうか問い合わせるというプロセスを実装していきます。 なぜ、わざわざ画像をAPI Gatewayに渡さずに、先にS3に置いておくかというと、API Gatewayは転送データ量に対しても料金が発生するからです(Amazon API Gateway の料金)。コスト最適化の対策です。
実装!
とりまSAMで取り急ぎご挨拶(Hello World)
そもそもLambdaが動くことをセットアップ、確認するために、とりまHello Worldします。
詳細はこちらのQiita記事をご参照ください→(AWS SAM ローカルでいろんな角度からhello worldする)
curlでURL叩いてRekognitionする(パラメーターなし)
Lambda
まずはLambdaを編集していきます。 Aws::Rekognition::Client#search_faces_by_imageというメソッドを使います。
require 'json'
# SDK追加
require 'aws-sdk-s3'
require 'aws-sdk-rekognition'
def lambda_handler(event:, context:)
# この辺は後でcurlでパラメータとして渡してあげたい
collection_id = 'app-users' # Rekognitionの顔コレクションのid
bucket = 'face-images' # the bucket name without 's3://'
ok_photo_key = 'user-code-1/kaku_2013.jpg' # OKになるはずの写真
ng_key = 'user-code-2/hyde.jpg' # NGになるはずの写真
client = Aws::Rekognition::Client.new(
region: ENV['AWS_REGION'],
access_key_id: ENV['AWS_ACCESS_KEY_ID'],
secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
)
# 顔認証用に送られた顔写真と同じ顔が、顔collectionの中でマッチするものを取得
response = client.search_faces_by_image({
collection_id: collection_id,
face_match_threshold: 95,
image: {
s3_object: {
bucket: bucket,
name: ok_key, # ng_keyにすればNGの時の動作を確認できる
},
},
max_faces: 5,
})
# ログ確認用
puts response
# responseの中にface_matchesというのがある(後述)
face_ids = response.face_matches.map { |f| f.face.face_id }
result = if face_ids.any?
:success
else
:failure
end
{
statusCode: 200,
body: {
message: result,
}.to_json
}
end
環境変数セット
Rekognitionのclientをnewするときのcredentials(ENV['AWS_ACCESS_KEY_ID']
とか)の渡し方は色々あるようですが、今回はターミナルに変数をセットする方法に落ち着きました。sam local start
をするターミナルで、AWSの認証情報をexport
します。AWS_SECRET_ACCESS_KEY
、AWS_ACCESS_KEY_ID
、AWS_REGION
が必要です。(ドキュメント)
export
したら、printenv
コマンドで環境変数を確認できます。
// 認証情報をexport
$ export AWS_SECRET_ACCESS_KEY=XXX AWS_ACCESS_KEY_ID=XXX AWS_REGION=ap-northeast-1
// 環境変数の確認
$ printenv
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
identify_user
Sample SAM Template for identify_user
Globals:
Function:
Runtime: ruby2.7
Timeout: 30
Resources:
IdentifyUserFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: identify_user/
Handler: app.lambda_handler
Runtime: ruby2.7
Events:
IdentifyUser:
Type: Api
Properties:
Path: /identify_user
Method: post
curlする
さて、準備ができたのでcurlします。 うまくいくとLambdaからのレスポンスが返ってきます。
curl -X POST http://127.0.0.1:3000/identify_user
// response
{"message":"success"}%
レスポンス
Rekognitionの#search_faces_by_imageのレスポンスはこんな感じになっています。
{
message:"
{
:searched_face_bounding_box=>{
:width=>0.1847885549068451,
:height=>0.3748999536037445,
:left=>0.41051727533340454,
:top=>0.26663076877593994
},
:searched_face_confidence=>99.99877166748047,
:face_matches=>[{
:similarity=>99.91470336914062,
:face=>{
:face_id=>"ee1b5c92-7201-4826-8813-07c1e8dd9da9",
:bounding_box=>{
:width=>0.1847890019416809,
:height=>0.3749000132083893,
:left=>0.41051700711250305,
:top=>0.26663100719451904
},
:image_id=>"774f9136-2232-320c-9c3a-5c67f3ed6b7d",
:external_image_id=>nil,
:confidence=>99.9988021850586
}
}],
:face_model_version=>"5.0"
}
"
}
顔写真プールの中から似ている顔写真を見つけた場合のレスポンスには、face_matches
というキーの中にデータが含まれていて、そこにsimilarity
(類似度)やface_id
の情報があります。今と8年前の自分の写真で試してみたのですが、顔一致率99.9%以上で返ってきてビックリしましたw
ちなみに、マッチするものが見つからない場合のresponseはこちら。face_matches
の値が空[]
になっています。
{
message: "
{
:searched_face_bounding_box=>{
:width=>0.23569627106189728,
:height=>0.4746306240558624,
:left=>0.4260195195674896,
:top=>0.15645968914031982
},
:searched_face_confidence=>99.99295806884766,
:face_matches=>[],
:face_model_version=>"5.0"
}
"
}
curlでパラメーターも渡しつつURL叩いてRekognitionする
次は1ステップ進んで、APIにパラメーターで渡して同じことをするというのをしたいと思います。
Lambda
require 'json'
require 'aws-sdk-s3'
require 'aws-sdk-rekognition'
def lambda_handler(event:, context:)
params = JSON.parse(event['body'], symbolize_names: true)
client = Aws::Rekognition::Client.new(
region: ENV['AWS_REGION'],
access_key_id: ENV['AWS_ACCESS_KEY_ID'],
secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
)
# 顔認証用に送られたimageと同じ顔がcollection_idの中でマッチするものを取得
response = client.search_faces_by_image({
collection_id: params[:collection_id],
face_match_threshold: 95,
image: {
s3_object: {
bucket: params[:bucket],
name: params[:ok_photo_key],
},
},
max_faces: 5,
})
puts response
face_ids = response.face_matches.map { |f| f.face.face_id }
result = if face_ids.any?
:success
else
:failure
end
{
statusCode: 200,
body: {
message: result,
}.to_json
}
end
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
identify_user
Sample SAM Template for identify_user
Globals:
Function:
Timeout: 30
Resources:
IdentifyUserFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: identify_user/
Handler: app.lambda_handler
Runtime: ruby2.7
Events:
HelloWorld:
Type: Api
Properties:
Path: /identify_user
Method: post
curl(パラメーターも渡す)
curlコマンドの--dataオプションでパラメータを渡してあげます。
curl -X POST --data '{"collection_id":"app-users","bucket":"face-images","ok_photo_key":"user-code-1/kaku_2013.jpg"' http://127.0.0.1:3005/identify_user
{"message":"success"}%
レスポンス
Lambdaに仕込んでおいたputs response
のログがsam localのサーバーに出ています。
Mounting /Users/username/directory/sam-app/.aws-sam/build/IdentifyUserFunction as /var/task:ro,delegated inside runtime container START RequestId: 93230268-dbd1-451d-9cbb-118c0fff96f0 Version: $LATEST {:searched_face_bounding_box=>{:width=>0.18047305941581726, :height=>0.32837414741516113, :left=>0.3123670816421509, :top=>0.20935456454753876}, :searched_face_confidence=>99.99854278564453, :face_matches=>[{:similarity=>99.91470336914062, :face=>{:face_id=>"ee1b5c92-7201-4826-8813-07c1e8dd9da9", :bounding_box=>{:width=>0.1847890019416809, :height=>0.3749000132083893, :left=>0.41051700711250305, :top=>0.26663100719451904}, :image_id=>"774f9136-2232-320c-9c3a-5c67f3ed6b7d", :external_image_id=>nil, :confidence=>99.9988021850586}}], :face_model_version=>"5.0"} END RequestId: 93230268-dbd1-451d-9cbb-118c0fff96f0 REPORT RequestId: 93230268-dbd1-451d-9cbb-118c0fff96f0 Init Duration: 0.13 ms Duration: 1434.27 ms Billed Duration: 1500 ms Memory Size: 128 MB Max Memory Used: 128 MB No Content-Type given. Defaulting to 'application/json'. 2021-11-11 09:20:06 127.0.0.1 - - [11/Nov/2021 09:20:06] "POST /identify_user HTTP/1.1" 200 - Invoking app.lambda_handler (ruby2.7) Skip pulling image and use local one: amazon/aws-sam-cli-emulation-image-ruby2.7:rapid-1.22.0.
face_matches
の中に:similarity=>99.91470336914062
と入っています。
無事にローカルで、サーバーレス環境で顔認識ができました!:party: