AIとか画像解析とか聞くとなんか難しそうだけど、Amazonが提供しているサービスを使うと簡単に実装できるので、試してみた!
Amazon Rekognitionとは
Amazon Rekognition では、イメージ分析とビデオ分析をアプリケーションに簡単に追加することができます。Amazon Rekognition API にイメージやビデオを指定するだけで、このサービスによってモノ、人物、テキスト、シーン、アクティビティを識別できます。不適切なコンテンツも検出できます。また、Amazon Rekognition では、高精度な顔分析および顔認識も可能です。顔の検出、分析、比較は、ユーザー検証、カタログ作成、人数計数、公共安全など、多岐にわたって活用できます。
ひとまず実装してみる
環境を作成するのが手間なのでLambdaを使って実装。
プログラムはLambdaで提供があるPython 3.7。
顔認識
人物が写っている2枚の写真から同一人物かどうかを判定する。
S3にアップされている写真を比較するサンプルプログラム。
from boto3 import Session
def lambda_handler(event, context):
session = Session(region_name="ap-northeast-1")
rekognition = session.client("rekognition")
response=rekognition.compare_faces(
SourceImage={'S3Object':{'Bucket':"XXXXX",'Name':"XXXXX"}},
TargetImage={'S3Object':{'Bucket':"XXXXX",'Name':"XXXXX"}},
SimilarityThreshold=80)
return response
簡単にプログラムの解説。
from boto3 import Session
AWSの各種サービスを簡単に仕様できるライブラリboto3を使って実装。
※ライブライはC++、Go、Java、JavaScript,.Net、Node.js、PHP、Rubyがある
session = Session(region_name="ap-northeast-1")
rekognition = session.client("rekognition")
サービスを使用するためには必ずSessionというAWSとの接続情報が必要。
リージョンはアジア東京を指定。
rekognitionを使用するためのオブジェクト取得。
response=rekognition.compare_faces(
SourceImage={'S3Object':{'Bucket':"XXXXX",'Name':"XXXXX"}},
TargetImage={'S3Object':{'Bucket':"XXXXX",'Name':"XXXXX"}},
SimilarityThreshold=80)
これがAmazonから提供されている「顔認証」関数。
API仕様 こちらから
SourceImageに認識したい顔だけが写っている写真
TargetImageに解析したい写真
SimilarityThresholdに認識度。高いほど厳しいチェックになる。0-100で指定。
responseは
FaceMatches に同一人物を判断した顔の情報
UnmatchedFaces に違うと判断した顔の情報
Confidenceがその判断の信頼度
{
"SourceImageFace": {
"BoundingBox": {
"Width": 0.30209746956825256,
"Height": 0.21446102857589722,
"Left": 0.319732129573822,
"Top": 0.23807010054588318
},
"Confidence": 99.99998474121094
},
"FaceMatches": [
{
"Similarity": 99.37952423095703,
"Face": {
"BoundingBox": {
"Width": 0.21386872231960297,
"Height": 0.16094279289245605,
"Left": 0.6417301297187805,
"Top": 0.6225799322128296
},
"Confidence": 99.99987030029297,
"Landmarks": [
{
"Type": "eyeLeft",
"X": 0.7082532644271851,
"Y": 0.6811327934265137
},
{
"Type": "eyeRight",
"X": 0.7683542370796204,
"Y": 0.7155389189720154
},
{
"Type": "mouthLeft",
"X": 0.6812899112701416,
"Y": 0.7383925914764404
},
{
"Type": "mouthRight",
"X": 0.7310847640037537,
"Y": 0.7682420015335083
},
{
"Type": "nose",
"X": 0.6889581680297852,
"Y": 0.7370006442070007
}
],
"Pose": {
"Roll": 38.84079360961914,
"Yaw": -19.29507827758789,
"Pitch": -41.210079193115234
},
"Quality": {
"Brightness": 44.140357971191406,
"Sharpness": 78.64350128173828
}
}
}
],
"UnmatchedFaces": [
{
"BoundingBox": {
"Width": 0.01333685964345932,
"Height": 0.013453098013997078,
"Left": 0.9252238869667053,
"Top": 0.4759874939918518
},
"Confidence": 97.7153091430664,
"Landmarks": [
{
"Type": "eyeLeft",
"X": 0.9277710914611816,
"Y": 0.4811204969882965
},
{
"Type": "eyeRight",
"X": 0.933621883392334,
"Y": 0.4823104441165924
},
{
"Type": "mouthLeft",
"X": 0.9270203113555908,
"Y": 0.4870522618293762
},
{
"Type": "mouthRight",
"X": 0.931785523891449,
"Y": 0.4880983531475067
},
{
"Type": "nose",
"X": 0.9278116226196289,
"Y": 0.484792023897171
}
],
"Pose": {
"Roll": 13.193720817565918,
"Yaw": -30.132854461669922,
"Pitch": -10.473925590515137
},
"Quality": {
"Brightness": 75.9936752319336,
"Sharpness": 73.32209777832031
}
},
{
"BoundingBox": {
"Width": 0.2887393832206726,
"Height": 0.32251936197280884,
"Left": 0.1142626702785492,
"Top": 0.45462948083877563
},
"Confidence": 99.99995422363281,
"Landmarks": [
{
"Type": "eyeLeft",
"X": 0.1551831066608429,
"Y": 0.5991319417953491
},
{
"Type": "eyeRight",
"X": 0.25697842240333557,
"Y": 0.6217619776725769
},
{
"Type": "mouthLeft",
"X": 0.14137881994247437,
"Y": 0.7185965180397034
},
{
"Type": "mouthRight",
"X": 0.22245647013187408,
"Y": 0.7383744716644287
},
{
"Type": "nose",
"X": 0.14210543036460876,
"Y": 0.6684256792068481
}
],
"Pose": {
"Roll": 17.434614181518555,
"Yaw": -41.510498046875,
"Pitch": -21.136119842529297
},
"Quality": {
"Brightness": 41.628482818603516,
"Sharpness": 95.51618957519531
}
},
{
"BoundingBox": {
"Width": 0.007370786275714636,
"Height": 0.008144599385559559,
"Left": 0.6609883904457092,
"Top": 0.49813687801361084
},
"Confidence": 83.50224304199219,
"Landmarks": [
{
"Type": "eyeLeft",
"X": 0.6622864603996277,
"Y": 0.5042642951011658
},
{
"Type": "eyeRight",
"X": 0.6650356650352478,
"Y": 0.5022475719451904
},
{
"Type": "mouthLeft",
"X": 0.6650884747505188,
"Y": 0.5060529112815857
},
{
"Type": "mouthRight",
"X": 0.6673902273178101,
"Y": 0.5043438076972961
},
{
"Type": "nose",
"X": 0.6656265258789062,
"Y": 0.505027174949646
}
],
"Pose": {
"Roll": -43.10660934448242,
"Yaw": -4.3733038902282715,
"Pitch": -33.09136962890625
},
"Quality": {
"Brightness": 73.02571868896484,
"Sharpness": 53.330047607421875
}
},
{
"BoundingBox": {
"Width": 0.009758301079273224,
"Height": 0.010332607664167881,
"Left": 0.966498851776123,
"Top": 0.46405404806137085
},
"Confidence": 61.05154037475586,
"Landmarks": [
{
"Type": "eyeLeft",
"X": 0.9691260457038879,
"Y": 0.4726991653442383
},
{
"Type": "eyeRight",
"X": 0.9677429795265198,
"Y": 0.4707190990447998
},
{
"Type": "mouthLeft",
"X": 0.9723362326622009,
"Y": 0.4712704122066498
},
{
"Type": "mouthRight",
"X": 0.9711670279502869,
"Y": 0.4696311056613922
},
{
"Type": "nose",
"X": 0.9699736833572388,
"Y": 0.4713567793369293
}
],
"Pose": {
"Roll": -119.80672454833984,
"Yaw": -8.657889366149902,
"Pitch": 21.94735336303711
},
"Quality": {
"Brightness": 84.52898406982422,
"Sharpness": 73.32209777832031
}
},
{
"BoundingBox": {
"Width": 0.018424568697810173,
"Height": 0.020794881507754326,
"Left": 0.7489200830459595,
"Top": 0.47518208622932434
},
"Confidence": 87.87468719482422,
"Landmarks": [
{
"Type": "eyeLeft",
"X": 0.7618342638015747,
"Y": 0.48698189854621887
},
{
"Type": "eyeRight",
"X": 0.7628127336502075,
"Y": 0.4866960644721985
},
{
"Type": "mouthLeft",
"X": 0.7631105184555054,
"Y": 0.4888738989830017
},
{
"Type": "mouthRight",
"X": 0.7637985944747925,
"Y": 0.4886566698551178
},
{
"Type": "nose",
"X": 0.7641363143920898,
"Y": 0.48711803555488586
}
],
"Pose": {
"Roll": -34.46097946166992,
"Yaw": 60.46308135986328,
"Pitch": -14.032490730285645
},
"Quality": {
"Brightness": 67.51453399658203,
"Sharpness": 83.14741516113281
}
},
{
"BoundingBox": {
"Width": 0.011087420396506786,
"Height": 0.011487020179629326,
"Left": 0.9760779142379761,
"Top": 0.4641396105289459
},
"Confidence": 97.86558532714844,
"Landmarks": [
{
"Type": "eyeLeft",
"X": 0.978825032711029,
"Y": 0.469203919172287
},
{
"Type": "eyeRight",
"X": 0.9811084270477295,
"Y": 0.4689272344112396
},
{
"Type": "mouthLeft",
"X": 0.9797573685646057,
"Y": 0.47199031710624695
},
{
"Type": "mouthRight",
"X": 0.9815858006477356,
"Y": 0.47177186608314514
},
{
"Type": "nose",
"X": 0.979856014251709,
"Y": 0.47067731618881226
}
],
"Pose": {
"Roll": -11.149984359741211,
"Yaw": -42.128475189208984,
"Pitch": -1.7187515497207642
},
"Quality": {
"Brightness": 79.50706481933594,
"Sharpness": 73.32209777832031
}
},
{
"BoundingBox": {
"Width": 0.008290473371744156,
"Height": 0.009163682349026203,
"Left": 0.6165273785591125,
"Top": 0.5080556273460388
},
"Confidence": 73.62310791015625,
"Landmarks": [
{
"Type": "eyeLeft",
"X": 0.6195554137229919,
"Y": 0.5123717188835144
},
{
"Type": "eyeRight",
"X": 0.6210446357727051,
"Y": 0.5128311514854431
},
{
"Type": "mouthLeft",
"X": 0.6189116835594177,
"Y": 0.5142983794212341
},
{
"Type": "mouthRight",
"X": 0.6200852394104004,
"Y": 0.5146732926368713
},
{
"Type": "nose",
"X": 0.6193997263908386,
"Y": 0.5135700702667236
}
],
"Pose": {
"Roll": 20.73882293701172,
"Yaw": -33.038211822509766,
"Pitch": -11.055394172668457
},
"Quality": {
"Brightness": 41.28157424926758,
"Sharpness": 67.22731018066406
}
}
],
"ResponseMetadata": {
"RequestId": "d638e263-eeb5-4cbf-807d-aa6328f8096b",
"HTTPStatusCode": 200,
"HTTPHeaders": {
"content-type": "application/x-amz-json-1.1",
"date": "Thu, 19 Dec 2019 12:20:36 GMT",
"x-amzn-requestid": "d638e263-eeb5-4cbf-807d-aa6328f8096b",
"content-length": "5545",
"connection": "keep-alive"
},
"RetryAttempts": 0
}
}
導入事例
千株式会社の写真販売サービス「はいチーズ」。幼稚園や保育園でのイベントの写真販売の際に、スマホから「自分の子どもが映った写真」だけを選んで抽出できるサービスを提供。
テキスト検出
from boto3 import Session
def lambda_handler(event, context):
session = Session(region_name="ap-northeast-1")
rekognition = session.client("rekognition")
response=rekognition.detect_text(Image={'S3Object':{'Bucket':"XXXXX",'Name':"XXXXX"}})
return response
簡単にプログラムの解説。
from boto3 import Session
AWSの各種サービスを簡単に仕様できるライブラリboto3を使って実装。
※ライブライはC++、Go、Java、JavaScript,.Net、Node.js、PHP、Rubyがある
session = Session(region_name="ap-northeast-1")
rekognition = session.client("rekognition")
サービスを使用するためには必ずSessionというAWSとの接続情報が必要。
リージョンはアジア東京を指定。
rekognitionを使用するためのオブジェクト取得。
response=rekognition.detect_text(Image={'S3Object':{'Bucket':"XXXXX",'Name':"XXXXX"}})
これがAmazonから提供されている「テキスト検出」関数。
API仕様 こちらから
Imageに解析したい画像を渡すだけ。
この画像を解析した結果。
行と単語ごとに解析される。
「J389 NLT」は行(line)で「J389 NLT」、単語で「J389」「NLT」。
DetectedTextが検出したテキスト
Confidenceが信頼度
{
"TextDetections": [
{
"DetectedText": "C",
"Type": "LINE",
"Id": 0,
"Confidence": 72.02649688720703,
"Geometry": {
"BoundingBox": {
"Width": 0.04000072553753853,
"Height": 0.022570611909031868,
"Left": 0.25833332538604736,
"Top": 0.1862500011920929
},
"Polygon": [
{
"X": 0.25833332538604736,
"Y": 0.1862500011920929
},
{
"X": 0.2983340620994568,
"Y": 0.18744535744190216
},
{
"X": 0.29803428053855896,
"Y": 0.21001596748828888
},
{
"X": 0.2580335736274719,
"Y": 0.20882061123847961
}
]
}
},
{
"DetectedText": "J389 NLT",
"Type": "LINE",
"Id": 1,
"Confidence": 99.67211151123047,
"Geometry": {
"BoundingBox": {
"Width": 0.16278892755508423,
"Height": 0.054150763899087906,
"Left": 0.47471365332603455,
"Top": 0.7062126398086548
},
"Polygon": [
{
"X": 0.47471365332603455,
"Y": 0.7062126398086548
},
{
"X": 0.6375026106834412,
"Y": 0.7274553775787354
},
{
"X": 0.6343620419502258,
"Y": 0.781606137752533
},
{
"X": 0.4715730845928192,
"Y": 0.7603633999824524
}
]
}
},
{
"DetectedText": "C",
"Type": "WORD",
"Id": 2,
"ParentId": 0,
"Confidence": 72.02649688720703,
"Geometry": {
"BoundingBox": {
"Width": 0.040019527077674866,
"Height": 0.022515427321195602,
"Left": 0.25833332538604736,
"Top": 0.1862500011920929
},
"Polygon": [
{
"X": 0.25833332538604736,
"Y": 0.1862500011920929
},
{
"X": 0.2983333468437195,
"Y": 0.1875
},
{
"X": 0.29750001430511475,
"Y": 0.20999999344348907
},
{
"X": 0.25833332538604736,
"Y": 0.20874999463558197
}
]
}
},
{
"DetectedText": "J389",
"Type": "WORD",
"Id": 3,
"ParentId": 1,
"Confidence": 99.63874053955078,
"Geometry": {
"BoundingBox": {
"Width": 0.08508982509374619,
"Height": 0.05266508460044861,
"Left": 0.4749999940395355,
"Top": 0.706250011920929
},
"Polygon": [
{
"X": 0.4749999940395355,
"Y": 0.706250011920929
},
{
"X": 0.559166669845581,
"Y": 0.71875
},
{
"X": 0.5550000071525574,
"Y": 0.7712500095367432
},
{
"X": 0.4716666638851166,
"Y": 0.7587500214576721
}
]
}
},
{
"DetectedText": "NLT",
"Type": "WORD",
"Id": 4,
"ParentId": 1,
"Confidence": 99.70547485351562,
"Geometry": {
"BoundingBox": {
"Width": 0.07040064036846161,
"Height": 0.05260571464896202,
"Left": 0.5674999952316284,
"Top": 0.7200000286102295
},
"Polygon": [
{
"X": 0.5674999952316284,
"Y": 0.7200000286102295
},
{
"X": 0.637499988079071,
"Y": 0.7275000214576721
},
{
"X": 0.6341666579246521,
"Y": 0.7799999713897705
},
{
"X": 0.5649999976158142,
"Y": 0.7724999785423279
}
]
}
}
],
"ResponseMetadata": {
"RequestId": "b4280fa0-2982-4ac7-8ce3-02beb83b986f",
"HTTPStatusCode": 200,
"HTTPHeaders": {
"content-type": "application/x-amz-json-1.1",
"date": "Thu, 19 Dec 2019 13:26:41 GMT",
"x-amzn-requestid": "b4280fa0-2982-4ac7-8ce3-02beb83b986f",
"content-length": "2131",
"connection": "keep-alive"
},
"RetryAttempts": 0
}
}
料金
API を使用して画像を分析するたびに料金が発生。
だいたい、 1 か月あたり 100 万枚分析して 1000 USD。
料金例は こちらから
捕捉
ACCESS_KEY、SECRET_KEYを指定せずに正常にsessionが取得できているのはLambdaを使っているからですね。
ポリシーを作成し、Lambda実行するロールに割り当ている。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"rekognition:DetectText",
"rekognition:CompareFaces",
"s3:*"
],
"Resource": "*"
}
]
}
rekognition:DetectText、rekognition:CompareFacesの使用とs3への全ての操作を許可。
これがないと下記のエラーが出る。
"errorMessage": "An error occurred (AccessDeniedException) when calling the CompareFaces operation: User: arn:aws:sts::294326415501:assumed-role/testRekognitionPythonFace-role-jcjxzbmq/testRekognitionPythonFace is not authorized to perform: rekognition:CompareFaces",