如何用TensorFlow Lite进行光学字符识别(附实例)

610 阅读4分钟

古语有云:"一图胜千言"。图片含有丰富的视觉信息,但有时关键在于其中的文字。虽然识字的人类很容易阅读嵌入在图像中的文字,但我们如何使用计算机视觉和机器学习来教计算机这样做呢?

今天,我们将向你展示如何使用TensorFlow Lite从Android设备上的图像中提取文本。我们将带领你完成光学字符识别(OCR)安卓应用的关键步骤,我们最近在这里开源了这个应用,你可以参考它的完整代码。你可以在下面的动画中看到该应用如何从三个谷歌产品标识中提取产品名称。

从图像中识别文字的过程被称为光学字符识别,在许多领域被广泛使用。例如,谷歌地图使用OCR技术从地理定位的图像中自动提取信息,以改进谷歌地图。

一般来说,OCR是一个有多个步骤的管道。通常它们由文本检测和文本识别组成。

  • 使用一个文本检测模型来找出文本周围的边界框。
  • 做一些后处理来转换边界框。
  • 将这些边界框内的图像转化为灰度,以便文本识别模型能够映射出文字和数字。

在我们的案例中,我们将利用TensorFlow Hub的文本检测文本识别模型。有几个不同的模型版本用于速度/准确性的权衡;我们在这里使用float16量化的模型。关于模型量化的更多信息,请参考TensorFlow Lite量化部分。我们还使用OpenCV,这是一个广泛使用的计算机视觉库,用于非最大抑制(NMS)和透视变换(我们将在后面展开)来对检测结果进行后处理。此外,我们使用TFLite支持库对图像进行灰度化和标准化处理。

OCR pipeline from text detection, perspective transformation, to recognition
OCR管线从文本检测、透视变换到识别。

对于文本检测,由于检测模型接受320x320的固定尺寸,我们使用TFLite支持库来调整输入图像的大小和标准化。

val imageProcessor =
 ImageProcessor.Builder()
   .add(ResizeOp(height, width, ResizeOp.ResizeMethod.BILINEAR))
   .add(NormalizeOp(means, stds))
   .build()
var tensorImage = TensorImage(DataType.FLOAT32)

tensorImage.load(bitmapIn)
tensorImage = imageProcessor.process(tensorImage)

然后我们使用TFLite来运行检测模型。

detectionInterpreter.runForMultipleInputsOutputs(detectionInputs, detectionOutputs)

检测模型的输出是一些旋转的边界框,其中包含图像中的文字。我们用OpenCV运行非最大抑制法,为每个文本块识别一个边界框。

NMSBoxesRotated(
  boundingBoxesMat,
  detectedConfidencesMat,
  detectionConfidenceThreshold.toFloat(),
  detectionNMSThreshold.toFloat(),
  indicesMat
)

有时,图像内的文本会因透视角度而变形(例如,我笔记本电脑上的'kubernetes'贴纸)。

Perspective transformation demo
透视变换演示

如果我们只是将原始的旋转边界框送入识别模型,模型不太可能正确识别字符。在这种情况下,我们需要使用OpenCV来做透视变换。

val rotationMatrix = getPerspectiveTransform(srcPtsMat, targetPtsMat)

warpPerspective(
  srcBitmapMat,
  recognitionBitmapMat,
  rotationMatrix,
  Size(recognitionImageWidth.toDouble(), recognitionImageHeight.toDouble())
)

之后,我们再次使用TFLite支持库来调整大小、灰度,并将转换后的图像在边界框内归一化。

val imageProcessor =
  ImageProcessor.Builder()
    .add(ResizeOp(height, width, ResizeOp.ResizeMethod.BILINEAR))
    .add(TransformToGrayscaleOp())
    .add(NormalizeOp(mean, std))
    .build()

最后,我们运行文本识别模型,从模型输出中映射出字符和数字,并更新应用程序的用户界面。

recognitionInterpreter.run(recognitionTensorImage.buffer, recognitionResult)

var recognizedText = ""
for (k in 0 until recognitionModelOutputSize) {
  var alphabetIndex = recognitionResult.getInt(k * 8)
  if (alphabetIndex in 0..alphabets.length - 1)
    recognizedText = recognizedText + alphabets[alphabetIndex]
}
Log.d("Recognition result:", recognizedText)
if (recognizedText != "") {
  ocrResults.put(recognizedText, getRandomColor())
}

这就是了。我们现在能够在我们的应用程序中使用TFLite从输入的图像中提取文本。

最后,如果你只是想要一个随时可用的OCR SDK,谷歌也通过ML Kit提供了设备上的OCR功能,它在下面使用TFLite,应该足以满足大多数OCR使用情况。在有些情况下,你可能想用TFLite建立你自己的OCR解决方案,比如说。

  • 你有你自己的文本检测/识别TFLite模型,你想使用。

  • 你有特殊的业务需求(例如识别颠倒的文字),需要定制OCR管道。

  • 你想支持ML Kit没有涵盖的语言。

  • 你的目标用户设备不一定安装有Google Play服务

  • 你想控制用于运行你的模型的硬件后端(CPU/GPU/等)。

在这些情况下,我希望这个教程和我们的实例实现可以帮助你开始在你的应用程序中建立你自己的OCR功能。

你可以通过下面的资源了解更多关于OCR的信息。

鸣谢

作者要感谢Tian Lin的有益反馈,感谢社区贡献者@Tulasi123789@risingsayak之前使用TFLite进行的OCR工作(创建并上传模型到TF Hub,提供配套的笔记本等)。