45
37

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

夏季休暇だったのでAzureのFaceAPIを使ってみた

Last updated at Posted at 2018-08-14

Azure FaceAPIの利用

AzureのFaceAPIを利用するには、まずAzureにアカウントを作成しなければなりません。
アカウント作成は無料で行えます。
https://azure.microsoft.com/ja-jp/

登録後、ポータルへ入り、リソースの作成を行ます。

スクリーンショット 0030-08-14 午後10.27.18.png

AI + Machine LearningからFace APIを選択し、
適当に項目を埋めて、作成します。

すこし時間を待っていると、作成したリソースがデプロイされますので、そこでようやくダッシュボードでリソースを選択することができるようになります。

リソースを選択し、Keysの項目をクリックすると、KEY1とKEY2が出てきます。
これらはあとでsubscription keyとして利用するので、どこかに控えておいても良いかもしれません(利用するのはKEY1で良いです)。

スクリーンショット 0030-08-14 午後10.31.43.png

faceAPIの仕組み

faceAPIにおいて人物の顔を登録し、それを学習させる過程としては、まず、人物の集合であるグループを定義する必要があります。

FACE APIについて

Person Groupの作成

PersonGroupにはこれから登録する顔が所属するグループを作成します。 たとえば、自分の好きなアイドルグループとか。

ここではpythonのHTTPライブラリであるrequestsを利用してPersonGroupを作成します。

face_api.py
import requests

def createPersonGroup():
    result = requests.put(
        'https://japaneast.api.cognitive.microsoft.com/face/v1.0/persongroups/欅坂46',
        headers = {
            'Ocp-Apim-Subscription-Key': (subscription key for Azure FaceAPI)
        },
        json = {
            'name' = '欅坂46'
        }
    )

このようにfaceAPIのPersonGroup作成用のURLにputリクエストを出してあげると、resultにその結果が入ります。
その後print(result.text)をしてみると、作成に成功した場合、レスポンスのボディは空になって返ってきます。
エラーが起きた場合には、エラーの種類と原因が返ってきますので、それを参考に問題を解決すれば良いです。

PersonGroupを作成した後、今度はそのグループに属するPersonを登録していきます。

Document
How to create PersonGroup

Personの登録

それでは作成したPersonGroupにPersonを登録していきます。 「欅坂46」グループに「長濱ねる」を作成する場合、 Person登録用のURLは https://[location].api.cognitive.microsoft.com/face/v1.0/persongroups/{personGroupId}/persons なので、
face_api.py
import requests
import json

def createPerson(personName):
    result = requests.post{
        'https://japaneast.api.cognitive.microsoft.com/face/v1.0/persongroups/欅坂46/persons',
        headers = {
            'Ocp-Apim-Subscription-Key': (subscription key for Azure FaceAPI)
        },
        json = {
            'name': personName
        }
    }
    personId = json.loads(result.text)['personId'] # personのidを抽出できる
    return personId

createPerson('長濱ねる')

この次の工程では登録したpersonにその顔画像を、personその人として登録していくので、返ってくるpersonIdが必要になってきます。

Document
How to create Person

Add Faceの工程

登録したpersonの顔写真を登録していきます。 おそらく、以下のような実装になるかと思います。
face_api.py
import requests

def addFace(personId, imageUrl):
    requests.post(
        'https://japaneast.api.cognitive.microsoft.com/face/v1.0/persongroups/欅坂46/persons/' + personId + '/persistedFaces',
        headers = {
            'Ocp-Apim-Subscription-Key': (subscription key for Azure FaceAPI)
        },
        json = {
            'url': imageUrl
        }
    )

personId # createPerson()で得られたpersonId
imageUrl # personの画像url personその人しか写っていないものでないと、エラーになる
pictures = [imageUrl, imageUrl, imageUrl......]
for i in pictures:
    addFace(personId, i)

事前に取得しておいたpersonIdに、顔写真一枚一枚をそれぞれを登録していきます。

Document
How to add face to a person

さて、ここまでの処理で学習させるための素材の準備が完了しました。
PersonGroupを作成し、そこに所属する人間と、その人間の写真を登録しましたので、これらを学習させてみましょう。
そうすることで、こちら側で用意した画像のurlを読み込ませて、そこに写る顔から、該当する人間の情報を返させるようにします。

Trainの実施

さきほどのaddFaceが終わった後、以下のメソッドを実行します。
face_api.py
import requests

def trainGroup():
    requests.post(
        'https://japaneast.api.cognitive.microsoft.com/face/v1.0/persongroups/欅坂46/train',
        headers = {
            'Ocp-Apim-Subscription-Key': (subscription key for Azure FaceAPI)
        },
        json = {
            'personGroupId': '欅坂46'
        }
    )

これで学習が終了しました。
あっけなくて逆に物足りなさを感じるかもしれません。

Document
How to train PersonGroup

