こんにちは、この記事は、Flutter Advent Calendar 21日目の記事です。(2時間遅れてしまって、上書きされてしまいました。。。とほほ。)
Flutter Advent Calendar #2 23日目が空いていたので、埋めさせていただきました。
概要
ポイントとしては、Cloud AutoML Visionから吐き出したTensorFlow Liteの形式のモデルを
Flutterで読み込ませて推論を行います。そのため、オフラインでも画像認識が行うことができます。
ただ、現状、Flutter内で、推論を行おうとすると、アプリがクラッシュして落ちてしまいます...
(本記事の最後に原因の推測と対応策をまとめています。)
Cloud AutoML Visionで画像認識モデルをつくる
早速Flutterではなく、GCPをいじります。
AutoML APIの有効化
GCPのプロジェクト作成や請求先アカウントは作成済みの前提で進めます。
右のメニューから「Vision -> ダッシュボード」を選択すると、AutoML Visionのダッシュボードが開きます。
まず、ここでAutoML APIを有効化しましょう。
データセットのラベリング
AutoML APIを有効化するとデータセットの画面が開きます。
ここで、画像を含んだデータセットを登録することができます。
今回は、以下のペットのデータセットを使ってみましょう。
データセットをダウンロードすると以下のようなディレクトリ構成になっています。
一つのディレクトリにファイル名でラベリングしている形式です。
images
├── Abyssinian_1.jpg
├── Abyssinian_10.jpg
├── Abyssinian_100.jpg
...
AutoMLはファイル名ではなく、ディレクトリ名を元にラベリングを行うので、これだと一つのラベルしか付与されません。
そのため、ファイル名を元に以下のような形でディレクトリ毎にファイルを分けましょう。
自分は、人力でやりましたが、bash scriptなどを使うのがよいかと思います。
pets
├── Abyssinian
│ ├── Abyssinian_1.jpg
│ ├── Abyssinian_10.jpg
│ ├── Abyssinian_100.jpg
│ ...
├── American_bulldog
│ ├── american_bulldog_10.jpg
│ ├── american_bulldog_100.jpg
│ ├── american_bulldog_101.jpg
│ ...
├── Basset_Hound
│ ├── basset_hound_1.jpg
│ ├── basset_hound_10.jpg
│ ├── basset_hound_100.jpg
...
これをzipで固めてアップロードします。
データセットのアップロード
Vision -> データセット から新しいデータセットを選択します。
データセット名には適当な名前を、モデルの目的は「単一ラベル分類」を使いましょう。
「パソコンから画像をアップロード」で先程作成したzipファイルを選択。
画像はGoogle Cloud Storageに上がるので、バケットがない場合は、プロンプトに従ってバケットを作成しましょう。
既にGoogle Cloud Storageのバケットがある方はそれを選択します。
画像のインポートは少々時間がかかるので他のことをして待ちましょう。
インポートが終わると、メールでお知らせしてくれます。
インポートが終わるとステータスが変わります。
「警告: 画像のインポート」となったら取り込みが完了しています。
警告は一部の画像フォーマットが対応していないためで、特に気にしなくてよいです。
データセットをクリックして開くと、取り込まれた画像およびラベリングが確認できます。
トレーニング
データセットを取り込み終わると学習を開始できます。
トレーニングから学習を開始しましょう。
モデルの定義以下のように選択します。
- モデルの定義: Edge
- モデル最適化のオプション: Best Trade-Off(精度と速さのバランスをとったもの)
- Node Hour Budget(トレーニングの上限時間): デフォルトで推測時間をセットしてくれてるのでそれを利用
ちなみに、Node Hour Budgetは最初の15時間は無料とのことです。
トレーニング開始を押すと、学習を開始します。
これも時間がかかるのでしばらく待ちましょう。
(自分は、2,3時間ほどで学習が完了しました。)
モデルのトレーニングが完了するとモデルの評価も見ることができます。
正答率は100%-90%を出せていて優秀ですね。
TensorFlow Liteモデルのダウンロード
モデルができたので、TensorFlow Liteのモデルをダウンロードしましょう。
「テストと使用」をクリックすると様々な種類のモデルをダウンロードできます。
この中から「TF Lite」を選択しましょう。クリックするとGoogle Cloud Storageにエクスポートするダイアログが出るので、エクスポートしましょう。
エクスポートすると、GCSの中に、tfliteとラベルのtxtファイルがあるのでそれをダウンロードします。(jsonファイルは利用しません。)
Flutterで画像認識アプリをつくる
モデルを配置する
ダウンロードしてきたTensorFlow Liteをassets
フォルダに配置します。
assets
└── custom
├── dict.txt
└── model.tflite
pubspec.yml
にも以下の通り記載します。
# To add assets to your application, add an assets section, like this:
assets:
- assets/custom/model.tflite
- assets/custom/dict.txt
必要なプラグインを読み込む
FlutterでTensorFlow Liteのモデルを読み込めるプラグインがあるので、それを使ってみます。
https://github.com/shaqian/flutter_tflite
プロジェクトを作成して、pubspec.yml
に以下のとおり、プラグインを追加しましょう。
(image_pickerは、画像を選択できるピッカーのプラグインです。)
dev_dependencies:
flutter_test:
sdk: flutter
+ tflite: 1.0.4
+ image_picker: 0.6.2+3
画面をつくる
main.dart
で画面を作っていきます。
非常にシンプルなので、説明は省略します。
import 'dart:async';
import 'dart:io';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Image Picker Example',
theme: new ThemeData(
primarySwatch: Colors.red,
),
home: new MyHomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _HomePageState extends State<HomePage> {
File _image;
@override
void initState() {
super.initState();
}
Future getImage() async {
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
_image = image;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Image Picker Example'),
),
body: Center(
child: _image == null
? Text('No image selected.')
: Image.file(_image),
),
floatingActionButton: FloatingActionButton(
onPressed: getImage,
tooltip: 'Pick Image',
child: Icon(Icons.add_a_photo),
),
);
}
}
立ち上げ時にモデルを読み込む
_HomePageState
のinitState
時にモデルを読み込むように処理を追加します。
Tflite
のloadModel
メソッドを呼び出すと、モデルを読み込むことができます。
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
+import 'package:tflite/tflite.dart';
class MyHomePage extends StatefulWidget {
(省略...)
@override
void initState() {
super.initState();
+ loadModel();
}
+ Future loadModel() async {
+ String res = await Tflite.loadModel(
+ model: "assets/custom/model.tflite",
+ labels: "assets/custom/dict.txt",
+ );
+ print(res);
+ }
(省略...)
}
画像選択時に推論するようにする
画像選択したときにモデルから推論を行うようにします。
+ Future detectImage(File in_image) async {
+ var predictions = await Tflite.runModelOnImage(path: in_image.path);
+ print(predictions);
+ }
Future getImage() async {
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
+ detectImage(image);
setState(() {
_image = image;
});
}
これで、アプリは完成しました。
ただし前述の通り、現状だと画像を読み込ませて、runModelOnImage
時になにもログを吐かずにアプリが終了します。
動かない原因の推測
現状、flutterのtflite
プラグインがCloud AutoML Visionのモデルに対応していない可能性が高いです。
実際、モデルをtflite
のリポジトリ内のサンプルモデルに差し替えて動かすと、動きます。
実際、このIssueでも「Cloud AutoML VisionのモデルはQuantized Modelのため利用できない可能性が高い」という旨の言及があります。
対応策
以下のような対応策が考えられます。
-
tflite
プラグインのコードを読んで、修正・プルリクを出す - Cloud AutoML VisionのモデルをEdgeではなく、APIにして呼び出す
- Firebase AutoML Vision Edgeとfirebase_ml_visionを利用する
最初からやり直すなら、最後のFirebase AutoML Vision Edgeを使うのがいいかな、と思いました。
Firebaseといっても、ほぼほぼGoogle Cloud版と機能は同じですし、Firebaseで勝手にモデルの更新もやってくれるみたいですし、非常に便利そうです。
次回はこれで、成功させてみたいな、と思ってます。
参考資料
Real-Time Object Detection with Flutter, TensorFlow Lite and Yolo -Part 1
Cross-Platform On-Device ML Inference TensorFlow Lite ft. Flutter