Face APIで同一人物検出アプリを作ってみた

### 概要
学校の卒業アルバムや旅行のアルバムなど大勢の人が映るアルバムでは、特定の人が偏って掲載されていると不公平になってしまうと思います。
そのため同じ人が映っている写真を均等に掲載させたいところですが、膨大な枚数の写真から同じ人が映っている写真を手作業で1枚1枚チェックしようとするととても時間がかかります。
そこで、同一人物をAIに検出させ、分類をさせる簡単なWebアプリを作りました。

### Cognitive Services Face API
* Azureが提供するサービス「[Cognitive Services](https://docs.microsoft.com/ja-jp/azure/cognitive-services/what-are-cognitive-services)」の1つで、人物の顔を検出して年齢や性別などの情報を取得をしたり、タグ付けや学習を行うことができます。
   * 詳細は[こちら](https://azure.microsoft.com/ja-jp/services/cognitive-services/face/#demo)をご参照ください。

今回はそれらのうちFace DetectとFace Groupを組み合わせて、同一人物検出アプリを作成しました。

#### [Face Detect](https://japaneast.dev.cognitive.microsoft.com/docs/services/563879b61984550e40cbbe8d/operations/563879b61984550f30395236)
* 画像から顔を検出し、Face IDを発行したり、顔の様々な情報を取得するAPIです。
* APIを試した結果はこちらです。
    * リクエスト
        * 下記のように画像URLまたはバイナリデータをリクエストに入れます。
        
        ```json
        {
            "url": "http://example.com/1.jpg"
        }
        ```
    * レスポンス
        * Face IDと顔の座標が取得できます。
        * `emotion`の属性情報から人の感情も取得できます。「幸せ」「怒り」「悲しみ」など様々な感情がそれぞれ何%マッチしているのかがスコアリングされます。
            * その他、性別や年齢などの属性情報も取得できます。詳しくは[こちら](https://docs.microsoft.com/ja-jp/azure/cognitive-services/face/concepts/face-detection#attributes)をご参照ください。
        * 1件の画像に人物が複数映っている場合も、それぞれ情報が取得できます。下記のサンプルでは2名の人物が検出されています。
        
    ```json
    [
        {
            "faceId": "ae03abe4-3358-450b-b869-599d54200cb1",
            "faceRectangle": {
                "top": 347,
                "left": 893,
                "width": 135,
                "height": 135
            },
            "faceAttributes": {
                "emotion": {
                    "anger": 0.0,
                    "contempt": 0.001,
                    "disgust": 0.0,
                    "fear": 0.0,
                    "happiness": 0.633,
                    "neutral": 0.366,
                    "sadness": 0.0,
                    "surprise": 0.0
                }
            }
        },
        {
            "faceId": "29c098a5-3bab-48f0-9886-5a62f9d4f155",
            "faceRectangle": {
                "top": 342,
                "left": 1247,
                "width": 132,
                "height": 132
            },
            "faceAttributes": {
                "emotion": {
                    "anger": 0.0,
                    "contempt": 0.0,
                    "disgust": 0.001,
                    "fear": 0.0,
                    "happiness": 0.0,
                    "neutral": 0.34,
                    "sadness": 0.658,
                    "surprise": 0.0
                }
            }
        }
    ]
    ```

#### [Face Group](https://japaneast.dev.cognitive.microsoft.com/docs/services/563879b61984550e40cbbe8d/operations/563879b61984550f30395238)
* 上述のFace Detectで取得したFace IDを同一人物でグルーピングするAPIです。
* APIを試した結果はこちらです。
    * リクエスト
        * Face Detectで取得した`faceId`を`faceIds`のリストに入れます。
       
    ```json
    {
        "faceIds": [
            "b9bdc395-2def-4b2a-9cc0-4bd0cbd2171d",
            "19b09186-f45c-4102-8eee-f35da431b7ee",
            "88d5dccd-a678-4953-b4be-867ee92fd676",
            "8964ecbb-c7ac-4717-a882-9a4248583a90",
            "88403595-ebed-4562-8e9b-ed845f183217"
        ]
    }
    ```

    * レスポンス
        * `groups`に同一人物のリストが表示されました。
        * `messyGroup`はグルーピングされなかった＝検出された顔が1件のみのIDのリストです。
        
    ```json
    {
        "groups": [
            [
                "b9bdc395-2def-4b2a-9cc0-4bd0cbd2171d",
                "8964ecbb-c7ac-4717-a882-9a4248583a90"
            ],
            [
                "88d5dccd-a678-4953-b4be-867ee92fd676", 
                "19b09186-f45c-4102-8eee-f35da431b7ee"
            ]
        ],
        "messyGroup": [
            "88403595-ebed-4562-8e9b-ed845f183217"
        ]
    }
    ```

### Webアプリに組み込んでみた
Face APIの仕様を確認したところで、さっそくWebアプリケーションに組み込んでみましょう。

#### 処理フロー
![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/962964/c5621d76-8ea7-c3cd-b8f8-6928405d184b.png)

処理フローの詳細は下記の通りです。
データの状態はCosmos DBに保管します。

1. ユーザーは画像をアップロードする
1. アップロードした画像は[Azure BLOBストレージ](https://docs.microsoft.com/ja-jp/azure/storage/blobs/storage-blobs-introduction)に保管
1. Face Detect APIで人物の顔を検出する
1. Face Group APIで同一人物をグルーピングする
1. グルーピング結果を画面に表示する

#### 使用したライブラリ
このようなSPA & Backendの構成でWebアプリを作成しました。

* Frontend: Angular 10
* Backend: ASP .NET Core 3.1
    * Face API SDK: [Microsoft.Azure.CognitiveServices.Vision.Face](https://www.nuget.org/packages/Microsoft.Azure.CognitiveServices.Vision.Face/2.6.0-preview.1)
    * BLOBストレージSDK: [Azure.Storage.Blobs](https://www.nuget.org/packages/Azure.Storage.Blobs/12.7.0)

#### APIの呼び出し方（.NET SDK）
Face APIでは[.NET環境向けのSDK](https://www.nuget.org/packages/Microsoft.Azure.CognitiveServices.Vision.Face/2.6.0-preview.1)が提供されているため、そちらを使ったAPIの呼び出し例をご紹介します。

1. FaceClientの作成
    * Cognitive Services サブスクリプションキーとエンドポイントを下記の通り指定し、クライアントを作成します。
    * FaceClientはDisposableなクライアントなので、staticなクラス変数とし、コンストラクタでのみ生成すると良いでしょう。
    
   ```csharp
   public class FaceApiService
   {
       private static FaceClient FaceClient;

       public FaceApiService()
       {
           var subscriptionKey = "cognitive-services-subscription-key";
           var endPoint = "https://japaneast.api.cognitive.microsoft.com/";
           FaceClient = new FaceClient(new ApiKeyServiceClientCredentials(subscriptionKey), new System.Net.Http.DelegatingHandler[] { })
           {
               Endpoint = endPoint
           };
       }
   }
   ```
1. Face Detect APIの呼び出し方
   * 画像のURLから解析する場合、上述で生成したFaceClientの`DetectWithUrlAsync`メソッドを呼び出します。
   * 性別、年齢などの[属性情報](https://docs.microsoft.com/ja-jp/azure/cognitive-services/face/concepts/face-detection#attributes)を取得する場合は、`returnFaceAttributes`を設定します。
       * Emotionを取得する場合は、`returnFaceAttributes`に`FaceAttributeType.Emotion`を指定します。
   * 下記のコードは画像のURLをもとにFace Detect APIを呼び出し、結果を返すメソッドです。
   
   ```csharp
   public class FaceApiService
   {
       public async Task<IList<DetectedFace>> DetectAsync(string url)
       {
           // Emotionの取得
           var returnFaceAttributes = new List<FaceAttributeType?>
           {
               FaceAttributeType.Emotion
           };
           return await FaceClient.Face.DetectWithUrlAsync(url, returnFaceAttributes: returnFaceAttributes);
       }
   }
   ```
1. Face Group APIの呼び出し方
   * Face Group APIはDetect API同様、FaceClientの`GroupAsync`メソッドを呼び出します。
   * 下記のコードはFace IDの一覧からFace Group APIを呼び出し、グルーピング結果を返すAPIです。
   
   ```csharp
   public class FaceApiService
   {
       public async Task<GroupResult> GroupAsync(IList<Guid> faceIds)
       {
           return await FaceClient.Face.GroupAsync((IList<Guid?>)faceIds);
       }
   }
   ```

#### 動かしてみる
* アップロード画面

![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/962964/3593f0aa-79ab-00ab-8818-8c9d608b8393.png)


試しにこちらの4枚の画像をアップロードします。
解析が正しければ、3名の人物が検出されるはずです。

![image3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/962964/37e8236c-3c34-3693-3a7c-310af9c5f995.png)


* 一覧画面
    * 想定通り3名の人物が検出されました。
    * それぞれ同一人物が何件ヒットしたかもバッジで表示させています。
![image4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/962964/57d3491a-d43c-c0f6-28c9-2908a4b16109.png)


* 詳細画面
    * 上記一覧画面の最初の男性を選択すると、この男性が映っている画像が一覧表示されます。
    * emotionの結果も組み合わせてどんな表情をしているかも表示させてみました。
![image5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/962964/4b4a716b-73cf-a0bd-fabb-6bfcea377d9c.png)


#### ハマったポイント
* Face APIの .NET SDKは通常パッケージマネージャでは表示されない
    * Cognitive Services APIを呼び出せる .NET SDKは充実していますが、Face API用のSDKだけVisual Studioのパッケージマネージャから普通に探してもヒットしませんでした・・・。
    * 現在Face APIをインストールしたい場合、パッケージマネージャでは「プレリリースを含める」チェックをONにする必要があるようでした。
        * [公式のクイックスタート](https://docs.microsoft.com/ja-jp/azure/cognitive-services/face/quickstarts/client-libraries?tabs=visual-studio&pivots=programming-language-csharp#install-the-client-library)にも記載されています。

![image6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/962964/6fa1e6e0-7c06-68e7-68c2-9a27f8957b1a.png)

### まとめ
* Azure Cognitive ServiceのFace APIは、数回APIを実行するだけで簡単に同一人物を検出・分類させることができます。
* 今回はFace APIのうちDetect APIとGroup APIを組み合わせ、簡単な同一人物検出Webアプリを作成しました。
* 今後はタグ付け機能や学習機能なども使ってさらにリッチにしていきたいと思います。

### 関連リンク
* [フリー素材ぱくたそ](https://www.pakutaso.com)