2021 年 9 月 27 日 — 作者:Wei Wei,TensorFlow 开发者倡导者 常言道,“一张图片胜过千言万语”。图像蕴藏着丰富的视觉信息,但有时关键在于其中的文字。虽然对于识字的人类来说,从图像中读取文字轻而易举,但我们如何利用计算机视觉和机器学习来教会计算机完成这一任务呢?今天,我们将向您展示如何在 Android 设备上使用...
作者:Wei Wei,TensorFlow 开发者倡导者
常言道,“一张图片胜过千言万语”。图像蕴藏着丰富的视觉信息,但有时关键在于其中的文字。虽然对于识字的人类来说,从图像中读取文字轻而易举,但我们如何利用计算机视觉和机器学习来教会计算机完成这一任务呢?
今天,我们将向您展示如何使用 TensorFlow Lite 从 Android 设备上的图像中提取文字。我们将带您逐步了解最近开源的 [光学字符识别 (OCR) Android 应用](https://github.com/tensorflow/examples/tree/master/lite/examples/optical_character_recognition/android) 的关键步骤,您可以参考该应用的完整代码。您可以看到该应用如何在下面的动画中从三个 Google 产品徽标中提取产品名称。
从图像中识别文字的过程被称为光学字符识别,在许多领域都有广泛应用。例如,[Google 地图](https://ai.googleblog.com/2017/05/updating-google-maps-with-deep-learning.html) 利用 OCR 技术自动从地理位置图像中提取信息,以改善 Google 地图。
一般来说,OCR 是一个包含多个步骤的流程。通常包括文本检测和文本识别。
在本例中,我们将利用 TensorFlow Hub 中的 [文本检测](https://tfhub.dev/sayakpaul/lite-model/east-text-detector/fp16/1) 和 [文本识别](https://tfhub.dev/tulasiram58827/lite-model/keras-ocr/float16/2) 模型。有多个不同版本的模型,以实现速度/精度之间的权衡;我们在这里使用 float16 量化模型。有关模型量化的更多信息,请参阅 [TensorFlow Lite 量化](https://tensorflowcn.cn/lite/performance/model_optimization) 部分。我们还使用 [OpenCV](https://opencv.ac.cn/),这是一个广泛使用的计算机视觉库,用于 [非最大抑制 (NMS)](https://www.coursera.org/lecture/convolutional-neural-networks/non-max-suppression-dvrjH) 和透视变换(我们将在后面详细介绍),以对检测结果进行后处理。此外,我们使用 [TFLite 支持库](https://tensorflowcn.cn/lite/inference_with_metadata/lite_support) 将图像转换为灰度并进行归一化。
从文本检测、透视变换到识别的 OCR 流程。 |
对于文本检测,由于 [检测模型](https://tfhub.dev/sayakpaul/lite-model/east-text-detector/fp16/1) 接受固定大小为 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 运行 [非最大抑制](https://www.coursera.org/lecture/convolutional-neural-networks/non-max-suppression-dvrjH) 来为每个文本块识别一个边界框。
NMSBoxesRotated(
boundingBoxesMat,
detectedConfidencesMat,
detectionConfidenceThreshold.toFloat(),
detectionNMSThreshold.toFloat(),
indicesMat
)
有时图像中的文字会发生变形(例如,我笔记本电脑上的“Kubernetes”贴纸)并带有透视角度。
透视变换演示 |
如果我们只是将原始旋转边界框直接输入识别模型,则该模型不太可能正确识别字符。在这种情况下,我们需要使用 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()
最后,我们运行文本识别模型,从模型输出中识别出字符和数字,并更新应用 UI。
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,Google 还通过 [ML Kit](https://developers.google.com/ml-kit/vision/text-recognition) 提供了设备上的 OCR 功能,它在底层使用 TFLite,应该足以满足大多数 OCR 使用场景。在某些情况下,您可能需要使用 TFLite 构建自己的 OCR 解决方案,例如:
在这些情况下,我希望本教程和我们的 [示例实现](https://github.com/tensorflow/examples/tree/master/lite/examples/optical_character_recognition/android) 可以帮助您开始在应用中构建自己的 OCR 功能。
您可以通过以下资源了解更多关于 OCR 的信息。
作者要感谢 Tian Lin 提供的有益反馈,以及社区贡献者 [@[Tulasi123789](https://twitter.com/Tulasi123789)] 和 [@[risingsayak](https://twitter.com/risingsayak)] 之前在使用 TFLite 进行 OCR 方面的贡献(创建和上传到 TF Hub 的模型,提供配套笔记本等)。