FaceAPIをRubyから使う

この記事は[Microsoft Azure Cognitive Services Advent Calendar 2020](https://qiita.com/advent-calendar/2020/cognitive)の21日目の記事です(大遅刻)。
RubyをやっててAIによる画像分析を簡単な形で使ってみたいな、という初心者向けの記事になります。


# FaceAPIとは
Microsoft Azureの提供する構築済みAIによる人間の特徴分析機能です。
AWS,GCP等各社がクラウドベースのAIを提供する中で私がこのサービスを選定したのはデフォルトで「年齢・性別」の機能を使えるからです。

Ledgeのまとめ記事が大変参考になりました。->[顔認識・顔検出とは？6つの顔認識APIの機能・精度・価格を比較！|Ledge.ai] (https://ledge.ai/faceapi/)

FaceAPIのデモページが[こちら](https://azure.microsoft.com/ja-jp/services/cognitive-services/face/#demo)にあるのでどういったパラメータが取得できるのか気軽に試せます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609338/e6da2057-5bd3-6a4a-c57b-323c2f65cd4b.png)

ちなみに以前は右側のレスポンス欄の"faceattributes"に年齢と性別が返ってきていたのですが最近(要出典)はnullが返ってくるようになってますね。




# 利用準備
[こちら](https://azure.microsoft.com/ja-jp/account/)からAzureの登録をしてきましょう。
すでにOffice365などでメールアドレスを取得されてる方はフローが少し楽になるのかしら?(未確認)

登録してきたらFaceAPIを使うためのサブスクリプションキーを発行します。

[Azureのポータル](https://portal.azure.com/#home)に移動して「新しいリソースを作成」します。

新規リソースの作成画面はこのようになっていて、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609338/0da1a583-0690-c624-f19f-eaca45f940b7.png)
件の FaceAPI用のエンドポイント作成は矢印の先の「[Face](https://portal.azure.com/#create/Microsoft.CognitiveServicesFace)」から行います。

画面の指示にしたがって

- リソースグループ (使いたいサービス:この場合FaceAPI)
- リージョン (多くの場合東日本と西日本になると思いますが、リージョンごとにそれぞれエンドポイントが変わります。)
- 名前 (一意でユニークなものにしましょう)
- 価格レベル (Freeで多くの場合十分だと思います。価格設定は詳細から確認してください。)

を設定し、「確認と作成」を押したら流れで作成できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609338/b0f7206b-b16a-23ef-2f0c-b41a71d6cc50.png)

作成したら先ほどのポータルのホームに戻ると設定した名前でリソースが作成されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609338/6d99b942-bfb8-3973-4bf3-a87a42c99cc3.png)

これをクリックすると、各種設定画面に移ります。
キーとエンドポイントタブをクリックするとAPIにリクエストを飛ばすためのキーが確認できます。
このページで表示されるエンドポイントは今回は使いません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609338/72d0b6ee-c7db-29b1-8e40-86c105b80d77.png)

# RubyでAPIをコールする
検索上位でヒットする[日本語ドキュメント](https://docs.microsoft.com/ja-jp/azure/cognitive-services/face/quickstarts/client-libraries?tabs=visual-studio&pivots=programming-language-csharp)にはPython,C#,GoおよびREST (curl)でのサンプルしか載っていませんが、Cognitive Servicesのページを深掘りすると[もう少し多くの言語でのサンプルが載ってるドキュメント](https://japanwest.dev.cognitive.microsoft.com/docs/services/563879b61984550e40cbbe8d/operations/563879b61984550f30395236)があります。

ページ下部に以下のような部分があるのでこちらをコピペしてそのまま骨組みになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609338/ba1f5ab2-acaf-d97e-ed09-8034fff7d9c9.png)

全文がこのようになってます。

```Ruby:Rubyのサンプル

require 'net/http'

uri = URI('https://japanwest.api.cognitive.microsoft.com/face/v1.0/detect')
uri.query = URI.encode_www_form({
    # Request parameters
    'returnFaceId' => 'true',
    'returnFaceLandmarks' => 'false',
    'returnFaceAttributes' => '{string}', #ここを変える①
    'recognitionModel' => 'recognition_03',
    'returnRecognitionModel' => 'false',
    'detectionModel' => 'detection_02', #ここを変える②
    'faceIdTimeToLive' => '86400'
})

request = Net::HTTP::Post.new(uri.request_uri)
# Request headers
request['Content-Type'] = 'application/json'
# Request headers
request['Ocp-Apim-Subscription-Key'] = '{subscription key}' #ここを変える③
# Request body
request.body = "{body}" #ここを変える④

response = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
    http.request(request)
end

puts response.body

```

コード中の「ここを変える」の部分を変えると今回の目的である「年齢と性別」をレスポンスとして得られるようになります。

## どんなリクエストを飛ばしたいか
それぞれの値を変える解説をする前に、「Curl」のサンプルを見てもらうとわかりますが、APIに投げるリクエストはuriの形をとります。
サンプルドキュメントにはこの記事の冒頭で触れたデモのような[コンソール](https://japanwest.dev.cognitive.microsoft.com/docs/services/563879b61984550e40cbbe8d/operations/563879b61984550f30395236/console)が用意されていて、こちらでCurlで投げるuriの作成ができます。

先に載せたRubyのコードでは`uri.query`の中身にクエリパラメータとして各種文字列を入力しています。

それを踏まえて、実際に送られるuriは以下のようになります。

```
https://japanwest.api.cognitive.microsoft.com/face/v1.0/detect?returnFaceId=true&returnFaceLandmarks=false&recognitionModel=recognition_03&returnRecognitionModel=false&detectionModel=detection_01&faceIdTimeToLive=86400
```
なおこのパラメータもコンソールのページで実際にリクエストとして投げられて正しい値を入れると`200 ok`が返ってきます。

また、指定するエンドポイントとして`japanwest.api.cognitive.microsoft.com`としていますが、東日本リージョンを指定した場合は`japaneast.api.cognitive.microsoft.com`となります。


## ここを変える①

```
'returnFaceAttributes' => '{string}'
```
この`FaceAttributes`にはレスポンスとして返したい(=分析したい属性)を入力します。

例えば「年齢と性別」を返したい場合、

```
'returnFaceAttributes' => 'age,gender'
```
とすると良いでしょう。
なお他には`accessories`を指定すればマスクやメガネをつけているか否かが、`makeup`を指定すればお化粧をしているかすっぴんか否かが返ってきたりします。詳しくはドキュメントを読みましょう。

## ここを変える②

```
'detectionModel' => 'detection_02'
```
上記の`detection_02`を`detection_01`に変えます。

感情分析や年齢等の識別機能を使うには、同じクエリパラメータの`recognitionModel`で`recognition_03`を指定してつかいますが、現時点では新しい人の顔の検出モデルである`detection_02`は`recognition_03`に対応していません。そのため`detection_01`をつかいます。

## ここを変える③

```
request['Ocp-Apim-Subscription-Key'] = '{subscription key}'
```

`Ocp-Apim-Subscription-Key`として指定するのは*利用準備*の項目で触れた「キー１」で、これを`subscription key`として使います。
ローカルで動かすだけであればそのまま書いても差し支えないと思いますが、リモート上のリソースとするのであればyamlファイルに記述したり環境変数を管理できる仕組みを入れた上でファイルの外に参照できるようにしておくことをお勧めします。

以上で下準備は終わりです。


## ここを変える④

```
request.body = "{body}"
```
bodyには画像のURL、またはバイナリとして展開したjpegなどが指定できます。
今回の例ではサンプルとしてPR Timesさんの記事より、Matzとロケッタ社のお二人(鳴瀬さんと清水さん)が写っている画像をお借りしてきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609338/4a040df3-6901-a61a-d0b8-ddad7d846ce5.png)

画像のアドレス:https://prtimes.jp/i/35716/3/resize/d35716-3-604425-6.jpg

ローカルにあるファイルを送信する場合は

```Ruby:Ruby
file_path = ARGV.shift
file_data = File.open(file_path, "r+b")
request.body = file_data.read
file_data.close
```
のように記述すると良いかと思います。

## どんなレスポンスが返ってくるか。

以上の準備を整えた上で、ローカルでソースコードを実行すると、


リクエストがjson形式で返ってきて、hashとしてパースすると、

```
{"faceId"=>"a7d7d489-b657-4149-a2d8-e7b1b8af9aa5", "faceRectangle"=>{"top"=>69, "left"=>438, "width"=>62, "height"=>62}, "faceAttributes"=>{"gender"=>"male", "age"=>33.0}}
{"faceId"=>"b07990e7-d0b3-4030-bd2a-41bbcf86de7d", "faceRectangle"=>{"top"=>71, "left"=>126, "width"=>58, "height"=>58}, "faceAttributes"=>{"gender"=>"male", "age"=>23.0}}
{"faceId"=>"26876e61-d6fb-482c-b43b-3e7d4410df52", "faceRectangle"=>{"top"=>93, "left"=>286, "width"=>57, "height"=>57}, "faceAttributes"=>{"gender"=>"male", "age"=>44.0}}
```

のような形で得られます。

人間の顔がDetectされた位置情報もついでに取得できているのでこれらをもとに矩形を描画すると以下のようになります。

![generated14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609338/b1330278-f0d1-51b4-18dc-8d01ee9f09fe.png)


matzの年齢は44才と撮影当時で10才前後若く出ていました。
う〜〜ん、エンジニアはやはり見た目が若い人が多い...

# その他のポイント

多かれ少なかれ、学習を行ったデータセットによってバイアス生じます。
画像認識、および機械学習が私の専門ではないのとドキュメントやテクニシャンからの評価を深掘りできていないのですが、あるいはrecognitionモデルにも人種的なモデルの偏りがあれば、東アジア系の顔立ちだと年齢が若く出る傾向があったりするかも？
また、性別は男女の二値ではありますが、髪が長く輪郭を隠した男性が女性として判別されやすかったりする例もあります。
これらは推程度を百分率で返したりもできるので、上手に使ってAIでの判別を行えるようになれば新たな知識を得るきっかけになると思います。


# 終わりに
この手のプラットフォームはUIをはじめ提供サービスの詳細が割と頻繁に変わりますのでできるだけ最新情報を探されることをお勧めします。(釈迦に説法)


Githubに[サンプルとドキュメント](https://github.com/MicrosoftDocs/azure-docs.ja-jp/blob/master/articles/cognitive-services/Face/QuickStarts/Ruby.md)が用意してありますのでこちらも参考にしてみてください。

記事内で使わせていただいた画像サンプルはこちらからお借りしてきました。
URL: https://prtimes.jp/main/html/rd/p/000000003.000035716.html
著作権や肖像権は全て元画像の権利者に帰属します。


Ruby3.0.0リリースおめでとうございます！！！(2020/12/25)