flutter:【实战篇】MLKIT 实现OCR文本识别,再也不用付费SDK了

引言

最近项目里要用到 OCR 拍照识别文本的能力。小编一开始想要的是接入百度的 OCR sdk,奈何领导直接说不批任何费用,看来只能另谋出路了。

于是,小编找到了这个库 google_mlkit_text_recognition,该库支持传入图片识别文本,最重要的还是免费

闲话不多说,先来一张实现的效果图:

image.png

拍照支持局部识别,下面来说说实现步骤。

实现方式

为了实现拍照识别,分别依赖了下面三个库:

dependencies:
  flutter:
    sdk: flutter
  camera: ^0.10.2+1
  google_mlkit_commons: ^0.5.0
  google_mlkit_text_recognition: ^0.9.0

通过 camera 库实现相机预览能力

CameraPreview(
  cameraController,
)

并且相机开启预览后 cameraController 可以拿到每一帧的图片数据

cameraController.startImageStream(_processCameraImage);

将每一帧的图片数据,调用文本识别的google库,返回该图像内所有识别到的文本内容(注意:这里要自行做节流处理,我们不需要每帧都进行图像分析的开销)

Future _processCameraImage(CameraImage image) async {
  final WriteBuffer allBytes = WriteBuffer();
  for (final Plane plane in image.planes) {
    allBytes.putUint8List(plane.bytes);
  }
  final bytes = allBytes.done().buffer.asUint8List();

  final Size imageSize = Size(
    image.width.toDouble(),
    image.height.toDouble(),
  );

  final camera = _cameras[0];
  final imageRotation = InputImageRotationValue.fromRawValue(
    camera.sensorOrientation,
  );
  if (imageRotation == null) return;

  final inputImageFormat = InputImageFormatValue.fromRawValue(
    image.format.raw,
  );
  if (inputImageFormat == null) return;

  final planeData = InputImageMetadata(
    size: imageSize,
    rotation: imageRotation,
    format: inputImageFormat,
    bytesPerRow: image.planes[0].bytesPerRow,
  );
  final inputImage = InputImage.fromBytes(
    bytes: bytes,
    metadata: planeData,
  );
  processImage(inputImage);
}
Future<void> processImage(InputImage inputImage) async {
  final recognizedText = await _textRecognizer.processImage(inputImage);
  // 这是识别到的整张图片的文本
  final scanText = recognizedText.text; 
}

但是我们要做局部识别又该如何处理呢?那么我们不能直接拿到 scanText 就直接使用,我们需要做筛选处理:

recognizedText 会返回识别到的文本内容,同时会返回其对应的坐标信息,使用这些信息于我们示例GIF中绘制的蓝色框框的坐标进行包含判断,只筛选出坐标处于蓝色框框坐标范围内的数据。

大致代码如下:

String scannedText = '';
for (final textBunk in recognizedText.blocks) {
  for (final element in textBunk.lines) {
    for (final textBlock in element.elements) {
      final left = translateX(
        (textBlock.boundingBox.left),
        rotation,
        size,
        absoluteImageSize,
      );
      final top = translateY(
        (textBlock.boundingBox.top),
        rotation,
        size,
        absoluteImageSize,
      );
      final right = translateX(
        (textBlock.boundingBox.right),
        rotation,
        size,
        absoluteImageSize,
      );

      // 判断是否蓝色框框坐标范围内
      if (left >= boxLeft &&
          right <= boxRight &&
          (top >= (boxTop + 15) && top <= (boxBottom - 20))) {
        scannedText += " ${textBlock.text}";
      }
    }
  }
}
log('蓝色框框内识别的文本:$scannedText')

根据符合筛选范围的数据,自己拼出结果内容。

如果不使用相机预览,直接从相册选中识别呢?

从上面的代码可以看到,文本识别的入参是一个 inputImage 实例。

final recognizedText = await _textRecognizer.processImage(inputImage);

inputImage 是 google_mlkit_common 中提供的类型,查看代码如下:

/// Creates an instance of [InputImage] from path of image stored in device.
factory InputImage.fromFilePath(String path) {
  return InputImage._(filePath: path, type: InputImageType.file);
}

/// Creates an instance of [InputImage] by passing a file.
factory InputImage.fromFile(File file) {
  return InputImage._(filePath: file.path, type: InputImageType.file);
}

/// Creates an instance of [InputImage] using bytes.
factory InputImage.fromBytes(
    {required Uint8List bytes, required InputImageMetadata metadata}) {
  return InputImage._(
      bytes: bytes, type: InputImageType.bytes, metadata: metadata);
}

其提供根据文件路径构造实例的方法。

推荐一下宝子,各种功能库扩展

题外话,Google ML Kit提供多种可免费使用的实用功能库。支持 Android、iOS,例如:

告别国内厂家的收费模式,开发应用变得更加简洁。

demo 已开源

本篇示例代码已上传到 github : https://github.com/liyufengrex/flutter_ocr_text_recognization

该封装工具库已开源发布:flutter_ocr_text_recognization

使用方式:

dependencies:
    flutter_ocr_text_recognization: x.x.x
import 'package:flutter_ocr_text_recognization/flutter_ocr_text_recognization.dart';
TextOrcScan(
      paintboxCustom: Paint()
          ..style = PaintingStyle.stroke
          ..strokeWidth = 4.0
          ..color = const Color.fromARGB(153, 102, 160, 241),
      boxRadius: 12,
      painBoxLeftOff: 5,
      painBoxBottomOff: 2.5,
      painBoxRightOff: 5,
      painBoxTopOff: 2.5,
      widgetHeight: MediaQuery.of(context).size.height / 3,
      getScannedText: (value) {
        setText(value);
      },
)

参数说明:

Parameter Description
painBoxLeftOff 蓝色框框左偏移量
painBoxBottomOff 蓝色框框下偏移量
painBoxRightOff 蓝色框框右偏移量
painBoxTopOff 蓝色框框上偏移量
getScannedText 返回识别出的文本

本库参考自 https://pub-web.flutter-io.cn/packages/flutter_scalable_ocr , 因项目需要,分析内部实现后,修复部分原库发现的问题,新建的该工具库。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容