TensorFlowLiteで1枚の画像から物体検出するたぶん最小構成 for Android

#はじめに
[前に](https://qiita.com/Anadreline/items/606a36ce64f35c954d0e)TensorFlow Mobileでの物体検出はやったんですが、今やるならTensorFlowLiteでしょうということでやってみた

#検証環境

- Android Studio 3.2.1
- CompileSdkVersion:28
- MinSdkVersion:18
- TargetSdkVersion:28
- TensorFlowLite

#プロジェクト作成
まずはプロジェクトの作成お好きなプロジェクト名で
![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36793/d667ffa7-ed89-2e0c-addc-0f202397daa4.png)
minumum SDKはAPI 18未満だとエラーが出るんで、18以上を選択
![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36793/93d7adb3-e678-6f83-7e54-b3416ede2f28.png)
初期Activityは何でもいいですが、ここでは最小構成ということでEmpty Activity
![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36793/d088bf9e-d910-a7fc-2c04-fc82681ee05e.png)

#build.gradleの設定

```build.gradle(app)
apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.anadreline.android.peopleaicounter"
        minSdkVersion 18
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    aaptOptions {
        noCompress "tflite"
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly'
    implementation 'org.tensorflow:tensorflow-lite-gpu:0.0.0-nightly'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
```

ポイントは2つだけ
**TensorFlowLiteの依存設定**と
　implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly'
　implementation 'org.tensorflow:tensorflow-lite-gpu:0.0.0-nightly'
**tfliteファイルを圧縮させないためのnoCompress設定**
　aaptOptions {
　　noCompress "tflite"
　}

#検出関連クラス
![004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36793/ac309f8e-871a-b2ca-670e-4595e1aada9c.png)
[**公式のサンプル**](https://github.com/tensorflow/examples/tree/master/lite/examples/object_detection/android)からClassifier.javaとTensorFlowObjectDetectionAPIModel.javaとLogger.javaの3つ
それにassetsフォルダに[**公式サイト**](https://www.tensorflow.org/lite/models/object_detection/overview)のモデルファイル(detect.tflite)とラベルファイル(label_map.txt)を入れます

#レイアウト
あとは画像選択させるためMainActivityにボタンを設置してー

```activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Detect"
        android:onClick="onClick"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>
```

#物体検出
最後に画像を取得して物体検出する処理を作成して完成

```MainActivity.java
package com.anadreline.android.objectdetection;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import java.io.IOException;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private static final int TF_OD_API_INPUT_SIZE = 300;
    private static final boolean TF_OD_API_IS_QUANTIZED = true;
    private static final String TF_OD_API_MODEL_FILE = "detect.tflite";
    private static final String TF_OD_API_LABELS_FILE = "file:///android_asset/labelmap.txt";

    private final int REQUEST_FILE = 1;
    private final int REQUEST_CAMERA = 2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void onClick(View view) {
        Intent intentGallery;
        if (Build.VERSION.SDK_INT < 19) {
            intentGallery = new Intent(Intent.ACTION_GET_CONTENT);
            intentGallery.setType("image/*");
        } else {
            intentGallery = new Intent(Intent.ACTION_OPEN_DOCUMENT);
            intentGallery.addCategory(Intent.CATEGORY_OPENABLE);
            intentGallery.setType("image/*");
        }
        startActivityForResult(intentGallery, REQUEST_FILE);
    }

    @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == RESULT_OK) {
            Uri resultUri = data.getData();
            if (resultUri == null) { return;}
            Bitmap image = Utl.decodeUri(this, resultUri, 1200, 0);
            int height = image.getHeight();
            int width = image.getWidth();
            Bitmap crop = Bitmap.createBitmap(TF_OD_API_INPUT_SIZE, TF_OD_API_INPUT_SIZE, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(crop);
            canvas.drawBitmap(image, new Rect(0, 0, width, height), new Rect(0, 0, TF_OD_API_INPUT_SIZE, TF_OD_API_INPUT_SIZE), new Paint());
            try {
                Classifier detector = TFLiteObjectDetectionAPIModel.create(
                                getAssets(),
                                TF_OD_API_MODEL_FILE,
                                TF_OD_API_LABELS_FILE,
                                TF_OD_API_INPUT_SIZE,
                                TF_OD_API_IS_QUANTIZED);
                final List<Classifier.Recognition> results = detector.recognizeImage(crop);
                for (final Classifier.Recognition result : results) {
                    Log.v("Detect", "Title:" + result.getTitle());
                    Log.v("Detect", "Confidence:" + result.getConfidence());
                    Log.v("Detect", "Location:" + result.getLocation());
                }
            } catch (final IOException e) {
                e.printStackTrace();
                Toast toast = Toast.makeText(getApplicationContext(), "Classifier could not be initialized", Toast.LENGTH_SHORT);
                toast.show();
                finish();
            }
        }
    }
}
```

ここでのポイントは検出クラスに投げる画像は決められたサイズにする必要があること
上記のこのへんで変形してます

```MainActivity.java
    //TF_OD_API_INPUT_SIZE=300;
    Bitmap crop = Bitmap.createBitmap(TF_OD_API_INPUT_SIZE, TF_OD_API_INPUT_SIZE, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(crop);
    canvas.drawBitmap(image, new Rect(0, 0, width, height), new Rect(0, 0, TF_OD_API_INPUT_SIZE, TF_OD_API_INPUT_SIZE), new Paint());
```

以上TensorFlowLiteによる物体検出のたぶん最小構成