見出し画像

FlutterとTensorFlow Lite(TFLite)でリアルタイム物体検出をしてみる

はじめに

この記事は Flutter #1 Advent Calendar 2020 16日目の記事です。

2020年は色々と濃い1年でした。技術面から2020年を振り返ると、個人的には業務も趣味もほぼほぼFlutter漬けな1年でした。

色々なアプリを作ったのですが、まだ機械学習に関するアプリは作ったことがありませんでした。なので、アドカレを機に、次のような物体検出(Object Detection)アプリを作ってみました。

画像2

この記事では、Flutterによるリアルタイム物体検出アプリの作り方を解説します。

なお、機械学習フレームワークはTensorFlow Liteを使います。

使用ライブラリ

TensorFlow Lite(以下、TFLite)は、モバイル端末で推論を行うためのディープラーニングフレームワークです。

tflite_flutterはTFLiteのC++ APIをdart:ffiでバインドし、Flutterから利用可能にするライブラリです。

また、tflite_flutter_helperはTFLiteで画像を扱う上での前処理などを行うためのライブラリです。

ここではtflite_flutterとtflite_flutter_helperを使い、FlutterからTFLiteを利用します。

ライブラリのインストール

まず、pubspec.yamlに、このアプリで使用するライブラリ一覧を追記します。

dependencies:
 flutter:
   sdk: flutter
 hooks_riverpod: ">=0.12.0 <1.0.0"
 flutter_hooks: ">=0.14.0 <1.0.0"
 tflite_flutter: ">=0.5.0 <1.0.0"
 tflite_flutter_helper: ">=0.1.2 <1.0.0"
 camera: ">=0.5.8 <1.0.0"

これらを追記後、次のコマンドでインストールしておきます。

flutter pub get

パッケージをインストールしたら、次はTFLiteダウンロードします。TFLiteのダウンロード方法として、まず下記スクリプトをダウンロードします。Linux、Macはinstall.shを、Windowsはinstall.batをダウンロードします。

install.sh

insatll.bat​

ダウンロードしたスクリプトは、プロジェクトのルートに配置しておきます。

なお、このスクリプトではwgetを使用するため、Macを使っている場合は、次のコマンドでwgetをインストールしておきます。

brew install wget

あとは、次のようなコマンドで、TFLiteをインストールできます

# Linux、Mac
sh install.sh

# Windows
install.bat

もし、TFLiterでDelegateを使いたい場合は、sh install.sh -dやinstall.bat -dと、引数をつけてインストールします。Delegateとは、TFLiteのグラフの一部/全体をCPU以外のExcecutorへ委譲する機能です。Delegateを使用することで、推論のレイテンシ、電力効率を改善することができます。

# Delegateをサポートする場合
# Linux、Mac
sh install.sh -d

# Windows
install.bat -d

TFLiteのダウンロードを終えたら、次のリンクからモデルとラベルをダウンロードします。

coco_ssd_mobilenet_v1_1.0_quant_2018_06_29.zip

ダウンロードした coco_ssd_mobilenet_v1_1.0_quant_2018_06_29.zipを解凍し、中に含まれるdetect.tflitelabelmap.txt<プロジェクト>/assets下に配置しておきます。

また、pubspec.yamlへ次を追記し、assets内のファイルを参照できるようにしておきます。

flutter:
 assets:
   - assets/

ここまでで、必要なファイルのダウンロードは終えました。

あとは、使用しているcameraパッケージがAPI Level 21(Android 5.0)以上を要求するので、app/build.gradleを開き、minSDKを21に変更しておきます。

android {
	...
 defaultConfig {
		...
     minSdkVersion 21
	}
}

識別器の作成

早速コードの完成形を載せてしまいますが、次のような識別器を作ることで、画像ストリームから物体検出を行うことができます。

なお、識別器はtflite_flutter作者さんの次の記事を参考に作成しています。

識別器を作ったら、次はこの識別器を使って、画像ストリームから推論してみます。

画像ストリーミングに対して推論する

ここでは、cameraパッケージを使用して画像ストリームへアクセスします。

cameraパッケージでは次のようにして、画像ストリームに対して処理を行うことができます。

import 'package:camera/camera.dart';

final cameras = await availableCameras();
final cameraController = CameraController(
	cameras[0],
	ResolutionPreset.low,
	enableAudio: false,
);
await cameraController.initialize();
cameraController.startImageStream((CameraImage cameraImage){
	// ここに画像ストリームに対する処理を書く
});

cameraController.startImageStream()のコールバック関数へ推論処理を書くことで、画像ストリームから物体検出をすることができます。

ただ、Dartの実行モデルは、シングルスレッド・イベントループです。

推論処理など負荷が高い処理をMainIsolateで処理すると、イベントループが詰まり、他イベントが処理されないことから、画面が固まってしまいます。

そのため、推論処理は別のIsolateを起動して、そちらで並列処理します。

Flutterではcompute関数を使うことで、簡単に並列処理を扱うことができます。

// computeを使うことで、並列処理が可能
// 一番目の引数に、並列に処理するコールバック関数を書く
// 二番目の引数に、コールバック関数へ渡す値を書く
await compute(inference, isolateData);

// 推論処理
static Future<List<Recognition>> inference(
     IsolateData isolateData) async {
   var image = ImageUtils.convertYUV420ToImage(
     isolateData.cameraImage,
   );
   if (Platform.isAndroid) {
     image = image_lib.copyRotate(image, 90);
   }

   final classifier = Classifier(
     interpreter: Interpreter.fromAddress(
       isolateData.interpreterAddress,
     ),
     labels: isolateData.labels,
   );

   return classifier.predict(image);
 }
}

// inferenceへ渡す引数
// 引数はトップレベル関数である必要がある
// 引数にクラスのインスタンスや、staticメソッドを含むと
// Isolateへ渡すことができない
class IsolateData {
 IsolateData({
   this.cameraImage,
   this.interpreterAddress,
   this.labels,
 });
 final CameraImage cameraImage;
 final int interpreterAddress;
 final List<String> labels;
}

最後に、画像ストリームを処理する部分の、完成版コードを載せておきます。

カメラプレビューの表示

ここまではUIではなく、推論処理に関する部分を実装してきました。

最後に、次のようにUI表示部分を作り、これまでの処理と合わせることで、物体検出アプリが完成となります。

あとは、これらを実行すれば、冒頭のデモのような物体検出を行うことができます。

次のリポジトリへ、今回作ったアプリの全ソースコードを置いています。

終わりに

ということで、Flutterを使った物体検出アプリを作ってみました。作ろうと思ってから約1日で動くデモが作れるなど、Flutterは生産性が高いフレームワークだと思います。

何より書いていて楽しいので、また来年もFlutterで色々作っていきたいなと思います。

参考文献



この記事が気に入ったらサポートをしてみませんか?