LoginSignup
10
8

More than 1 year has passed since last update.

Flutter×Firebase(MachineLearning)で画像認識(OCR/オブジェクトのラベリング)をやってみる

Posted at

:book: Flutterの記事を整理し本にしました :book:

  • 本稿の記事を含む様々な記事を体系的に整理し本にまとめました
  • 今後はこちらを最新化するため、最新情報はこちらをご確認ください
  • 25万文字を超える超大作になっています!!

まとめ記事

はじめに

  • VisionAPIを使ったことがあったので、それと親しいFirebaseのMLをFlutterと組み合わせて動かしてみました。

概要

Firebase MachineLearning(以下ML)は機械学習の画像認識APIを用いて、判定やクラス分け、ラベリングなどを行う環境を提供します。
自前で学習やモデル作成を行えば、より幅広い機械学習の識別が実現できます。

有効化手順

MLの有効化は、Firebaseの左側のメニューのMachineLearningを選択し、始めるをクリックします。

pic1.png

機械学習には、以下の3種類があります。

  • APIs:GCPのVisionAPIのように、画像認識を基本にテキスト認識や、ラベリング、顔検知などを行えます
  • Custom:TensorFlowなどのtfliteの学習モデルを使って処理を行えます
  • AutoML:CloudAutoMLでGCP上での機械学習と連携することができます。 me

以後は、事前のモデル作成が不要なAPIsについて解説していきます

APIsには、いくつかの種類が用意されていますので、使いたい目的に合わせて使用するAPIを選択します。

pic2.png

処理には、デバイスで行うオンデバイスと、クラウドで行う2種類があります。

APIによって利用できる形態や機能に制約がありますので、ご注意ください。

クラウドベースを行う際には、プランをBlazeにアップグレードする必要があります。

なお、今回解説するテキスト認識の日本語は、クラウドでないと使えないため、プランをアップグレードしています。

pic3.png

準備

pubspec.yaml
  firebase_core: ^1.1.0
  firebase_ml_vision: ^0.12.0+1
  image_picker: ^0.7.4

MLを使うためのパッケージがfirebase_ml_visionfirebase_coreになります。
image_pickerは画像やカメラを選択するために使っています。

実装

デバイス内の画面を選択してテキスト認識(OCR)をするものと、カメラを使って取った写真のラベリングを行うものの2種類を実装しています。
どちらもクラウド側で処理を行っています。

main.dart
import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ml_vision/firebase_ml_vision.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Text _text;
  Image _image;

  Future<void> _ocr() async {
    PickedFile pickerFile =
        await ImagePicker().getImage(source: ImageSource.gallery);

    final File imageFile = File(pickerFile.path);
    final FirebaseVisionImage visionImage =
        FirebaseVisionImage.fromFile(imageFile);
    TextRecognizer textRecognizer =
        FirebaseVision.instance.cloudTextRecognizer();
    final VisionText visionText =
        await textRecognizer.processImage(visionImage);

    String text = visionText.text;
    for (TextBlock block in visionText.blocks) {
      print(block.text);
    }

    // 画面に反映
    setState(() {
      _text = Text(text);
      _image = Image.file(File(pickerFile.path));
    });

    // リソースの開放
    textRecognizer.close();
  }

  Future<void> _labeling() async {
    PickedFile pickerFile =
        await ImagePicker().getImage(source: ImageSource.camera);

    final File imageFile = File(pickerFile.path);
    final FirebaseVisionImage visionImage =
        FirebaseVisionImage.fromFile(imageFile);

    ImageLabeler labeler = FirebaseVision.instance.imageLabeler();
    List<ImageLabel> labels = await labeler.processImage(visionImage);
    String text = "";
    for (ImageLabel label in labels) {
      print(label.text);
      text += label.text + " ";
      print(label.confidence);
    }

    // 画面に反映
    setState(() {
      _text = Text(text);
      _image = Image.file(File(pickerFile.path));
    });

    // リソースの開放
    labeler.close();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              if (_text != null) _text,
              if (_image != null) SafeArea(child: _image),
            ],
          ),
        ),
        floatingActionButton:
            Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
          FloatingActionButton(onPressed: _ocr, child: Icon(Icons.photo_album)),
          FloatingActionButton(
              onPressed: _labeling, child: Icon(Icons.photo_camera))
        ]));
  }
}

まず、firebaseを利用するために以下の2行で初期化処理をしています。
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();

テキスト認識用とラベリング用のボタンがあり、それぞれが_ocr_labelingに対応しています。

基本的な流れは同じですが、使用するAPIによって使用するインスタンスが異なります。

テキスト認識(OCR)×写真ギャラリー

まず、ImagePickerを使って、ギャラリーからOCRで文字を読み取りたい画像を選択します。

その後、VisionImageTextRecognizerのインスタンスを作成し、TextRecognizerに画像を与えて解析を行います。
そして、結果からtextで文字や詳細情報を取得します。

最後に、画面に表示させたあとリソースを開放しています。

オブジェクトラベリング×カメラ

こちらも、まず、ImagePickerを使って、ギャラリーからOCRで文字を読み取りたい画像を選択します。

その後、VisionImageImageLabelerのインスタンスを作成し、ImageLabelerに画像を与えて解析を行います。
そして、結果(Labelのリスト)から認識されたラベルテキストを取得します。

最後に、画面に表示させたあとリソースを開放しています。

動作イメージ

ギャラリーのアイコンをタップした後、ストレージから本書の帯を選択したときの例

テキスト認識(OCR)×写真ギャラリー

pic4.png

result.sh
I/flutter (14560): Flutter実践入門
I/flutter (14560): 環境構築からAndroid/iOS/Webに
I/flutter (14560): 広告を付けて同時にリリース!!

コンソール及び画面に、写真から識別した文字が、1字の誤字もなく高い精度で変換されていることが分かります。

オブジェクトラベリング×カメラ

カメラのアイコンをタップした後、時計を撮影したときの例

pic5.png

result.sh
I/flutter (18593): Clock
I/flutter (18593): 0.8892788887023926

写真が約90%の確率で時計であると認識されています。
今回は、時計のみの識別ですが、写真のとり方によってはいくつかの選択肢が提示されたりします。
また、複数のオブジェクトが写っている場合は、それぞれに識別が行われます。

10
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
8