Android のクイックスタート

このページでは、TensorFlow Lite を使用して Android アプリを構築し、ライブ カメラ フィードを分析してオブジェクトを識別する方法を説明します。この機械学習のユースケースは、オブジェクト検出と呼ばれます。サンプル アプリでは、 Google Play サービス経由でビジョン用の TensorFlow Lite タスク ライブラリを使用して、物体検出機械学習モデルの実行を可能にします。これは、TensorFlow Lite で ML アプリケーションを構築する場合に推奨されるアプローチです。

物体検出のアニメーションデモ

サンプルをセットアップして実行する

この演習の最初の部分では、GitHub からサンプル コードをダウンロードし、 Android Studio を使用して実行します。このドキュメントの次のセクションでは、コード例の関連セクションを検討して、独自の Android アプリに適用できるようにします。これらのツールの次のバージョンをインストールする必要があります。

  • Android Studio 4.2以降
  • Android SDK バージョン 21 以降

サンプルコードを入手する

サンプル コードのローカル コピーを作成して、ビルドして実行できるようにします。

サンプルコードを複製してセットアップするには、次のようにします。

  1. git リポジトリ
    git clone https://github.com/tensorflow/examples.git
    
    のクローンを作成します
  2. スパース チェックアウトを使用するように Git インスタンスを構成し、オブジェクト検出サンプル アプリのファイルのみを用意します:
    cd examples
    git sparse-checkout init --cone
    git sparse-checkout set lite/examples/object_detection/android_play_services
    

プロジェクトをインポートして実行する

Android Studio を使用して、ダウンロードしたサンプル コードからプロジェクトを作成し、プロジェクトをビルドして実行します。

サンプル コード プロジェクトをインポートしてビルドするには:

  1. Android Studioを起動します。
  2. Android Studio の [ようこそ]ページで、 [プロジェクトのインポート]を選択するか、 [ファイル] > [新規作成] > [プロジェクトのインポート]を選択します。
  3. build.gradle ファイルを含むサンプル コード ディレクトリ ( ...examples/lite/examples/object_detection/android_play_services/build.gradle ) に移動し、そのディレクトリを選択します。

このディレクトリを選択すると、Android Studio は新しいプロジェクトを作成してビルドします。ビルドが完了すると、Android Studio の[ビルド出力ステータス] パネルにBUILD SUCCESSFULメッセージが表示されます。

プロジェクトを実行するには:

  1. Android Studio から、 「実行」>「実行…」および「MainActivity」を選択してプロジェクトを実行します。
  2. アプリをテストするには、カメラが接続されている Android デバイスを選択します。

サンプルアプリの仕組み

サンプル アプリは、TensorFlow Lite 形式の事前トレーニング済みオブジェクト検出モデル ( mobilenetv1.tfliteなど) を使用して、Android デバイスのカメラからのライブ ビデオ ストリーム内のオブジェクトを探します。この機能のコードは主に次のファイルにあります。

  • ObjectDetectorHelper.kt - ランタイム環境を初期化し、ハードウェア アクセラレーションを有効にし、オブジェクト検出 ML モデルを実行します。
  • CameraFragment.kt - カメラ画像データ ストリームを構築し、モデルのデータを準備し、オブジェクト検出結果を表示します。

次のセクションでは、Android アプリを変更してこの機能を追加できるように、これらのコード ファイルの主要なコンポーネントを示します。

アプリを構築する

次のセクションでは、独自の Android アプリを構築し、サンプル アプリに示されているモデルを実行するための主要な手順について説明します。これらの手順では、参照ポイントとして前に示したサンプル アプリを使用します。

プロジェクトの依存関係を追加する

基本的な Android アプリに、TensorFlow Lite 機械学習モデルを実行し、ML データ ユーティリティ関数にアクセスするためのプロジェクトの依存関係を追加します。これらのユーティリティ関数は、画像などのデータをモデルで処理できるテンソル データ形式に変換します。

サンプル アプリでは、 Google Play サービスビジョン用 TensorFlow Lite タスク ライブラリを使用して、物体検出機械学習モデルの実行を可能にします。次の手順では、必要なライブラリの依存関係を独自の Android アプリ プロジェクトに追加する方法について説明します。

