[Vision API] Javaで画像内の人の顔を検出する

Googleが提供しているVision APIをJavaで使ってみました。

APIを使用し、画像内に含まれている人の顔を検出します。

画像内の人の顔の位置のみだけではなく、
・顔が向いている向き
・顔のパーツ位置（[目、鼻など](#landmarktype)）
・表情（[怒っているか、悲しんでいるかなど](#faceannotation)）
なども情報として取得できます。

:::note info
APIを利用する環境の準備から始める場合や、コードを実行する際は、
[⧉[Vision API] Javaで画像内のいろいろなものを検出する](https://qiita.com/doran/items/444cb6231b7dffb9ddac)
を参照ください。
:::

|No||目次|
|---|---|---|
|1||[Feature.Typeの指定](#1-featuretypeの指定)|
|2||[実行結果の取得](#2-実行結果の取得)|
||1|[レスポンスの出力](#21-レスポンスの出力)|
||2|[レスポンスの内容](#22-レスポンスの内容)|
|3||[結果の検証](#3-結果の検証)|
||1|[人物が少ない場合](#31-人物が少ない場合)|
||2|[人物が多い場合](#32-人物が多い場合)|

# 1. Feature.Typeの指定
人の顔を検出する場合は、
**Feature.Type.FACE_DETECTION**
を指定します。

以下の記事でexecApi()にタイプを指定します。
[⧉[Vision API] Javaで画像内のいろいろなものを検出する](https://qiita.com/doran/items/444cb6231b7dffb9ddac#25-apiを呼び出す)

# 2. 実行結果の取得
### 2.1. レスポンスの出力
取得した結果をコンソールに出力します。

```
private static void output(BatchAnnotateImagesResponse response){
    List<AnnotateImageResponse> responses = response.getResponsesList();

    for (AnnotateImageResponse res : responses) {
        if (res.hasError()) {
            System.out.format("Error: %s%n", res.getError().getMessage());
            return;
        }
    
        for (FaceAnnotation annotation : res.getFaceAnnotationsList()) {
            System.out.format(
                "anger: %s%njoy: %s%nsurprise: %s%nposition: %s",
                annotation.getAngerLikelihood(),
                annotation.getJoyLikelihood(),
                annotation.getSurpriseLikelihood(),
                annotation.getBoundingPoly());
        }
    }
}
```
### 2.2. レスポンスの内容
BatchAnnotateImagesResponseの
getResponsesListでAnnotateImageResponseリストが取得できます。
検出されなった場合は、空のリストが返ってきます。

#### AnnotateImageResponse
|メソッド|戻り値|内容|
|---|---|---|
|getFaceAnnotationsList|List\<[FaceAnnotation](#faceannotation)\>|検出された情報|
#### FaceAnnotation
|メソッド|戻り値|内容|
|---|---|---|
|getBoundingPoly|[BoundingPoly](#boundingpoly)|顔の周囲の境界ポリゴン座標|
|getFdBoundingPoly|[BoundingPoly](#boundingpoly)|getBoundingPolyよりも厳密で、顔の皮膚部分を含む境界ポリゴン座標|
|getLandmarksList|List\<[Landmark](#landmark)\>|検出された顔のランドマーク(目、鼻とか)|
|getRollAngle|float|ロール角。顔に垂直な軸を中心とした画像垂直に対する顔の時計回り/反時計回りの回転量|
|getPanAngle|float|ヨー角。画像に垂直な垂直面に対して顔が向いている左向き/右向きの角度。範囲は-180〜180|
|getTiltAngle|float|ピッチ角。画像の水平面に対して顔が向いている上向き/下向きの角度。範囲は-180〜180|
|getDetectionConfidence|float|検出の信頼性。範囲は0〜1|
|getLandmarkingConfidence|float|ランドマーク検出の信頼性。範囲は0〜1|
|getJoyLikelihood|[Likelihood(enum)](#likelihood)|喜びの可能性|
|getSorrowLikelihood|[Likelihood(enum)](#likelihood)|悲しみの可能性|
|getAngerLikelihood|[Likelihood(enum)](#likelihood)|怒りの可能性|
|getSurpriseLikelihood()|[Likelihood(enum)](#likelihood)|驚きの可能性|
|getUnderExposedLikelihood()|[Likelihood(enum)](#likelihood)|画像の露出不足の可能性|
|getBlurredLikelihood|[Likelihood(enum)](#likelihood)|ぼんやりしている可能性|
|getHeadwearLikelihood|[Likelihood(enum)](#likelihood)|帽子を被っている可能性|
#### BoundingPoly
|メソッド|戻り値|内容|
|---|---|---|
|getVerticesList|List\<[Vertex](#vertex)\>|境界ポリゴンの頂点。<br>左上、右上、左下、右下の順|
#### Vertex
|メソッド|戻り値|内容|
|---|---|---|
|getX|int|X座標|
|getY|int|Y座標|
#### Landmark
|メソッド|戻り値|内容|
|---|---|---|
|getType|[Landmark.Type(enum)](#landmarktype)|顔のランドマークタイプ|
|getPosition|[Position](#position)|ランドマークの3D座標|
#### Position
|メソッド|戻り値|内容|
|---|---|---|
|getX|float|X座標|
|getY|float|Y座標|
|getZ|float|Z座標|
#### Likelihood
可能性を定義値(文字列)で返します。一覧の下の方がより可能性が高くなります。
|定義値|内容|
|---|---|
|UNKNOWN|可能性は不明|
|VERY_UNLIKELY|可能性は非常に低い|
|UNLIKELY|可能性は低い|
|POSSIBLE|可能はある|
|LIKELY|可能性は高い|
|VERY_LIKELY|可能性は非常に高い|

#### Landmark.Type
|定義値|内容|
|---|---|
|UNKNOWN_LANDMARK|不明|
|LEFT_EYE|左目|
|RIGHT_EYE|右目|
|LEFT_OF_LEFT_EYEBROW|左眉毛の左側|
|RIGHT_OF_LEFT_EYEBROW|左眉毛の右|
|LEFT_OF_RIGHT_EYEBROW|右眉毛の左側|
|RIGHT_OF_RIGHT_EYEBROW|右眉毛の右側|
|MIDPOINT_BETWEEN_EYES|目の間の中間点|
|NOSE_TIP|鼻先|
|UPPER_LIP|上唇|
|LOWER_LIP|下唇|
|MOUTH_LEFT|口は左|
|MOUTH_RIGHT|口は右|
|MOUTH_CENTER|口の中心|
|NOSE_BOTTOM_RIGHT|鼻、右下|
|NOSE_BOTTOM_LEFT|鼻、左下|
|NOSE_BOTTOM_CENTER|鼻、中央下|
|LEFT_EYE_TOP_BOUNDARY|左目、上部の境界|
|LEFT_EYE_RIGHT_CORNER|左目、右隅|
|LEFT_EYE_BOTTOM_BOUNDARY|左目、下の境界|
|LEFT_EYE_LEFT_CORNER|左目、左隅|
|RIGHT_EYE_TOP_BOUNDARY|右目、上部の境界|
|RIGHT_EYE_RIGHT_CORNER|右目、右隅|
|RIGHT_EYE_BOTTOM_BOUNDARY|右目、下の境界線|
|RIGHT_EYE_LEFT_CORNER|右目、左隅|
|LEFT_EYEBROW_UPPER_MIDPOINT|左眉毛、上中点|
|RIGHT_EYEBROW_UPPER_MIDPOINT|右眉毛、上中点|
|LEFT_EAR_TRAGION|左耳の損傷|
|RIGHT_EAR_TRAGION|右耳の損傷|
|LEFT_EYE_PUPIL|左目の瞳孔|
|RIGHT_EYE_PUPIL|右目の瞳孔|
|FOREHEAD_GLABELLA|額の眉間|
|CHIN_GNATHION|顎関節|
|CHIN_LEFT_GONION|チン左ゴニオン|
|CHIN_RIGHT_GONION|あご右ゴニオン|
|LEFT_CHEEK_CENTER|左頬中央|
|RIGHT_CHEEK_CENTER|右頬中央|

## 3. 結果の検証
画像に含まれる人物が少ない場合と多い場合についてそれぞれ検証してみました。
結果画像の加工は取得した座標を使ってjava.awt.Graphics2Dで加工しています。

### 3.1. 人物が少ない場合
**検出させる元画像**
元の画像です。
<img width="480" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114621/54654079-0cca-60af-b44f-b6dc6a357d04.jpeg">

**検出された結果**
顔の位置に青枠を追加しました。
表情は、喜びの可能性が「非常に高い」とでました。
<img width="480" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114621/83743fb0-bbff-93d4-4c41-6c6d0bec6e81.jpeg">

### 3.2. 人物が多い場合
**検出させる元画像**
<img width="480" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114621/fec71310-f11e-002e-607a-793d90d7a2c9.jpeg">

**検出された結果**
顔の位置に青枠を追加しました。
表情は全員、喜びの可能性が「非常に高い」とでました。
<img width="480" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114621/7938b088-6547-47f8-24d5-741c4589302a.jpeg">

<BR>
<BR>
おしまい。。。