📝
ImagePickerとOCRとSelectableText
マンガをスクショして単語や表現を抽出
Flutter大学の第十五期共同開発で
英単語帳を作る企画に参加している。
担当しているのは、スクショ画像から単語を拾う機能。
使うのはこの三つ
dependencies:
image_picker: ^1.0.7
google_mlkit_text_recognition: ^0.11.0
provider: ^6.1.1
mlkitにはいろんな機能があるらしいが、これは文字列に特化したタイプ。
いろんな言語に対応させたければ+αの設定が必要だけれど
今回は英語を英語として読むだけなので、シンプルに。
裏側?の設定
ここが一番難関だったPodfile、
documentや記事の情報、単独では動かず、継ぎ接ぎしているので怪しげ。
でも、動く。
# add this line:
$iOSVersion = '12.0'
post_install do |installer|
xcode_base_version = `xcodebuild -version | grep 'Xcode' | awk '{print $2}' | cut -d . -f 1`
# add these lines:
installer.pods_project.build_configurations.each do |config|
config.build_settings["EXCLUDED_ARCHS[sdk=*]"] = "armv7"
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = $iOSVersion
end
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
# add these lines:
target.build_configurations.each do |config|
if Gem::Version.new($iOSVersion) > Gem::Version.new(config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'])
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = $iOSVersion
end
if config.base_configuration_reference && Integer(xcode_base_version) >= 15
xcconfig_path = config.base_configuration_reference.real_path
xcconfig = File.read(xcconfig_path)
xcconfig_mod = xcconfig.gsub(/DT_TOOLCHAIN_DIR/, "TOOLCHAIN_DIR")
File.open(xcconfig_path, "w") { |file| file << xcconfig_mod }
end
end
end
end
こちらはRunnerのinfo.plist、ライブラリへのアクセス許可
<key>NSPhotoLibraryUsageDescription</key>
<string>選択した画像から文字列の抽出を行います</string>
Android側はappのbuild.gradleで
compileSdkVersionとminSdkVersionが21以上であることを確認
英語以外の言語を使うなら、+αの設定が必要。
View
imagepickerで画像を取得してOCRにかける。
もちろん画像にもよるのだけれど、
英文がいくつかのブロックとして取得されるので、それをListで表示。
ListTileの中をSelectableTextにすることで、
任意の単語や表現だけを選択的にコピーできる。
それを単語帳の主機能に貼り付けよう、というわけ。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'ocr_model.dart';
class OcrPage extends StatelessWidget {
const OcrPage({super.key});
Widget build(BuildContext context) {
return ChangeNotifierProvider<OcrModel>(
create: (context) => OcrModel(),
child: Consumer<OcrModel>(
builder: (_, model, child) {
return Scaffold(
appBar: AppBar(
title: const Text('OCR Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () => model.getImage(),
child: const Text('ライブラリから選択する'),
),
model.image == null
? const Text('No image selected.')
: Image.file(model.image!), // 選択した画像を表示
ElevatedButton(
onPressed: () => model.processImage(),
child: const Text('OCRで読み込む'),
),
Expanded(
child: ListView.builder(
itemCount: model.texts.length,
itemBuilder: (context, index) {
return ListTile(
title: SelectableText(model.texts[index]),
);
},
),
),
],
),
),
);
}
),
);
}
}
Model
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
class OcrModel with ChangeNotifier {
File? _image;
List<String> _texts = [];
final picker = ImagePicker();
File? get image => _image;
List<String> get texts => _texts;
Future<void> getImage() async {
final pickedFile = await picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
_image = File(pickedFile.path);
notifyListeners();
} else {
print('画像が選択できませんでした');
}
}
Future<void> processImage() async {
if (_image != null) {
final inputImage = InputImage.fromFile(_image!);
final textRecognizer = TextRecognizer(script: TextRecognitionScript.latin);
final RecognizedText recognizedText = await textRecognizer.processImage(inputImage);
_texts = recognizedText.blocks.map((block) => block.text).toList();
notifyListeners();
}
}
}
値をコピペで受け渡すって、楽すぎないか!?
Discussion