モジュールの依存関係を追加するには:

  1. TensorFlow Lite を使用する Android アプリ モジュールで、モジュールのbuild.gradleファイルを更新して、次の依存関係を含めます。コード例では、このファイルは次の場所にあります: ...examples/lite/examples/object_detection/android_play_services/app/build.gradle

    ...
    dependencies {
    ...
        // Tensorflow Lite dependencies
        implementation 'org.tensorflow:tensorflow-lite-task-vision-play-services:0.4.2'
        implementation 'com.google.android.gms:play-services-tflite-gpu:16.1.0'
    ...
    }
    
  2. Android Studio で、 [ファイル] > [プロジェクトを Gradle ファイルと同期]を選択して、プロジェクトの依存関係を同期します。

Google Playサービスを初期化する

Google Play サービスを使用して TensorFlow Lite モデルを実行する場合、使用する前にサービスを初期化する必要があります。サービスで GPU アクセラレーションなどのハードウェア アクセラレーション サポートを使用する場合は、この初期化の一環としてそのサポートも有効にします。

Google Play サービスを使用して TensorFlow Lite を初期化するには:

  1. TfLiteInitializationOptionsオブジェクトを作成し、それを変更して GPU サポートを有効にします。

    val options = TfLiteInitializationOptions.builder()
        .setEnableGpuDelegateSupport(true)
        .build()
    
  2. TfLiteVision.initialize()メソッドを使用して Play サービス ランタイムの使用を有効にし、リスナーを設定して正常に読み込まれたことを確認します。

    TfLiteVision.initialize(context, options).addOnSuccessListener {
        objectDetectorListener.onInitialized()
    }.addOnFailureListener {
        // Called if the GPU Delegate is not supported on the device
        TfLiteVision.initialize(context).addOnSuccessListener {
            objectDetectorListener.onInitialized()
        }.addOnFailureListener{
            objectDetectorListener.onError("TfLiteVision failed to initialize: "
                    + it.message)
        }
    }
    

ML モデルインタープリターを初期化する

モデル ファイルをロードし、モデル パラメーターを設定して、TensorFlow Lite 機械学習モデル インタープリターを初期化します。 TensorFlow Lite モデルには、モデル コードを含む.tfliteファイルが含まれています。モデルは開発プロジェクトのsrc/main/assetsディレクトリに保存する必要があります。次に例を示します。