それでは、今度はFaceAPIに、無作為で選んだ画像urlを渡し、それが「長濱ねる」かどうかを判定させましょう。
現状では「長濱ねる」だけを学習させていますので、他のメンバーも学習させて、その中から「長濱ねる」を当てられるようにしておきます。

face_api.py
import requests

# 鈴本美愉の登録
personId = createPerson('鈴本美愉')
pictures = [imageUrl, imageUrl, imageUrl......]
for i in pictures:
    addFace(personId, i)

# 渡邉理佐の登録
personId = createPerson('渡邉理佐')
pictures = [imageUrl, imageUrl, imageUrl......]
for i in pictures:
    addFace(personId, i)

# グループの学習
trainGroup()

画像から顔を判別してPersonGroupの中から同一の顔の持ち主を探す

判別させたい画像のurlを用意します。
face_api.py
import requests
import json

imageUrl = 'http://cdn.keyakizaka46.com/images/14/752/02396f50b62224f8b6b686b1cffcb/400_320_102400.jpg'

def detectFace(imageUrl):
    result = requests.post(
        'https://japaneast.api.cognitive.microsoft.com/face/v1.0/detect',
        headers = {
            'Ocp-Apim-Subscription-Key': (subscription key for Azure FaceAPI)
        },
        json = {
            'url': imageUrl
        }
    )
    detectedFaceId = json.loads(result.text)[0]['faceId']
    return detectedFaceId # 画像から取得された顔のid

def identifyPerson(detectedFaceId):
    result = requests.post(
        'https://japaneast.api.cognitive.microsoft.com/face/v1.0/identify',
        headers = {
            'Ocp-Apim-Subscription-Key': (subscription key for Azure FaceAPI)
        },
        json = {
            'faceIds': [detectedFaceId],
            'personGroupId': '欅坂46'
        }
    )
    identifiedPerson = json.loads(result.text)[0]['candidates']
    return identifiedPerson # detectedFaceIdをから抽出されたcandidatesを格納

detectedFaceId = detectFace(imageUrl)
identifiedPerson = identifyPerson(detectedFaceId)

print(identifiedPerson)
# [{'personId': '*********************************', 'confidence': 0.68672}] というような形で画像の顔と一致した人物のデータを返す

まず、detectFace()に画像のurlを渡し、その画像から読み取られた顔のidを手に入れます。
次にそのidをidentifyPerson()に渡し、PersonGroup内に登録した人物の顔とdetect時に読み込ませた画像の顔を比較します。
最後に、その結果得られた候補者(candidates)のidを抽出します。

この後、このcandidateのidはそのpersonの名前を抽出するために利用します。

Document
How to detect the registered person's face
How to identify the person

特定されたpersonIdからpersonの情報を取得する

最後の工程です。
identifyPerson()から取得したpersonIdを利用して、そのidの持ち主の名前を抽出します。

face_api.py
import requests
import json

def getPersonNameByPersonId(personId):
    result = requests.get(
        'https://japaneast.api.cognitive.microsoft.com/face/v1.0/persongroups/欅坂46/persons',
        headers = {
	    "Ocp-Apim-Subscription-Key": (subscription key for Azure FaceAPI)
        },
        json = {
            'personGroupId': '欅坂46'
        }
    )
    # personsには登録された全てのpersonが入っている
    persons = json.loads(result.text)
    for person in persons:
        # 渡されたpersonIdと合致するidを持つpersonを抽出して、その名前を返す
        if person['personId'] == personId:
            return person['name']

identifiedPersonName = getPersonNameByPersonId(personId)
print(identifiedPersonName) # => 渡邉理佐

getPersonNameByPersonId()でPersonGroupに登録した人物の情報を全て抜き出してから、
あらかじめ渡しておいたpersonIdと同じidを持つ人物だけを抜き出します。
returnしたperson['name']の結果が正しい結果かどうかを確認しましょう。
FaceAPIも完璧ではないでしょうから、時には間違った答えを返すこともあると思いますが、基本的には正しい答えを返してくれると思います。
実装が間違っていなければ、基本的には。。。

ここで行ったこと

だいたいの流れをまとめます。
とにかくこの流れさえわかっておけば、間違えることはないと思うので。

#####学習工程#####
・PersonGroupの作成
・Personの登録
・PersonにFaceを登録
・PersonGroupを学習
#####判別工程#####
・特定の画像からFaceをdetect
・detectした人物の画像から、それに該当する人物のidを抽出
・idを基にその人物の名前を抽出

あと、一貫したソースコードも載せておきます。

face_api.py
#-*-coding:utf8-*-
import json
import logging
import requests
import time

BASE_URL = 'https://japaneast.api.cognitive.microsoft.com/face/v1.0/'
SUBSCRIPTION_KEY = 'your subscription key'
GROUP_NAME = '欅坂46'

def createPersonGroup():
    end_point = BASE_URL + 'persongroups/' + GROUP_NAME
    requests.put(
        end_point,
        headers = {
            'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY
        },
        json = {
            'name': GROUP_NAME
        }
    )

