Flutterから利用できるFirebaseサービスをカウンターアプリで実践(Firebase ML編)
はじめに
Flutterから利用できるFirebaseの機能をカウンターアプリに実装し、内容をまとめたシリーズの第11回目です。
今回はFirebaseを使った機械学習モデルの推論についてです。
目次
シリーズの内容
回数 | 内容 | リンク |
---|---|---|
第0回 | 準備編 | ブログ |
第1回 | Analytics |
ブログ |
第2回 | Firebase Crashlytics |
ブログ |
第3回 | Firebase Performance Monitoring |
ブログ |
第4回 | Firebase Remote Config |
ブログ |
第5回 | Firebase Authentication |
ブログ |
第6回 | Cloud Firestore |
ブログ |
第7回 | Firebase Realtime Database |
ブログ |
第8回 | Cloud Storage for Firebase |
ブログ |
第9回 | Firebase Cloud Messaging |
ブログ |
第10回 | Firebase In-App Messaging |
ブログ |
第11回 | Firebase ML |
イマココ |
第12回 | Cloud Functions for Firebase |
ブログ |
第13回 | Firebase Hosting |
ブログ |
第14回 | Firebaseのその他のサービス | ブログ |
開発環境
項目 | 内容 |
---|---|
PC | Macbook Air(M1) |
Flutter | 3.0.1 |
Firebase | 11.0.1 |
FlutterFire | 0.2.2+2 |
デバッグデバイス | Android 12(APIレベル31) , Chrome |
準備
準備編が完了出来ているものとします。
FirebaseとFlutterで使用できる機械学習の推論
FlutterとFirebaseを使った機械学習の推論には、オンデバイスで推論する方法と、クラウド上で推論する方法があります。
オンデバイスの推論でFirebaseを使うというのは、カスタムTensorFlowLiteモデルをFirebase MLで配信してローカルで推論することです。
実際の推論にはtflite_flutter
, ML Kit
などを使用します。
カスタマイズされたTFモデルをFirebaseで配信するメリットはユーザーがアプリをアップデートせずに最新のモデルを使用できるという点です。
クラウドの推論でFirebase(Google Cloud)を使うというのは、Cloud Vision AIやCloud Natural Languageなどで推論するということです。 現在のところ、Flutter向けにはAPIが提供されていないので、各OS向けのAPIを組み合わせる必要があります。
なお、上記以外にも、他サービスの機械学習モデルを使う方法があります。
FirebaseによるカスタムTensorflowLiteモデル配信の導入方法
自分で学習させたTFのカスタムモデルをTFLiteに変換したファイルを用意します。
今回は画像認識のタスクを実行するため、TensorFlow HubからImagenetの画像分類学習モデルを取得しました。 このとき、ライセンスとダウンロードするファイルの種類に気をつけてください。
ファイルを用意できたら、Firebase ConsoleのMachine Learning
からモデルをデプロイします。
公式では推論にtflite_flutterやtfliteが推奨されていましたが、私の開発環境ではtflite_flutter
等をインポートした状態でアプリをビルド出来なかったので、ML Kitで実験しました。
ML KitでTFLiteのカスタムモデルを使用できるタスクには、Image LabelingまたはObject Detection and Trackingがあります。
今回はImage Labeling
の推論をしますので、google_ml_kitとgoogle_mlkit_image_labelingも導入します。
dependencies: google_ml_kit: ^0.11.0 google_mlkit_image_labeling: ^0.3.0
Firebase MLからモデルのダウンロードは、google_mlkit_image_labeling
パッケージに入っているFirebaseImageLabelerModelManagerを使用します。
final bool response = await FirebaseImageLabelerModelManager().downloadModel(modelname); final options = FirebaseLabelerOption( confidenceThreshold: 0.5, modelName: modelname, maxCount: 3); _imageLabeler = ImageLabeler(options: options);
image_picker
等で写真のパスがわかれば、基本的なラベル推論に必要なコードは2行です。
final InputImage inputImage = InputImage.fromFilePath(path); final List labels = await _imageLabeler.processImage(inputImage);
推論された結果からラベルを取り出します。
String labelText = ''; for (final label in labels) { labelText += '\nLabel: ${label.label}'; }
アプリで確認してみます。 推論は出来ているみたいです。 ただ、思っていた結果とは違ったので、改善の余地がありそうです。
コード全文
前回からの変更点です。
Firebase ML
を実装ml_page
ページを追加main.dart
にml_page
ページに遷移するボタン追加- その他、コードの変更
ml_page.dart
/// Flutter import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:image_picker/image_picker.dart'; import 'package:google_mlkit_image_labeling/google_mlkit_image_labeling.dart'; /// Firebase import 'package:firebase_ml_model_downloader/firebase_ml_model_downloader.dart'; /// Providerの初期化 final imageStateProvider = StateProvider<File?>((ref) => null); final textStateProvider = StateProvider<String?>((ref) => null); class MLPage extends ConsumerStatefulWidget { const MLPage({Key? key}) : super(key: key); @override MLPageState createState() => MLPageState(); } class MLPageState extends ConsumerState<MLPage> { /// 画像ラベル分類問題の定義 late ImageLabeler _imageLabeler; ImagePicker? _imagePicker; /// ImageLabelerの初期化 @override void initState() { super.initState(); _initializeLabeler(); _imagePicker = ImagePicker(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('画像からラベル分類'), ), body: ListView( padding: const EdgeInsets.all(16), children: <Widget>[ /// 画像が存在すれば画像を表示 /// そうでなければ画像アイコンを表示 ref.watch(imageStateProvider) != null ? SizedBox( height: 400, width: 400, child: Stack( fit: StackFit.expand, children: <Widget>[ Image.file(ref.watch(imageStateProvider)!), ], ), ) : const Icon( Icons.image, size: 200, ), ElevatedButton( child: const Text('写真を選択'), onPressed: () => _getImage(ImageSource.gallery), ), Container( padding: const EdgeInsets.all(10), child: Text(ref.watch(imageStateProvider) == null ? '' : ref.watch(textStateProvider) ?? ''), ), ], ), ); } /// ラベラーの初期化 void _initializeLabeler() async { /// モデルの名前はFirebase MLにアップロードした名前 const modelname = 'Image'; /// FirebaseからML kitに学習モデルを読込ませるため、FirebaseImageLabelerModelManagerを使う /// 参考 : https://pub.dev/documentation/google_mlkit_image_labeling/latest/ final bool response = await FirebaseImageLabelerModelManager().downloadModel(modelname); print(response); /// ラベラーのオプションを設定し、読込 final options = FirebaseLabelerOption( confidenceThreshold: 0.5, modelName: modelname, maxCount: 3); _imageLabeler = ImageLabeler(options: options); } /// 画像を選択し推論を実行 Future _getImage(ImageSource source) async { /// 画像選択 final pickedFile = await _imagePicker?.pickImage(source: source); /// 有効な画像が選択できたら推論 if (pickedFile != null) { /// ファイルのパスを取得 final path = pickedFile.path; ref.read(imageStateProvider.state).state = File(path); final inputImage = InputImage.fromFilePath(path); /// ラベル分類推論実行 final labels = await _imageLabeler.processImage(inputImage); /// ラベルの分類が成功した場合、ラベルのテキストを生成 if (inputImage.inputImageData?.size != null && inputImage.inputImageData?.imageRotation != null) { } else { String labelText = ''; for (final label in labels) { labelText += '\nLabel: ${label.label}'; } ref.read(textStateProvider.state).state = labelText; } } } }
GitHubのページを貼ります。