はじめに
今回は、google_mlkit_text_recognitionを使ってレシートを読み取る方法を紹介します。
開発環境
- Flutter 3.7.11
- Dart 2.19.6
- camera 0.10.5+5
- google_mlkit_text_recognition 0.11.0
開発手順
1. ライブラリのインストール
文字認識をするパッケージを追加します。
カメラを使うために、こちらのパッケージを追加します。
ターミナルに以下のコマンドを使い、ライブラリを追加する。
flutter pub add google_mlkit_text_recognition camera
2. Androidの設定
まずは、Androidの設定をしていきます。
android/app/build.gradleを設定していきます。
android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.flutter_ocr_app"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
- minSdkVersion flutter.minSdkVersion
- targetSdkVersion flutter.minSdkVersion
+ minSdkVersion 22
+ targetSdkVersion 32
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
}
google_mlkit_text_recognitionでは、ラテン文字の認識のみをサポートします。そのため以下を追加して日本語を認識できるようにする必要があります。その他言語も同様
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation "com.google.mlkit:text-recognition-japanese:16.0.0"
}
Androidでカメラを使用するためにAndroidManifest.xmlに権限を追加します。(android/app/src/main/AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.flutter_ocr_app">
<!-- この下の1行追加 -->
+ <uses-permission android:name="android.permission.CAMERA" />
<application
android:label="flutter_ocr_app"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
~~~~~~~~~~~ 中略 ~~~~~~~~~~~
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
3. iOSの設定
iOSでカメラを使うために、info.plistにアクセス権限許可の追加します。(ios/Runner/Info.plist)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
~~~~~~~~~~~ 中略 ~~~~~~~~~~~
+ <key>NSCameraUsageDescription</key>
+ <string>your usage description here</string>
+ <key>NSMicrophoneUsageDescription</key>
+ <string>your usage description here</string>
</dict>
</plist>
google_mlkit_text_recognitionの他言語の認識のために以下をios/Podfile
に追加
pod 'GoogleMLKit/TextRecognitionJapanese', '~> 4.0.0'
4. 機能の実装
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_ocr_app/camera_page.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final cameras = await availableCameras();
final firstCamera = cameras.first;
runApp(MyApp(camera: firstCamera));
}
class MyApp extends StatelessWidget {
const MyApp({
super.key,
required this.camera,
});
final CameraDescription camera;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CameraPage(camera: camera),
);
}
}
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_ocr_app/result_page.dart';
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
class CameraPage extends StatefulWidget {
const CameraPage({
super.key,
required this.camera,
});
final CameraDescription camera;
@override
State<CameraPage> createState() => _CameraPageState();
}
class _CameraPageState extends State<CameraPage> {
late CameraController _controller;
late Future<void> _initializeControllerFuture;
final textRecognizer = TextRecognizer(script: TextRecognitionScript.japanese);
@override
void initState() {
super.initState();
// カメラの設定
_controller = CameraController(
widget.camera,
ResolutionPreset.max,
);
// コントローラーを初期化
_initializeControllerFuture = _controller.initialize();
}
@override
void dispose() {
_controller.dispose();
textRecognizer.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
// FutureBuilder で初期化を待ってからプレビューを表示(それまではインジケータを表示)
return Scaffold(
appBar: AppBar(
title: const Text('レシートを読み取って下さい'),
),
body: Padding(
padding: const EdgeInsets.all(48.0),
child: Center(
child: FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return CameraPreview(_controller);
} else {
return const Center(child: CircularProgressIndicator());
}
},
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
try {
// 写真を撮る
final image = await _controller.takePicture();
final file = File(image.path);
final inputImage = InputImage.fromFile(file);
final recognizedText =
await textRecognizer.processImage(inputImage);
await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ResultPage(text: recognizedText.text),
fullscreenDialog: true,
),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('失敗'),
),
);
}
},
child: const Icon(Icons.camera_alt),
),
);
}
}
レシート撮影後、画面遷移し抽出したTextを表示します。
import 'package:flutter/material.dart';
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
class ResultPage extends StatelessWidget {
final RecognizedText recognizedText;
const ResultPage({super.key, required this.recognizedText});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('結果'),
),
body: Container(
padding: const EdgeInsets.all(30.0),
child: Text(recognizedText.text),
),
);
}
}
実際に動かした時にスクショ
正しく抽出できていない箇所が少しありますが、基本的には認識できています。
レシートの情報としては、加工の過程を省いているので、これからの加工の仕方で、使えるデータになってくると思います。