.../src/main/assets/mobilenetv1.tflite`

モデルを初期化するには:

  1. .tfliteモデル ファイルを開発プロジェクトのsrc/main/assetsディレクトリに追加します ( ssd_mobilenet_v1など)。
  2. modelName変数を設定して、ML モデルのファイル名を指定します。

    val modelName = "mobilenetv1.tflite"
    
  3. 予測しきい値や結果セットのサイズなどのモデルのオプションを設定します。

    val optionsBuilder =
        ObjectDetector.ObjectDetectorOptions.builder()
            .setScoreThreshold(threshold)
            .setMaxResults(maxResults)
    
  4. オプションを使用して GPU アクセラレーションを有効にし、アクセラレーションがデバイスでサポートされていない場合にコードが正常に失敗するようにします。

    try {
        optionsBuilder.useGpu()
    } catch(e: Exception) {
        objectDetectorListener.onError("GPU is not supported on this device")
    }
    
    
  5. このオブジェクトの設定を使用して、モデルを含む TensorFlow Lite ObjectDetectorオブジェクトを構築します。

    objectDetector =
        ObjectDetector.createFromFileAndOptions(
            context, modelName, optionsBuilder.build())
    

TensorFlow Lite でのハードウェア アクセラレーション デリゲートの使用の詳細については、 「 TensorFlow Lite デリゲート 」を参照してください。

モデルのデータを準備する

画像などの既存のデータをTensorデータ形式に変換することで、モデルによる解釈用のデータを準備します。これにより、モデルで処理できるようになります。 Tensor 内のデータは、モデルのトレーニングに使用されるデータの形式と一致する特定の次元または形状を持っている必要があります。使用するモデルによっては、モデルが期待するものに合わせてデータを変換する必要がある場合があります。サンプル アプリでは、 ImageAnalysisオブジェクトを使用してカメラ サブシステムから画像フレームを抽出します。

モデルによる処理のためにデータを準備するには:

  1. ImageAnalysisオブジェクトを構築して、必要な形式で画像を抽出します。

    imageAnalyzer =
        ImageAnalysis.Builder()
            .setTargetAspectRatio(AspectRatio.RATIO_4_3)
            .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)
            .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
            .setOutputImageFormat(OUTPUT_IMAGE_FORMAT_RGBA_8888)
            .build()
            ...
    
  2. アナライザーをカメラ サブシステムに接続し、カメラから受信したデータを含むビットマップ バッファーを作成します。

            .also {
            it.setAnalyzer(cameraExecutor) { image ->
                if (!::bitmapBuffer.isInitialized) {
                    bitmapBuffer = Bitmap.createBitmap(
                        image.width,
                        image.height,
                        Bitmap.Config.ARGB_8888
                    )
                }
                detectObjects(image)
            }
        }
    
  3. モデルに必要な特定の画像データを抽出し、画像の回転情報を渡します。

    private fun detectObjects(image: ImageProxy) {
        // Copy out RGB bits to the shared bitmap buffer
        image.use { bitmapBuffer.copyPixelsFromBuffer(image.planes[0].buffer) }
        val imageRotation = image.imageInfo.rotationDegrees
        objectDetectorHelper.detect(bitmapBuffer, imageRotation)
    }    
    
  4. サンプル アプリのObjectDetectorHelper.detect()メソッドに示すように、最終的なデータ変換を完了し、画像データをTensorImageオブジェクトに追加します。

    val imageProcessor = ImageProcessor.Builder().add(Rot90Op(-imageRotation / 90)).build()
    
    // Preprocess the image and convert it into a TensorImage for detection.
    val tensorImage = imageProcessor.process(TensorImage.fromBitmap(image))
    

予測を実行する

正しい形式の画像データを含むTensorImageオブジェクトを作成したら、そのデータに対してモデルを実行して、予測または推論を生成できます。サンプル アプリでは、このコードはObjectDetectorHelper.detect()メソッドに含まれています。

モデルを実行して画像データから予測を生成するには:

  • 画像データを予測関数に渡して予測を実行します。

    val results = objectDetector?.detect(tensorImage)
    

モデル出力を処理します

オブジェクト検出モデルに対して画像データを実行すると、追加のビジネス ロジックの実行、ユーザーへの結果の表示、またはその他のアクションの実行によってアプリ コードが処理する必要がある予測結果のリストが生成されます。サンプル アプリの物体検出モデルは、検出された物体の予測と境界ボックスのリストを生成します。サンプル アプリでは、予測結果はさらに処理されてユーザーに表示されるためにリスナー オブジェクトに渡されます。

モデルの予測結果を処理するには:

  1. リスナー パターンを使用して、結果をアプリ コードまたはユーザー インターフェイス オブジェクトに渡します。サンプル アプリは、このパターンを使用して、検出結果をObjectDetectorHelperオブジェクトからCameraFragmentオブジェクトに渡します。

    objectDetectorListener.onResults( // instance of CameraFragment
        results,
        inferenceTime,
        tensorImage.height,
        tensorImage.width)
    
  2. ユーザーに予測を表示するなど、結果に基づいて行動します。サンプル アプリは、 CameraPreviewオブジェクトにオーバーレイを描画して結果を表示します。

    override fun onResults(
      results: MutableList<Detection>?,
      inferenceTime: Long,
      imageHeight: Int,
      imageWidth: Int
    ) {
        activity?.runOnUiThread {
            fragmentCameraBinding.bottomSheetLayout.inferenceTimeVal.text =
                String.format("%d ms", inferenceTime)
    
            // Pass necessary information to OverlayView for drawing on the canvas
            fragmentCameraBinding.overlay.setResults(
                results ?: LinkedList<Detection>(),
                imageHeight,
                imageWidth
            )
    
            // Force a redraw
            fragmentCameraBinding.overlay.invalidate()
        }
    }
    

次のステップ