Android で CloudVision API を使ってラベル検出する

### CloudVision

Google が提供するクラウド型の画像認識サービスです。

画像に写っている乗り物や動物などの事物のカテゴリを検出する
「ラベル検出」や、
類似の画像をインターネットで検索する「ウェブ検出」などの機能がある。

REST 型 API であり、Java、 Python、 c# など多くのプログラム言語に対応している。
クライアント環境も、Linux、Windows、 Mac の他、Android、iOS に対応している。

- [Cloud_Vision](https://cloud.google.com/vision/)
- [Cloud Vision API ドキュメント](https://cloud.google.com/vision/docs/)

### Cloud Vision の API Key を取得する

Google アカウントとクレジットカードが必要です。

クレジットカードを登録して、課金モードにしないと、
API Key が有効にならない。

利用料金が気になるところだが、
月1000件までは無料枠なのでそれ以下であれば課金されない。

一連の手続きは、下記を参考にした。
心構えができたら、
[Cloud Vision](https://cloud.google.com/vision/) から
「無料トライアル」に入って、 API Key を取得する。

- [Cloud Vision APIの使い方まとめ](https://syncer.jp/cloud-vision-api)

### Android で、CloudVision を使用する

公式サンプルコードが公開されている。

[Sample code for Google Cloud Vision](https://github.com/GoogleCloudPlatform/cloud-vision)

android、dotnet、ios、java などのディレクトリがある。
android ディレクトリを AndroidStudio にインポートする。

下記を参考にした。

- [Cloud Vision サンプル アプリケーション](https://cloud.google.com/vision/docs/samples)
- [Android CloudVisionのサンプルを動かす](https://barubora3.net/?p=119)

### CloudVision SDK の設定

```app/build.gradle
    compile 'com.google.apis:google-api-services-vision:v1-rev369-1.23.0' exclude module: 'httpclient'

    compile 'com.google.api-client:google-api-client-android:1.23.0' exclude module: 'httpclient'

    compile 'com.google.http-client:google-http-client-gson:1.23.0' exclude module: 'httpclient'
```

### uses-permission の設定

```AndroidManifest.xml
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.CAMERA"/>
```

### API Key の設定


```MainActivity.java
    private static final String CLOUD_VISION_API_KEY = "Your API Key";
```

### 端末内に画像データを用意する

公式サンプルコードに犬と猫の画像がある。
それを 端末にコピーする。

### サンプルアプリを実行する

実行すると、
FloatingActionButton　が表示される。
クリックすると、Camera Gallery か Camera かを選択する
alert dialog が表示される。

Gallery を選ぶと、フォトアプリが起動される。
画像を選ぶと、サンプルアプリに戻り、Cloud Vision に画像をアップロードする。
しばらくすると、画像認識の結果が戻る。


### フォトアプリから画像を取得する

フォトアプリを起動する。

```
        Intent intent = new Intent();
        intent.setType("image/*");
        intent.setAction(Intent.ACTION_GET_CONTENT);
            startActivityForResult(Intent.createChooser(intent, "Select a photo"),
                     1);
```

フォトアプリからは　画像情報は URI 形式で返送される。
それから Bitmap を取得する。

```
        Bitmap bitmap =
        MediaStore.Images.Media.getBitmap( getContentResolver(), uri );
```

 公式サンプルでは、Bitmap を取得すると、すぐに Cloud Vision に画像をアップロードするが。
意図せぬ画像を選ぶこともあるので、確認のダイアログを表示する。

### リクエストを作成する

リクエストは JSON形式です。
JSON形式のリクエストを作成するための Javaのクラスライブラリが用意されている。
関連するクラスは10個くらいある。
必要なパラメータを設定して組み立てていく。

- [docs : CloudVision API のリクエストとレスポンス](https://cloud.google.com/vision/docs/requests-and-responses)
- [reference : CloudVision API](https://cloud.google.com/vision/docs/reference/rest/)


### VisionRequestInitializer

リクエストの初期化クラス。

[documentation : VisionRequestInitializer](https://developers.google.com/resources/api-libraries/documentation/vision/v1/java/latest/com/google/api/services/vision/v1/VisionRequestInitializer.html)


```
// API_KEY から初期化クラスを生成する
   VisionRequestInitializer requestInitializer =
                new VisionRequestInitializer(CLOUD_VISION_API_KEY)　{
                    @Override
                    protected void initializeVisionRequest(VisionRequest<?> visionRequest) {
                         // 省略
                    }
                };
```


### Image

画像を JSON 形式で送信するためのクラス
Android に同名の Image クラスがあるが、それとは異なる。

[documentation : Image](https://developers.google.com/resources/api-libraries/documentation/vision/v1/java/latest/com/google/api/services/vision/v1/model/Image.html)


```
// Bitmap　を　byteArray　に変換する
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.JPEG, 90, byteArrayOutputStream);
            byte[] imageBytes = byteArrayOutputStream.toByteArray();

// Image を生成する
            Image base64EncodedImage = new Image();
            base64EncodedImage.encodeContent(imageBytes);
```


### Feature

リクエストに、検出する機能と関連するパラメータを指定するためのクラス

機能には下記のものがある。

|機能|文字列|説明|
|:------------------:|:------------------:|:------------------:|
|ラベル検出|LABEL_DETECTION|画像全体に対して画像コンテンツ分析を実行し、結果を返します
顔検出| FACE_DETECTION|画像内の顔を検出します
|文字検出|TEXT_DETECTION|画像内のテキストに対して光学式文字認識（OCR）を実行します


- [docs : Feature 機能](https://cloud.google.com/vision/docs/features)
- [reference : Feature](https://cloud.google.com/vision/docs/reference/rest/v1/Feature)


```
// Feature を生成する
        Feature labelDetection = new Feature();
// ラベル検出を指定する
        labelDetection.setType("LABEL_DETECTION");
        labelDetection.setMaxResults(10)
```


### AnnotateImageRequest

ユーザー提供の画像とユーザーが要求した機能からリクエストを生成するクラス

[reference : AnnotateImageRequest](https://cloud.google.com/vision/docs/reference/rest/v1/AnnotateImageRequest)

```
// AnnotateImageRequestを生成する
        AnnotateImageRequest annotateImageRequest = new AnnotateImageRequest();
// 画像を設定する
        annotateImageRequest.setImage(base64EncodedImage);
// 要求した機能を設定する
        annotateImageRequest.setFeatures(features);
```

### BatchAnnotateImagesRequest


複数の annotateImageRequest を１つのサービスコールにまとめるクラス。

[documentation : BatchAnnotateImagesRequest](https://developers.google.com/resources/api-libraries/documentation/vision/v1/java/latest/com/google/api/services/vision/v1/model/BatchAnnotateImagesRequest.html)


```
// BatchAnnotateImagesRequest を生成する
        BatchAnnotateImagesRequest
        batchAnnotateImagesRequest 
        = new BatchAnnotateImagesRequest();
         batchAnnotateImagesRequest.setRequests(annotateImageRequests);
```


### Vision

総合的なサービスを定義するクラス。

[documentation ; Vision](https://developers.google.com/resources/api-libraries/documentation/vision/v1/java/latest/com/google/api/services/vision/v1/Vision.html)


```
        // HttpTransport と JsonFactory　から Vision.Builder を生成する
        HttpTransport httpTransport = AndroidHttp.newCompatibleTransport();
        JsonFactory jsonFactory = GsonFactory.getDefaultInstance();

        Vision.Builder builder = new Vision.Builder(httpTransport, jsonFactory, null);

        // requestInitializer で初期化する
        builder.setVisionRequestInitializer(requestInitializer);

        // Vision を生成する
        Vision vision = builder.build();
```

### Vision.Images.Annotate

リクエストを実行するクラス

[documentation : Vision.Images.Annotate](https://developers.google.com/resources/api-libraries/documentation/vision/v1/java/latest/com/google/api/services/vision/v1/Vision.Images.Annotate.html)

```
// Vision.Images.Annotate を生成する
        Vision.Images.Annotate annotateRequest = vision.images().annotate(batchAnnotateImagesRequest);
```


### AsyncTask

Android の非同期実行するクラス
リクエストを実行して、レスポンスを受ける

[reference : AsyncTask](https://developer.android.com/reference/android/os/AsyncTask)

```
public  class LableDetectionTask extends AsyncTask<Object, Void, String> {
public LableDetectionTask(Activity activity, Vision.Images.Annotate annotate) {
            // 省略
        }
        protected String doInBackground(Object... params) {
                BatchAnnotateImagesResponse response = annotate.execute();
}
```

### レスポンスを処理する

Cloud Vision からのレスポンスは JSON 形式です
読みやすくなるようにラベルとスコアの文字列にする


### BatchAnnotateImagesResponse

JSON 形式のレスポンスを Java オブジェクトにマッピングしたクラス

[documentation : BatchAnnotateImagesResponse](https://developers.google.com/resources/api-libraries/documentation/vision/v1/java/latest/com/google/api/services/vision/v1/model/BatchAnnotateImagesResponse.html)


```
        BatchAnnotateImagesResponse response;

        List<AnnotateImageResponse> list = response.getResponses();
        AnnotateImageResponse annotateImageResponse = list.get(0);

        // ラベル検出の結果を取得する
        List<EntityAnnotation> labels = annotateImageResponse.getLabelAnnotations();

        StringBuilder message = new StringBuilder();
        for (EntityAnnotation label : labels) {
                // ラベルとスコアの文字列にする
                message.append(String.format(Locale.US, "%.3f: %s", label.getScore(), label.getDescription()));
        } 
```

### AnnotateImageResponse

レスポンスのクラス
getLabelAnnotations メソッドで、ラベル検出の結果を取得する

[documentation : AnnotateImageResponse](https://developers.google.com/resources/api-libraries/documentation/vision/v1/java/latest/com/google/api/services/vision/v1/model/AnnotateImageResponse.html)

### EntityAnnotation

レスポンスのアノテーーションのクラス
ラベル検出の時は、ラベルとスコアが取得できる


[EntityAnnotation](https://developers.google.com/resources/api-libraries/documentation/vision/v1/java/latest/com/google/api/services/vision/v1/model/EntityAnnotation.html)

### エラーの処理

API Key が正しくないときは 
GoogleJsonResponseException が発生する

[reference : GoogleJsonResponseException](https://developers.google.com/api-client-library/java/google-api-java-client/reference/1.19.1/com/google/api/client/googleapis/json/GoogleJsonResponseException)

### 実行例

スクリーンショット
![cloud_vision_1_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64075/520cd7ac-0c3c-5ed8-cace-657c29fff699.png)

ラベルとスコア

> Dog (犬): 0.995
> Mamma (l哺乳類): 0.989
> Vertebrate (脊椎動物): 0.985
> Canidae (イヌ科) : 0.981
> Cocker spaniel (コッカースパニエル): 0.901
> Dog breed 9犬の品種): 0.895
> Carnivore (肉食動物): 0.855
> Sporting Group (スポーツ犬): 0.833
> Grass (草): 0.822
> Companion dog (愛玩犬): 0.816

哺乳類の犬で犬種はコッカースパニエルと判定された。

サンプルコードをgithub に公開した。
https://github.com/ohwada/Android_Samples/tree/master/CloudVision1

### 関連記事

- [Android で CloudVision API を使ってウェブ検出する](https://qiita.com/ohwada/items/4f0c3934cbb115017143)
- [Android で CloudVision API を使って、リモート画像を指定して ラベル検出する](https://qiita.com/ohwada/items/70a3336fbc158661331c)
- [Android で CloudVision API を使って顔検出する](
https://qiita.com/ohwada/items/31b4b184b88e053d710c)