既に製品としての活用事例があるPepper × MS AzureのFaceAPIを試してみた。
ドキュメントがしっかりしていて、実装しやすいので是非お試しを..
#Azure FaceAPI ざっくりしたまとめ
FaceAPIには、顔認証に欠かせない主なAPIが5つあります。顔の検出を行うDetect、顔の認識を行うIdentify、Verify、Find Similar、Groupです。
##Face Detection(顔の検出)
####Detect
写真から人間の顔を検出するAPI。
- 検出された顔にはFaceIdが割り振られる。FaceIdの期限は、Detectコール後24時間。
- 一つの画像から最大64の顔を検出
- 画像は、バイナリもしくはURLで指定
- 検出した顔の示す四角形(left, top, width, height)を取得できる
- オプションで、gender, age, head pose, facial hair, glasses等の要素を取得できる
##Face Recognition(顔認識)
顔を認識するためのAPIとして、以下の4つが用意されている。
####Identify(顔識別)
Detectにより割り振られたFaceIdと一致する顔を、指定するPersonGroup(後述、顔のセット)から検索し、候補者を返すAPI。
- 候補の顔は、類似点を計算しPersonGroupの中から高い順に候補を返す。
- PersonGroupが事前にトレーニングされている必要がある。
- 1回のリクエストで10人以内の顔を特定することができる。
####Verify(顔検証)
2つの顔(2つのDetectされた顔、もしくは、1つのDetectされた顔とPersonオブジェクト)が同一人物のものかどうかの認証をするAPI。
####Find Similar(類似の顔を検出)
顔のセット(FaceList)を与えて、その中から問い合わせた顔(Detectされた顔)と類似の顔を検索するAPI。
- 検索に使う顔のセットには、FaceList(後述)、もしくは、DetectされたFaceIdの配列を指定することができる。
-
matchPerson
とmatchFace
の2つのモードがある。-
matchPerson
は、顔の閾値を利用してできるだけ同じ人の顔を検索する。(ない場合は空で返される) -
matchFace
は、閾値は無視して、類似性は低くとも、とにかく類似の顔としてランクされたものを返す
-
####Group(顔のグループ化)
Detectにより検出された複数の顔を、見た目の類似性に基づいてグループ化するAPI。
- グループは 、似ている顔のグループ、MessyGroup(似ている顔が見つからなかった顔のグループ)に分けられる。
- 少なくとも2以上の顔をが必要となる。最大で1000の顔をグルーピングすることができる。
- 同一人物の顔が異なるグループに分けられていることがあるので注意が必要になる。
##顔認証するために事前に用意する顔のセット
上記5つのAPIを使用するには、顔のセットを事前に登録しておく必要がある。
顔のセットとして、PersonGroupとFaceListの2つが用意されている。
使用するAPIによって、PersonGroupとFaceListを使い分ける。
####Person Group
Identifyで使用される顔のセット。
- グループの中に、一人の顔を表すPersonオブジェクト(後述)を、最大で1000まで登録できる。
- Identifyでは、Person Groupの中のPersonの顔から特定する。
- Person Groupを操作するAPIとして、生成・削除などのAPIが用意されている。
- PersonGroupに新しいPersonオブジェクトを生成したり、Personオブジェクトの情報を更新した場合、PersonGroupをトレーニングさせる必要がある。(トレーニングされるまで情報は更新されない。)
####Person
Person Groupに含まれる、一人の情報を表すのがPersonオブジェクトである。
- Personオブジェクトには、248枚までの画像を追加することができる
- 中に含まれる顔の情報は、persistedFaceIdとして保持される。(FaceIdのような期限はない)
- URLもしくは、バイナリデータにより顔を追加できる。
Person GroupとPersonオブジェクトの関係のイメージは以下のような感じです。
Identifyでは、以下のようなPersonを含むPerson Groupのどれかを指定し、顔を認証します。
####Face List
Face Listは、Find Similarで使用される顔のセットです。
- Face Listは、顔のグループで、中に最大1000まで持つことができる。
- Face Listに追加された顔に期限はない。
- Face Listを操作するAPIとして、追加・削除等のAPIが用意されている。
#Pythonで実装。
顔の登録と認証をPepperで試してみました。認証はIdentifyで行います。
##顔の登録
Personオブジェクトに顔を登録します。
※先にPersonオブジェクトを追加するためのPerson Groupを生成しておきます。
[流れ]
- Pepperのタブレットで入力された名前(登録名となる)を受け取る。
- 入力された名前を登録名として、Personオブジェクトを生成する(Create a Person)
- 作ったPersonにPepperのカメラで撮った顔を追加する(Add a Person Face)
- Personオブジェクトへの顔の追加が成功したら、PersonGroupのトレーニングをする(Train Person Group)
###Create a Person
タブレットから名前を受け取ったら、Create a Personで、特定のPerson Groupに新しいPersonオブジェクトを追加します。
Create a Person APIを使用するための関数
#-----------------------------------
# Create a Person POST Request
# param1: name--Personオブジェクト登録名
# param2: user_data--説明(Optional)
#-----------------------------------
def create(self, name, user_data=None):
import requests, json
url = "https://api.projectoxford.ai/face/v1.0/persongroups/"
url += str(self.memory.getData("AzureData/PersonGroupId"))
url += "/persons"
#Request Header
headers = {
'ocp-apim-subscription-key': self.memory.getData("AzureData/SubscriptionKey"),
'Content-Type': "application/json"
}
#Request Body
payload = {
'name': name,
'userData': user_data
}
try:
res = requests.post(url, data=json.dumps(payload), headers=headers)
if res.status_code == 200:
return res.json()
else:
error = res.json()
errorCode = error["error"]["code"].encode("utf8")
self.logger.info(errorCode)
errorMessage = error["error"]["message"].encode("utf8")
self.logger.info(errorMessage)
return None
except requests.exceptions.RequestException as e:
self.logger.info(e)
return None
###Add a Person Face
続いて、生成されたPersonオブジェクトに顔を追加します。
Add a Person Face APIを使用するための関数
#--------------------------------------------------------------
# Add a Person Face POST Request
# param1: image--画像のバイナリデータ
# param2: person_id--Create a Personで生成されたPersonオブジェクトのID
# param3: user_data--(Optional)
# param4: target_face--(Optional)
#--------------------------------------------------------------
def add_face(self, image, person_id, user_data=None, target_face=None):
import requests
personGroupId = self.memory.getData("AzureData/PersonGroupId")
url = "https://api.projectoxford.ai/face/v1.0/persongroups/" + personGroupId + "/persons/" + person_id + "/persistedFaces"
#Request Header
headers = {
'ocp-apim-subscription-key': self.memory.getData("AzureData/SubscriptionKey"),
'Content-Type': "application/octet-stream",
'cache-control': "no-cache",
}
#Request Body
params = {
'userData': user_data,
'targetFace': target_face,
}
try:
res = requests.post(url, headers=headers, params=params, data=image)
if res.status_code == 200:
return res.json()
else:
error = res.json()
errorCode = error["error"]["code"].encode("utf8")
self.logger.info(errorCode)
errorMessage = error["error"]["message"].encode("utf8")
self.logger.info(errorMessage)
#self.onFailure(errorMessage)
except requests.exceptions.RequestException as e:
self.logger.info(e)
#self.onFailure(e)
###Train Person Group
顔が登録できたら、最後にPerson Groupをトレーニングします。
Train a Person Group APIを使用するための関数
#----------------------------
# Train a Person Group POST
#----------------------------
def train(self):
import requests, json
#SubscriptionKey
subscriptionKey = self.memory.getData("AzureData/SubscriptionKey")
#Person Group ID
personGroupId = self.memory.getData("AzureData/PersonGroupId")
url = "https://api.projectoxford.ai/face/v1.0/persongroups/"
url += str(personGroupId)
url += "/train"
#Request Header
headers = {
'ocp-apim-subscription-key': str(subscriptionKey),
'Content-Type': "application/json"
}
#Request Body
payload = {
'personGroupId': person_group_id
}
try:
res = requests.post(url, data=json.dumps(payload), headers=headers)
if res.status_code == 202:
return res
else:
self.logger.info(res.status_code)
#self.onFailure()
except requests.exceptions.RequestException as e:
self.logger.info(e)
#self.onFailure(e)
##顔の認証
Identifyで、指定したPersonGroupの中の顔と一致するものがあるか、検索する。
[流れ]
- Pepperのカメラの画像に人間の顔があるか、検知する。(Detect)
- Detectで検知したFaceIdを使用して、PersonGroupから顔の認証を行う。(Identify)
- 候補者が見つかったら、見つかった人のPersonIDから、その人の情報を取得する。(Get a Person)
###Detect
Pepperでイメージをキャプチャし、顔が存在するか確認しに行く。返されるFaceIdを使用して次のIdentifyを行う。
Detect APIを使用するための関数
#-------------------------------
# Detect POST Request
# param1: image--画像のバイナリデータ
#-------------------------------
def detect(self, image):
import requests
url = "https://api.projectoxford.ai/face/v1.0/detect"
#Request Header
headers = {
'ocp-apim-subscription-key': self.memory.getData("AzureData/SubscriptionKey"),
'Content-Type': "application/octet-stream",
'cache-control': "no-cache",
}
#Request Parameter
params = {
'returnFaceId': True,
'returnFaceLandmarks': False,
'returnFaceAttributes': "age,gender"
}
try:
res = requests.post(url, headers=headers, params=params, data=image)
if res == []:
self.noFaceDetected()
else:
self.logger.info(res)
if res.status_code == 200:
data = res.json()
#self.logger.info(data)
if data == []: #配列が空の場合、顔が検出されていない
self.noFaceDetected(data)
else:
#FaceId
faceid = data[0]["faceId"].encode("utf8")
#Gender
gender = data[0]["faceAttributes"]["gender"].encode("utf8")
self.memory.insertData("AzureData/Gender", gender)
#Age
age = data[0]["faceAttributes"]["age"]
self.memory.insertData("AzureData/Age", age)
self.onSuccess(faceid)
else:
self.logger.info(res.status_code)
error = res.json()
self.logger.info(error["error"]["message"].encode("utf8"))
self.onFailure(res.status_code)
except requests.exceptions.RequestException as e:
self.logger.info(e)
self.onFailure(e)
###Identify
Detectから返されるFaceIdで、指定のPersonGroupから一致する顔を検索する。
Identify APIを使用するための関数
#-------------------------------
# Identify POST Request
# param1: face_ids --Detectで返されるFace IDの配列
# param2: max_candidates_return --Optional
# param3: threshold --Optional
#-------------------------------
def identify(self, face_ids, max_candidates_return=1, threshold=None):
import requests, json
url = 'https://api.projectoxford.ai/face/v1.0/identify'
#Request Header
headers = {
'ocp-apim-subscription-key': self.memory.getData("AzureData/SubscriptionKey"),
'Content-Type': "application/json"
}
#Request Body
payload = {
'personGroupId': self.memory.getData("AzureData/PersonGroupId"),
'faceIds': face_ids,
'maxNumOfCandidatesReturned': max_candidates_return,
'confidenceThreshold': threshold,
}
return requests.post(url, data=json.dumps(payload), headers=headers).json()
###Get a Person
Identifyで特定された候補者のPersonIdから、Get a Personで名前を取得する。
Get a Person APIを使用するための関数
#-------------------------------
# Get a Person GET Request
# param1: personId --取得したいPersonオブジェクトのID
#-------------------------------
def get_a_person(self, personId):
import requests
personGroupId = self.memory.getData("AzureData/PersonGroupId")
url = "https://api.projectoxford.ai/face/v1.0/persongroups/" + personGroupId + "/persons/" + personId
#Request Header
headers = {
'ocp-apim-subscription-key': self.memory.getData("AzureData/SubscriptionKey"),
'Content-Type': "application/json"
}
return requests.get(url, headers=headers)
#実際に顔認証してみて。
認証は問題なくできました!
年齢や笑顔といった要素は、少し怪しいのですが、認証は素晴らしい...
Personオブジェクトには、5枚ほど顔を登録しました。
多く写真を登録するば、より正確なデータが得られるのか・・・?