Microsoft AzureのFace APIを使って画像認識の技術に触れてみる

画像認識の技術は、自動車関連の分野としては道路標識や信号の色の識別、ナンバープレートの番号の読み取りなど様々な場面で使われており、身近なものとなっています

[YouTubeで検索](https://www.youtube.com/results?search_query=%E3%83%87%E3%83%A2+%E3%83%8A%E3%83%B3%E3%83%90%E3%83%BC%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88+%E8%AA%8D%E8%AD%98) してみるといくつかの動画が公開されていて、認識の様子を見ることができます

今回は、そういった画像認識の技術に触れてみようということで、Microsoftが提供しているFace APIを使ってみました

Face API - 顔認識 | Microsoft Azure
https://azure.microsoft.com/ja-jp/services/cognitive-services/face/

このAPIは画像に映っている人物の顔を認識して、年齢、性別、表情、髭の有無などの特徴を出すもの(Face Detection)です。他にも2つの顔が同一人物の物である確率を検証すること(Face Recongnition)ができます

# 準備

無料で使える範囲でFace APIを使ってみましょう。まずはAPIを使う準備をします
※ここで説明している画面は 2017年12月 のものです。将来的に変更になっているかもしれません

1. [Face APIのページ](https://azure.microsoft.com/ja-jp/services/cognitive-services/face/) にアクセスして[Face API を試す]をクリックします
![azl001.png](https://qiita-image-store.s3.amazonaws.com/0/61404/14429d68-9949-a3f6-fb97-7e0a36619c9a.png)

2. Face API の [APIキーの取得] をクリックします
![azl002.png](https://qiita-image-store.s3.amazonaws.com/0/61404/ac06014f-8a3f-4165-7507-4a059e84607e.png)

3. サービスの使用条件を確認して [次へ] 進みます
![azl003.png](https://qiita-image-store.s3.amazonaws.com/0/61404/52a6de56-5fd7-b88c-8d57-e59f92cf3400.png)

4. お持ちのアカウントでサインインします
![azl004.png](https://qiita-image-store.s3.amazonaws.com/0/61404/8adfd86a-02b3-cf3a-fa15-795cb7379d90.png)

5. APIのエンドポイントとキーを確認してください
![azl005.png](https://qiita-image-store.s3.amazonaws.com/0/61404/f58b0694-a1b9-b486-ff48-3ae2c5633b75.png)

※この記事では westcentralus.api.cognitive.microsoft.com/face/v1.0 をエンドポイントとしてAPIを実行しています。アカウントやキーの取得時期などによって変更となっている可能性がありますので、異なるエンドポイントとなっていた場合はそれに読み替えてください

以上で準備は完了です。無料で使える期間はAPIキーを取得してから1か月間で、APIの利用回数は1分あたり20回に制限されています

# Face API (Face Detection)を使ってみる

Face AIPはC#やJava、Rubyなどのプログラミング言語から使用できますが、手っ取り早くcURLを使ってRESTFULから実行してみます。Linuxやmacのターミナルから以下のコマンドを実行してください

```bash
$ curl -v -X POST "https://westcentralus.api.cognitive.microsoft.com/face/v1.0/detect?returnFaceId=true&returnFaceLandmarks=false&returnFaceAttributes=age,gender,headPose,smile,facialHair,glasses,emotion,hair,makeup,occlusion,accessories,blur,exposure,noise" \
-H "Content-Type: application/json" \
-H "Ocp-Apim-Subscription-Key: {subscription key}" \
--data-ascii '{"url":"https://upload.wikimedia.org/wikipedia/commons/c/c3/RH_Louise_Lillian_Gish.jpg"}'
```

オプションは [Quickstarts](https://docs.microsoft.com/ja-jp/azure/cognitive-services/face/quickstarts/curl) のと同じです。
`{subscription key}` の部分は各自取得したキーを入れてください

ターミナルからcurlを実行すると、以下のような結果が返ってきます

```text
* upload completely sent off: 88 out of 88 bytes
< HTTP/1.1 200 OK
< Cache-Control: no-cache
< Pragma: no-cache
< Content-Length: 978
< Content-Type: application/json; charset=utf-8
< Expires: -1
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< apim-request-id: 57d2a10b-57a6-49ca-bbcb-f76c4ccdd528
< Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
< x-content-type-options: nosniff
< Date: Sun, 17 Dec 2017 01:23:32 GMT
< 
* Connection #0 to host westcentralus.api.cognitive.microsoft.com left intact
[{"faceId":"6c123c30-0705-4b96-8da2-713c1313ca00","faceRectangle":{"top":131,"left":177,"width":162,"height":162},"faceAttributes":{"smile":0.0,"headPose":{"pitch":0.0,"roll":0.1,"yaw":-32.9},"gender":"female","age":22.9,"facialHair":{"moustache":0.0,"beard":0.0,"sideburns":0.0},"glasses":"NoGlasses","emotion":{"anger":0.0,"contempt":0.0,"disgust":0.0,"fear":0.0,"happiness":0.0,"neutral":0.986,"sadness":0.009,"surprise":0.005},"blur":{"blurLevel":"low","value":0.06},"exposure":{"exposureLevel":"goodExposure","value":0.67},"noise":{"noiseLevel":"low","value":0.0},"makeup":{"eyeMakeup":true,"lipMakeup":true},"accessories":[],"occlusion":{"foreheadOccluded":false,"eyeOccluded":false,"mouthOccluded":false},"hair":{"bald":0.0,"invisible":false,"hairColor":[{"color":"brown","confidence":1.0},{"color":"black","confidence":0.87},{"color":"other","confidence":0.51},{"color":"blond","confidence":0.08},{"color":"red","confidence":0.08},{"color":"gray","confidence":0.02}]}}}]
```

このJSONを読み解くとこのような情報が入ってました

| JSON項目 | 値 | 説明 |
|:--------|:---|:-----|
|faceId| 6c123c30-0705-4b96-8da2-713c1313ca00 | 後述する Face Recongnition など認識結果を別のAPIで使用するためのID。解析してから24時間有効 |
|faceRectangle| {<br>"top":131,<br>"left":177,<br>"width":162,<br>"height":162<br>} | 検出した顔の座標 |
|faceAttributes| {<br>"smile":0.0,<br>"headPose":{"pitch":0.0,"roll":0.1,"yaw":-32.9<br>} | 笑顔のスコア、顔の傾き |
|gender| female | 性別 |
|age| 22.9 | 年齢 |
|facialHair| {<br>"moustache":0.0,<br>"beard":0.0,<br>"sideburns":0.0<br>} | 顔の髪(口ひげ、あごひげ、ほおひげ) |
|glasses| NoGlasses | 眼鏡をかけているか |
|emotion| {<br>"anger":0.0,<br>"contempt":0.0,<br>"disgust":0.0,<br>"fear":0.0,<br>"happiness":0.0,<br>"neutral":0.986,<br>"sadness":0.009,<br>"surprise":0.005<br>} | 感情(怒り、恥ずかしい、うんざり、恐怖、幸せ、無表情、悲しみ、驚き) |
|blur| {<br>"blurLevel":"low",<br>"value":0.06<br>} | 霞み具合 |
|exposure| {<br>"exposureLevel":"goodExposure",<br>"value":0.67<br>} | 露出度 |
|noise| {<br>"noiseLevel":"low",<br>"value":0.0<br>} | ノイズレベル |
|makeup| {<br>"eyeMakeup":true,<br>"lipMakeup":true<br>} | 化粧をしているか(アイメイク、唇) |
|accessories| [] | アクセサリを付けているか |
|occlusion| {<br>"foreheadOccluded":false,<br>"eyeOccluded":false,<br>"mouthOccluded":false<br>} | 顔を塞いでいるか(額、目、口) |
|hair| {<br>"bald":0.0,<br>"invisible":false,<br>"hairColor":[<br>{"color":"brown","confidence":1.0},<br>{"color":"black","confidence":0.87},<br>{"color":"other","confidence":0.51},<br>{"color":"blond","confidence":0.08},<br>{"color":"red","confidence":0.08},<br>{"color":"gray","confidence":0.02}]<br>} | 髪の毛(ハゲ度合、見えないか？、髪の色・茶/黒/他/金/赤/灰の各信頼度) |

# 同一人物である可能性を検証する

次に、2つの画像を使って同一人物であるか検証してみます。自分の画像を出すのは見苦しいし、赤の他人の画像を使うのも肖像権うんぬんがあるので、ここでは [PAKUTASO](https://www.pakutaso.com/) 様の素材を使わせていただきました

基準となる画像
![00_PP42_PP_TP_V.jpg](https://qiita-image-store.s3.amazonaws.com/0/61404/2708de7b-74df-91a8-e96c-b2a475d7ff55.jpeg)

比較画像1 (同一人物)
![maxIMGL8927_TP_V.jpg](https://qiita-image-store.s3.amazonaws.com/0/61404/13292e88-9fa3-2453-2cf5-cf2b7d9d51f8.jpeg)

比較画像2 (同一人物)
![YUSEI_9V9A5379_TP_V.jpg](https://qiita-image-store.s3.amazonaws.com/0/61404/b329483d-d6e0-8be3-fa40-fbd0b51a95cc.jpeg)

## まず、基準となる画像を登録します

登録する顔がどんな人なのかを紐づけするために、Group と Person を作成します
ここでは、Groupを「MyFriend」、Persionを「John」としました

### 1.Group を作成する

```shell-session
$ curl -v -X PUT "https://westcentralus.api.cognitive.microsoft.com/face/v1.0/persongroups/my_friend" \
-H "Content-Type: application/json" \
-H "Ocp-Apim-Subscription-Key: {subscription key}" \
--data-ascii '{"name":"MyFriend","userData":"virtual friends that for lonely man"}'
```

ここでは my_friend という名前でgroupIdを作成しています。別の groupId を作るには URL 末尾の "my_friend" を変更してください。ただし、groupIdはアルファベット小文字か数字、'-'、'_'、で64文字以内としてください

```text:例)実行結果
> PUT /face/v1.0/persongroups/my_friend HTTP/1.1
> Host: westcentralus.api.cognitive.microsoft.com
> User-Agent: curl/7.56.1
> Accept: */*
> Content-Type: application/json
> Ocp-Apim-Subscription-Key: 9c02e4795a654184b71da8b4fdb3f9c5
> Content-Length: 68
> 
* upload completely sent off: 68 out of 68 bytes
< HTTP/1.1 200 OK
< Cache-Control: no-cache
< Pragma: no-cache
< Content-Length: 0
< Expires: -1
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< apim-request-id: a1b92c05-61dc-484d-b8be-564d970952b4
< Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
< x-content-type-options: nosniff
< Date: Thu, 21 Dec 2017 06:39:57 GMT
< 
* Connection #0 to host westcentralus.api.cognitive.microsoft.com left intact
```

### 2.Person を作成する

```shell-session
$ curl -v -X POST "https://westcentralus.api.cognitive.microsoft.com/face/v1.0/persongroups/my_friend/persons" \
-H "Content-Type: application/json" \
-H "Ocp-Apim-Subscription-Key: {subscription key}" \
--data-ascii '{"name":"John","userData":"a friend met from Google search"}'
```

nameとuserData(Optional)は任意に決めてください

```text:例)実行結果
> POST /face/v1.0/persongroups/my_friend/persons HTTP/1.1
> Host: westcentralus.api.cognitive.microsoft.com
> User-Agent: curl/7.56.1
> Accept: */*
> Content-Type: application/json
> Ocp-Apim-Subscription-Key: 9c02e4795a654184b71da8b4fdb3f9c5
> Content-Length: 60
> 
* upload completely sent off: 60 out of 60 bytes
< HTTP/1.1 200 OK
< Cache-Control: no-cache
< Pragma: no-cache
< Content-Length: 51
< Content-Type: application/json; charset=utf-8
< Expires: -1
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< apim-request-id: 2358fef1-d4c1-48fe-8b12-6370cc848fff
< Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
< x-content-type-options: nosniff
< Date: Thu, 21 Dec 2017 06:44:17 GMT
< 
* Connection #0 to host westcentralus.api.cognitive.microsoft.com left intact
{"personId":"e1367efa-f024-4817-83c8-3f8b13cd0979"}
```

personId がレスポンスに返ってくるのでメモしておきます

### 3.Person に顔を追加する

例に挙げている基準となる顔の画像を指定します

RESTfulの書式は次の通りです。groupIdとpersonIdは適宜読み替えてください

```text:書式
https://westcentralus.api.cognitive.microsoft.com/face/v1.0/persongroups/{groupId}/persons/{personId}/persistedFaces
```

```shell-session
$ curl -v -X POST "https://westcentralus.api.cognitive.microsoft.com/face/v1.0/persongroups/my_friend/persons/e1367efa-f024-4817-83c8-3f8b13cd0979/persistedFaces" \
-H "Content-Type: application/json" \
-H "Ocp-Apim-Subscription-Key: {subscription key}" \
--data-ascii '{"url":"https://camo.qiitausercontent.com/a9b67ed8072799063452a1291cd49787c4d4b86c/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f36313430342f32373038646537622d373464662d393161382d653936632d6232613437356437666635352e6a706567"}'
```

```text:例)実行結果
> POST /face/v1.0/persongroups/my_friend/persons/e1367efa-f024-4817-83c8-3f8b13cd0979/persistedFaces HTTP/1.1
> Host: westcentralus.api.cognitive.microsoft.com
> User-Agent: curl/7.56.1
> Accept: */*
> Content-Type: application/json
> Ocp-Apim-Subscription-Key: 9c02e4795a654184b71da8b4fdb3f9c5
> Content-Length: 269
> 
* upload completely sent off: 269 out of 269 bytes
< HTTP/1.1 200 OK
< Cache-Control: no-cache
< Pragma: no-cache
< Content-Length: 58
< Content-Type: application/json; charset=utf-8
< Expires: -1
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< apim-request-id: 9b0b7cd0-69c6-40f0-97e1-4e806de70063
< Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
< x-content-type-options: nosniff
< Date: Thu, 21 Dec 2017 06:49:42 GMT
< 
* Connection #0 to host westcentralus.api.cognitive.microsoft.com left intact
{"persistedFaceId":"7d70b5b4-0037-4961-a13d-b74a147124a6"}
```

## 比較してみます

### 1.比較画像1をFace Detectionにかけます

```shell-session
$ curl -v -X POST "https://westcentralus.api.cognitive.microsoft.com/face/v1.0/detect?returnFaceId=true&returnFaceLandmarks=false&returnFaceAttributes=age,gender" \
-H "Content-Type: application/json" \
-H "Ocp-Apim-Subscription-Key: {subscription key}" \
--data-ascii '{"url":"https://camo.qiitausercontent.com/f8b6bfedb951332ff070f18cc54b8ef344beb19a/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f36313430342f31333239326538382d396661332d323435332d326366352d6366326237643964353166382e6a706567"}'
```

```text:例)実行結果
> POST /face/v1.0/detect?returnFaceId=true&returnFaceLandmarks=false&returnFaceAttributes=age,gender HTTP/1.1
> Host: westcentralus.api.cognitive.microsoft.com
> User-Agent: curl/7.56.1
> Accept: */*
> Content-Type: application/json
> Ocp-Apim-Subscription-Key: 9c02e4795a654184b71da8b4fdb3f9c5
> Content-Length: 269
> 
* upload completely sent off: 269 out of 269 bytes
< HTTP/1.1 200 OK
< Cache-Control: no-cache
< Pragma: no-cache
< Content-Length: 161
< Content-Type: application/json; charset=utf-8
< Expires: -1
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< apim-request-id: bc03dfad-709a-4efd-8dcc-42260cf13d1a
< Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
< x-content-type-options: nosniff
< Date: Thu, 21 Dec 2017 07:08:41 GMT
< 
* Connection #0 to host westcentralus.api.cognitive.microsoft.com left intact
[{"faceId":"575ac45e-abb0-44d7-9cd2-99830fbc232d","faceRectangle":{"top":242,"left":996,"width":175,"height":175},"faceAttributes":{"gender":"male","age":37.2}}]
```

faceIdをメモします

### 2.groupIdとpersonId、Face DetectionしたfaceIdを指定してverifyします

```shell-session
$ curl -v -X POST "https://westcentralus.api.cognitive.microsoft.com/face/v1.0/verify" \
-H "Content-Type: application/json" \
-H "Ocp-Apim-Subscription-Key: {subscription key}" \
--data-ascii '{"faceId":"575ac45e-abb0-44d7-9cd2-99830fbc232d","personId":"e1367efa-f024-4817-83c8-3f8b13cd0979","personGroupId":"my_friend"}'
```

```text:例)実行結果
> POST /face/v1.0/verify HTTP/1.1
> Host: westcentralus.api.cognitive.microsoft.com
> User-Agent: curl/7.56.1
> Accept: */*
> Content-Type: application/json
> Ocp-Apim-Subscription-Key: 9c02e4795a654184b71da8b4fdb3f9c5
> Content-Length: 127
> 
* upload completely sent off: 127 out of 127 bytes
< HTTP/1.1 200 OK
< Cache-Control: no-cache
< Pragma: no-cache
< Content-Length: 41
< Content-Type: application/json; charset=utf-8
< Expires: -1
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< apim-request-id: d13c7f31-0ef7-4cbe-9f36-51ac384fcaec
< Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
< x-content-type-options: nosniff
< Date: Thu, 21 Dec 2017 07:11:50 GMT
< 
* Connection #0 to host westcentralus.api.cognitive.microsoft.com left intact
{"isIdentical":true,"confidence":0.61687}
```

isIdentical が true であれば同一人物と判定されています。confidence は0.0 ～ 1.0の間で高い程確実性があります

ちなみに、比較画像2でverifyしたところfalseになってました。髪型も変わっているので判別は難しかったのかもしれません

```比較画像2とのverify結果
> POST /face/v1.0/verify HTTP/1.1
> Host: westcentralus.api.cognitive.microsoft.com
> User-Agent: curl/7.56.1
> Accept: */*
> Content-Type: application/json
> Ocp-Apim-Subscription-Key: 9c02e4795a654184b71da8b4fdb3f9c5
> Content-Length: 127
> 
* upload completely sent off: 127 out of 127 bytes
< HTTP/1.1 200 OK
< Cache-Control: no-cache
< Pragma: no-cache
< Content-Length: 42
< Content-Type: application/json; charset=utf-8
< Expires: -1
< X-AspNet-Version: 4.0.30319
< X-Powered-By: ASP.NET
< apim-request-id: 996bb8b7-ee39-464c-b9ed-ac8ae4271b7a
< Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
< x-content-type-options: nosniff
< Date: Thu, 21 Dec 2017 07:18:43 GMT
< 
* Connection #0 to host westcentralus.api.cognitive.microsoft.com left intact
{"isIdentical":false,"confidence":0.45508} 
```

# もっと詳しく

Face APIの使い方は [Overview](https://docs.microsoft.com/ja-jp/azure/cognitive-services/face/overview) に書かれています

認識する画像にURLを指定してましたが、 [ドキュメント](https://westus.dev.cognitive.microsoft.com/docs/services/563879b61984550e40cbbe8d/operations/563879b61984550f30395236) を見ると、Request Bodyの所に `To detect in a URL (or binary data) specified image. ` と書かれていたので、ローカルファイルをアップロードして解析にかけることも出来そうです。

# さいごに

画像認識といえば、学習データを登録して、誤検知があればそれを取り除いていって、という地道で泥臭い作業が必要で、まともに使えるまでにはかなり手間がかかるような物というイメージでした。私は一時期、スパムメールのフィルターに関する仕事をやっていたことがあり、機械学習の大変さもある程度感じてました。Face APIのように簡単に画像認識が使えるのは、すごい時代になったと思います

今回は、簡単な紹介ということでライトな記事になりましたが、これをコマンド化して防犯カメラに仕掛けるのも面白いかと思いました。家族以外が映っていたらすぐにわかるようになるので防犯面にも役立ちます

人感センサーが反応したらカメラが画像をキャプチャして、画像認識をかける仕組みなら簡単に作れそうです。ただ、無料枠ではトランザクション数が限られてしまうので、どこまで運用に耐えられるのかは考えなければならないです