似た顔のFANZA女優を顔写真から検索出来るようにした

この記事は [驚異のFANZA女優検索](https://fanza-actress-bust-search.azurewebsites.net) Advent Calendar 2021 の 2 日目の記事です。

# 😘似た顔のFANZA女優検索
[似た顔のFANZA女優を検索](https://fanza-actress-bust-search.azurewebsites.net/nearFace)出来るページを作った。顔写真の画像はURLを入力するか、ローカルからファイルをアップロードすることで登録できる。登録した画像の顔が認識出来れば、似たFANZA女優の顔がスコアの高い順に表示されるという仕組みだ。対象となるFANZA女優は顔写真が提供されていてかつ顔認識に成功している人に限定されるので実質1万人ぐらいだ。作ったきっかけは[はてなブックマークコメント](https://b.hatena.ne.jp/entry/s/qiita.com/fabsabsfabsabs/items/e8e3b717dc311ac2176d)駆動？

# サンプル画像
入力してもらう手間を下げるためサンプル画像をいくつか置いておくのが親切かと思った。最初はFANZA女優を並べようかと思ったが、本人の写真では信頼度が100%になってしまうので面白くない。かといって作品のパッケージだと色々と表現的に不味いもののばかりだ。[ぱくたそ](https://www.pakutaso.com)の[モデルリリース](https://www.pakutaso.com/modelrelease.html)を取得した顔写真を使おうと思ったが規約的にまずそう。凄く悩んだがFANZAの[商品情報API](https://affiliate.dmm.com/api/v3/itemlist.html)の画像なら使えることに気づいてアイドルの作品を並べてみた。結果的には面白いサンプルになったと思う。

# 登録しているFAZNZA女優は1人1枚のみ
顔認識には1人の顔写真を沢山登録して、ある顔写真が、その沢山登録した人と同一人物かをみる方法がある。しかし、今回似た顔検索で使ったのはその方法ではなく、単純に沢山の顔写真を登録してその中から自分が指定した顔写真と近い顔を信頼度順に並べるという方法だ。つまり、1人1枚の顔写真との比較なので、例え本人の別の顔写真を登録しても必ずしも本人が1番になるとは限らない（もちろん1番にヒットすることも多々ある）。本人であっても違う顔写真であれば信頼度は大体70%ぐらいかなという印象。では別人で70%以上ならそっくりかというとやっぱりばらつきがある。この辺は主観になってくるので、実際に遊んでもらえればと思う。

#Azure Cognitive ServicesのFace API
顔認識にはAzure Cognitive Servicesの[Face API](https://azure.microsoft.com/ja-jp/services/cognitive-services/face/#overview)を使用しているのでこの使い方を書く。顔の登録には[FaceList](https://japaneast.dev.cognitive.microsoft.com/docs/services/563879b61984550e40cbbe8d/operations/563879b61984550f30395250)と[LargeFaceList](https://japaneast.dev.cognitive.microsoft.com/docs/services/563879b61984550e40cbbe8d/operations/5a158c10d2de3616c086f2d3)があるが、FaceListは「Add a face to a specified face list, up to 1,000 faces.」という記載あった。FANZA女優は5万人いるので、FaceListでは上限オーバーしてしまう。そのためLargeFaceListを使った（但し、LargeFaceListに「Free-tier subscription quota: 1,000 faces per large face list.」とあるので有料版でしか1000人以上は登録できないかも。未検証）。

#Face APIで顔リスト作成
先ずはリストを作る。[LargeFaceList - Create](https://japaneast.dev.cognitive.microsoft.com/docs/services/563879b61984550e40cbbe8d/operations/5a157b68d2de3616c086f2cc/console)。これはリンクのHP上で作ってしまってもよい。nameは単に認識用なので日本語が分かりやすいが、自分はIdと同じにした。recognitionModelは省略可能だが、省略するとrecognition_01になる。当然だが新しい方が精度が高い。実際最初recognitionModelを指定せずに試したが、recognition_04にした方が顔認識度は高かった。

```html
https://japaneast.api.cognitive.microsoft.com/face/v1.0/largefacelists/{largeFaceListId}
```

```json
{
    "name": "large-face-list-name",
    "recognitionModel": "recognition_04"
}
```

[LargeFaceList - Add Face](https://japaneast.dev.cognitive.microsoft.com/docs/services/563879b61984550e40cbbe8d/operations/5a158c10d2de3616c086f2d3/console)を呼ぶと顔を登録出来る。こちらもdetectionModelは省略できるが、detection_03とした方が良い。実際recognitionModelとdetectionModelを組み合わせた検証はしていない。ただ言えることはrecognition_01×detection_01よりもrecognition_04×detection_03の方が精度が良かったことは間違いない。まあ特に理由がなければバージョンは最新に越したことはないと思う。

```html
https://japaneast.api.cognitive.microsoft.com/face/v1.0/largefacelists/{largeFaceListId}/persistedfaces?detectionModel=detection_03
```
これで画像を登録するとレスポンスでpersistedFaceIdが返ってくる。実際に近い顔として返ってくるのはこのIdなので記録しておく必要がある。

```cs
public async Task<string> AddFaceAsync(string imageUrl)
{
    var url = $"{AzureFaceApiUrl}largefacelists/fanza_actress_face_list_id/persistedfaces?detectionModel=detection_03"; 
    var image = await GetImageContent(imageUrl); 
    var personResponse = await HttpClient.PostAsync(url, image);
    var json = await personResponse.Content.ReadAsStringAsync();
    var largeFaceListResponse = JsonSerializer.Deserialize<LargeFaceListResponse>(json);
    return string.IsNullOrEmpty(largeFaceListResponse.persistedFaceId) ? "" : largeFaceListResponse.persistedFaceId;
}
```
これで登録したい顔を全て登録したら[LargeFaceList - Train](https://japaneast.dev.cognitive.microsoft.com/docs/services/563879b61984550e40cbbe8d/operations/5a158422d2de3616c086f2d1)でトレーニングをすれば準備は完了だ。

#Face APIでfindsimilars
先ずはユーザーが選択した画像を[Face - Detect](https://japaneast.dev.cognitive.microsoft.com/docs/services/563879b61984550e40cbbe8d/operations/563879b61984550f30395236)する。

```html
https://japaneast.api.cognitive.microsoft.com/face/v1.0/detect?recognitionModel=recognition_04&detectionModel=detection_03
```
これで画像から顔を認識が出来れば、レスポンスが返ってくる。レスポンスからfaceIdを設定して[Face - Find Similar](https://japaneast.dev.cognitive.microsoft.com/docs/services/563879b61984550e40cbbe8d/operations/563879b61984550f30395237/console)を呼ぶ。ランク付けされた類似の顔を返したいならmodeはmatchFaceにする。

```html
https://japaneast.api.cognitive.microsoft.com/face/v1.0/findsimilars
```

```json
{
    "faceId": "detectで返ってきたID",
    "largeFaceListId": "fanza_actress_face_list_id",
    "maxNumOfCandidatesReturned": 10,
    "mode": "matchFace"
}
```

これで先ほど登録した顔がmaxNumOfCandidatesReturnedの数分だけpersistedFaceIdとconfidenceが返ってくるので、confidenceでソートしてやれば晴れて、似た顔にリストが作れる。

#つづく
さて、顔写真から[似た顔のFANZA女優を検索](https://fanza-actress-bust-search.azurewebsites.net/nearFace)出来るようにしたのにAzure Cognitive ServicesのFace APIを使いこなすよりももっと苦労したことがある。それはまた明日。