flutter:【實(shí)戰(zhàn)篇】MLKIT 實(shí)現(xiàn)OCR文本識(shí)別,再也不用付費(fèi)SDK了

引言

最近項(xiàng)目里要用到 OCR 拍照識(shí)別文本的能力。小編一開始想要的是接入百度的 OCR sdk,奈何領(lǐng)導(dǎo)直接說不批任何費(fèi)用,看來只能另謀出路了。

于是,小編找到了這個(gè)庫 google_mlkit_text_recognition,該庫支持傳入圖片識(shí)別文本,最重要的還是免費(fèi)

閑話不多說,先來一張實(shí)現(xiàn)的效果圖:

image.png

拍照支持局部識(shí)別,下面來說說實(shí)現(xiàn)步驟。

實(shí)現(xiàn)方式

為了實(shí)現(xiàn)拍照識(shí)別,分別依賴了下面三個(gè)庫:

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

通過 camera 庫實(shí)現(xiàn)相機(jī)預(yù)覽能力

CameraPreview(
  cameraController,
)

并且相機(jī)開啟預(yù)覽后 cameraController 可以拿到每一幀的圖片數(shù)據(jù)

cameraController.startImageStream(_processCameraImage);

將每一幀的圖片數(shù)據(jù),調(diào)用文本識(shí)別的google庫,返回該圖像內(nèi)所有識(shí)別到的文本內(nèi)容(注意:這里要自行做節(jié)流處理,我們不需要每幀都進(jìn)行圖像分析的開銷)

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);
  // 這是識(shí)別到的整張圖片的文本
  final scanText = recognizedText.text; 
}

但是我們要做局部識(shí)別又該如何處理呢?那么我們不能直接拿到 scanText 就直接使用,我們需要做篩選處理:

recognizedText 會(huì)返回識(shí)別到的文本內(nèi)容,同時(shí)會(huì)返回其對應(yīng)的坐標(biāo)信息,使用這些信息于我們示例GIF中繪制的藍(lán)色框框的坐標(biāo)進(jìn)行包含判斷,只篩選出坐標(biāo)處于藍(lán)色框框坐標(biāo)范圍內(nèi)的數(shù)據(jù)。

大致代碼如下:

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,
      );

      // 判斷是否藍(lán)色框框坐標(biāo)范圍內(nèi)
      if (left >= boxLeft &&
          right <= boxRight &&
          (top >= (boxTop + 15) && top <= (boxBottom - 20))) {
        scannedText += " ${textBlock.text}";
      }
    }
  }
}
log('藍(lán)色框框內(nèi)識(shí)別的文本:$scannedText')

根據(jù)符合篩選范圍的數(shù)據(jù),自己拼出結(jié)果內(nèi)容。

如果不使用相機(jī)預(yù)覽,直接從相冊選中識(shí)別呢?

從上面的代碼可以看到,文本識(shí)別的入?yún)⑹且粋€(gè) inputImage 實(shí)例。

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);
}

其提供根據(jù)文件路徑構(gòu)造實(shí)例的方法。

推薦一下寶子,各種功能庫擴(kuò)展

題外話,Google ML Kit提供多種可免費(fèi)使用的實(shí)用功能庫。支持 Android、iOS,例如:

告別國內(nèi)廠家的收費(fèi)模式,開發(fā)應(yīng)用變得更加簡潔。

demo 已開源

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

該封裝工具庫已開源發(fā)布: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);
      },
)

參數(shù)說明:

Parameter Description
painBoxLeftOff 藍(lán)色框框左偏移量
painBoxBottomOff 藍(lán)色框框下偏移量
painBoxRightOff 藍(lán)色框框右偏移量
painBoxTopOff 藍(lán)色框框上偏移量
getScannedText 返回識(shí)別出的文本

本庫參考自 https://pub-web.flutter-io.cn/packages/flutter_scalable_ocr , 因項(xiàng)目需要,分析內(nèi)部實(shí)現(xiàn)后,修復(fù)部分原庫發(fā)現(xiàn)的問題,新建的該工具庫。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容