def createPerson(personName):
    end_point = BASE_URL + 'persongroups/' + GROUP_NAME + '/persons'
    result = requests.post(
        end_point,
        headers = {
            'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY
        },
        json = {
            'name': personName
        }
    )
    personId = json.loads(result.text)['personId']
    return personId

def addFace(personId, imageUrl):
    end_point = BASE_URL + 'persongroups/' + GROUP_NAME + '/persons/' + personId  + '/persistedFaces'
    requests.post(
        end_point,
        headers = {
            'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY
        },
        json = {
            'url': imageUrl
        }
    )

def trainGroup():
    end_point = BASE_URL + 'persongroups/' + GROUP_NAME + '/train'
    requests.post(
        end_point,
        headers = {
            'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY
        },
        json = {
            'personGroupId': GROUP_NAME
        }
    )

def detectFace(imageUrl):
    end_point = BASE_URL + "detect"
    result = requests.post(
        end_point,
        headers = {
            'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY
        },
        json = {
            'url': imageUrl
        }
    )
    detectedFaceId = json.loads(result.text)[0]['faceId']
    return detectedFaceId

def identifyPerson(faceId):
    end_point = BASE_URL + 'identify'
    faceIds = [faceId]
    result = requests.post(
        end_point,
        headers = {
            'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY
        },
        json = {
            'faceIds': faceIds,
            'personGroupId': GROUP_NAME
        }
    )
    candidates = json.loads(result.text)[0]['candidates']
    return candidates

def getPersonNameByPersonId(personId):
    end_point = BASE_URL + 'persongroups/' + GROUP_NAME + '/persons'
    result = requests.get(
        end_point,
        headers = {
	    "Ocp-Apim-Subscription-Key": SUBSCRIPTION_KEY
        },
        json = {
            'personGroupId': GROUP_NAME
        }
    )
    persons = json.loads(result.text)
    for person in persons:
        if person['personId'] == personId:
            return person['name']

if __name__ == '__main__':
    # learning face
    createPersonGroup()
    # neru.txtに書き込まれた画像urlを配列にしてsourcesに格納
    sources = open('neru.txt').read().split('\n')
    personId = createPerson('長濱ねる')
    for i in sources:
        addFace(personId, i)
    # addFace()処理を待ってからtrainGroup()をしたいので、応急処置的にsleepしてます。。。
    # promise的なの使って改善すべき?? ちょっとこの辺まだよくわかってないです m(__)m
    time.sleep(10)
    trainGroup()

    # identify person
    imageUrl = 'https://scontent-nrt1-1.cdninstagram.com/vp/e714b56e8acc9001bd6e9c938cd738b5/5BDED0D0/t51.2885-15/e35/26180878_1782452602056444_9161322227717636096_n.jpg'
    detectedFaceId = detectFace(imageUrl)
    identifiedPerson = identifyPerson(detectedFaceId)
    if identifiedPerson[0]['personId']:
        personId = identifiedPerson[0]['personId']
        personName = getPersonNameByPersonId(personId)
        print(personName)

neru.txtには画像urlを1行ずつ書き込んでおく

neru.txt
http://48pedia.org/images/e/ee/2018%E5%B9%B4%E6%AC%85%E5%9D%8246%E3%83%97%E3%83%AD%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB_%E9%95%B7%E6%BF%B1%E3%81%AD%E3%82%8B_2.jpg
https://i0.wp.com/free-style-info.com/wp-content/uploads/2017/12/neru008-20171216-174300.jpg?resize=400%2C600&ssl=1
https://dreaming-baby.com/wp-content/uploads/2017/12/ac2eea4df1b0b02ac7bcdbf1a9f0479b.jpg
http://livedoor.blogimg.jp/fumichen2/imgs/1/1/1192251d.jpg
https://rr.img.naver.jp/mig?src=http%3A%2F%2Fimgcc.naver.jp%2Fkaze%2Fmission%2FUSER%2F20160918%2F79%2F7753109%2F205%2F853x1280xd8fa341ed662577f5d29ba9.jpg%2F300%2F600&twidth=300&theight=600&qlt=80&res_format=jpg&op=r

参考にしたもの

1. Getting Started with Face API in Python Tutorial 2. Azure face APIで遊んでみよう その2~顔の認識 3. AIで似ているAV女優を紹介しているスケベAI「スケベ博士」を作りました。

特にdaiさんのnoteは参考になりました。
qiitaに書いておきたいなと思い始めたのも、daiさんのnoteを読んで、自分で画像認識を実装してからでした。
正直なところ、daiさんのだけではわからなかったり、抜けていると感じたところがあったので、そういった点も含めてFaceAPIの使い方を残しておこうと思いました。
参考になれば嬉しいです。

間違った点や改善すべき点などがあればご指摘ください。

45
37
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
45
37

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?