[译]适用于iOS的Tesseract OCR教程

2,929 阅读10分钟

导语:在本教程中,您将学习如何使用Tesseract使用OCR读取和处理从图像中提取的文本。

本教程适用:Xcode 10.2,Swift 5, iOS 12.1 和TesseractOCRiOS (5.0.1)

OCR是从图像中电子提取文本的过程。您以前肯定看到过它 - 它被广泛用于处理扫描文档还有平板电脑上的手写涂鸦,再到GoogleTranslate应用程序中的Word Lens技术

在本教程中,您将学习如何使用由Google维护的开源OCR引擎Tesseract从爱情诗中获取文本并使其成为您自己的文本。准备好留下深刻印象!

开 始

这里下载本教程的材料,然后将文件夹解压缩到方便的位置。

Love In A Snap目录包含: · Love In A Snap Starter: 本教程的初始工程 · Love In A Snap Final:最终完成的工程 · Resources:你需要使用OCR处理的图片和包含Tesseract语言数据的目录

在Xcode中打开Love In A Snap Starter/Love In A

Snap.xcodeproj,然后构建运行开始的app。熟悉一下UI。

回到Xcode,看一下ViewController.swift 它已经包含了几个@IBOutlet和空的@IBAction方法,这几个方法已经和Main.storyboard接口连接好了。它还包含了performImageRecognition(_:),在这个函数里Tesseract进行处理。

往下滚动你会看见:

// 1
// MARK: - UINavigationControllerDelegate
extension ViewController: UINavigationControllerDelegate {
}
// 2
// MARK: - UIImagePickerControllerDelegate
extension ViewController: UIImagePickerControllerDelegate {
  // 3
  func imagePickerController(_ picker: UIImagePickerController,
    didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    // TODO: Add more code here...
  }
}

  1. 帮助你方便地添加图片的UIImagePickerViewController需要UINavigationControllerDelegate来访问图片选择控制器的代理方法。
  2. image picker也要UIImagePickerControllerDelegate来访问图片选择控制器的代理方法;
  3. imagePickerController(_:didFinishPickingMediaWithInfo:)代理方法返回选择的图片;

现在,轮到你接管并将这个应用程序变为现实!

Tesseract的限制

Tesseract OCR非常强大,但确实有以下限制: · 与某些OCR引擎不同 - 例如美国邮政局用于对邮件进行分类的引擎 - Tesseract没有接受过识别手写的培训,并且总共限制了大约100种字体。 · Tesseract需要一些预处理来改善OCR结果:图像需要适当缩放,具有尽可能多的图像对比度,并且文本必须水平对齐。 · 最后,Tesseract OCR仅适用于Linux,Windows和Mac OS X.

哦不!你打算如何在iOS中使用它? Nexor Technology为Tesseract OCR创建了兼容的Swift包装器。

添加Tesseract框架

首先,您必须通过CocoaPods安装Tesseract OCR iOS,这是一个广泛使用的iOS项目依赖管理器。

如果您尚未在计算机上安装CocoaPods,请打开终端,然后执行以下命令:

sudo gem install cocoapods

在请求完成CocoaPods安装时输入您的计算机密码。

下一步,进入到Love In A Snap Starter项目的目录下。如果你把它放到了桌面,就可以用下面的命令

cd ~/Desktop/"Love In A Snap/Love In A Snap Starter"

然后输入:

pod init

这将为您的项目创建一个Podfile。用以下内容替换Podfile的内容:

platform :ios, '12.1'

target 'Love In A Snap' do
  use_frameworks!

  pod 'TesseractOCRiOS'

end

这告诉CocoaPods您希望将TesseractOCRiOS包含为项目的依赖项。

回到终端,输入:

pod install

这会将pod安装到您的项目中。

当终端输出指示时,“Please close any current Xcode sessions and use Love In A Snap.xcworkspace for this project from now on.”使用Xcode打开Love In A Snap.xcworkspace

Tessertact OCR如何工作

一般来说,OCR使用人工智能来查找和识别图像中的文本。

一些OCR引擎依赖于一种称为机器学习的人工智能。机器学习允许系统通过识别和预测模式来学习和适应数据。

Tesseract OCR iOS引擎使用称为神经网络的特定类型的机器学习模型。

神经网络在人脑中的模型之后被松散地建模。我们的大脑包含大约860亿个连接的神经元,这些神经元被分组成各种网络,能够通过重复学习特定的功能。类似地,在更简单的尺度上,人工神经网络接收多种样本输入,并通过随时间的成功和失败来学习,从而产生越来越准确的输出。这些样本输入称为“训练数据”。

在调教系统时,这个培训数据:

  1. 通过神经网络的输入节点进入。
  2. 通过称为“边缘”的节点间连接进行传播,每个连接都以输入应该沿着该路径传播的感知概率加权。
  3. 通过一层或多层“隐藏”(即内部)节点,这些节点使用预定的启发式处理数据。
  4. 通过输出节点返回预测结果。

然后将该输出与期望的输出进行比较,并相应地调整边缘权重,使得传递到神经网络的后续训练数据返回越来越准确的结果。

神经网络的模式

Tesseract寻找像素,字母,单词和句子的图案。 Tesseract使用称为自适应识别的两步方法。对数据进行一次传递以识别字符,然后第二次传递以填写任何不太可能符合给定单词或句子上下文的字母的字母。

添加训练数据

为了在给定语言的范围内更好地磨练其预测,Tesseract需要特定于语言的训练数据来执行其OCR。

导航到Finder中的Snap / Resources中的Love。 tessdata文件夹包含一堆英语和法语培训文件。你将在本教程中处理的爱情诗主要是英文,但也包含一些法语。 Très浪漫主义!

现在,您将tessdata添加到您的项目中。 Tesseract OCR iOS要求您添加tessdata作为referenced folder

  1. 将tessdata文件夹从Finder拖到Xcode左侧Project导航器中的Love In A Snap文件夹中。
  2. 选择Copy items if needed
  3. Added Folders选项设置为Create folder references
  4. 确认选择target然后点击完成

添加tessdata作为引用文件夹

您现在应该在导航器中看到一个蓝色的tessdata文件夹。蓝色表示引用了文件夹而不是Xcode组。

现在您已经添加了Tesseract框架和语言数据,现在是时候开始使用有趣的编码了!

加载文件

首先,您需要先完成从设备的相机或照片库访问图像的方法。

打开ViewController.swift 然后在takePhoto(_:)中加入下面的代码:

// 1
let imagePickerActionSheet =
  UIAlertController(title: "Snap/Upload Image",
                    message: nil,
                    preferredStyle: .actionSheet)

// 2
if UIImagePickerController.isSourceTypeAvailable(.camera) {
  let cameraButton = UIAlertAction(
    title: "Take Photo",
    style: .default) { (alert) -> Void in
      // TODO: Add more code here...
  }
  imagePickerActionSheet.addAction(cameraButton)
}

// 3
let libraryButton = UIAlertAction(
  title: "Choose Existing",
  style: .default) { (alert) -> Void in
    // TODO: Add more code here...
}
imagePickerActionSheet.addAction(libraryButton)

// 4
let cancelButton = UIAlertAction(title: "Cancel", style: .cancel)
imagePickerActionSheet.addAction(cancelButton)

// 5
present(imagePickerActionSheet, animated: true)

import UIKit下面添加

import MobileCoreServices

这使ViewController可以访问kUTTypeImage抽象图像标识符,您将使用它来限制图像选择器的媒体类型。

现在在cameraButton UIAlertAction的闭包中,用以下代码替换// TODO注释:

// 1
self.activityIndicator.startAnimating()
// 2
let imagePicker = UIImagePickerController()
// 3
imagePicker.delegate = self
// 4
imagePicker.sourceType = .camera
// 5
imagePicker.mediaTypes = [kUTTypeImage as String]
// 6
self.present(imagePicker, animated: true, completion: {
  // 7
  self.activityIndicator.stopAnimating()
})

同样,在libraryButton的闭包里面也添加:

self.activityIndicator.startAnimating()
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary
imagePicker.mediaTypes = [kUTTypeImage as String]
self.present(imagePicker, animated: true, completion: {
  self.activityIndicator.stopAnimating()
})

这与您刚刚添加到cameraButton的闭包中的代码相同,除了imagePicker.sourceType = .photoLibrary。在这里,您将图像选择器设置为显示设备的照片库而不是相机。

接下来,要处理捕获或选定的图像,请将以下内容插入imagePickerController(_:didFinishPickingMediaWithInfo :)

// 1
guard let selectedPhoto =
  info[.originalImage] as? UIImage else {
    dismiss(animated: true)
    return
}
// 2
activityIndicator.startAnimating()
// 3
dismiss(animated: true) {
  self.performImageRecognition(selectedPhoto)
}

您将在本教程的下一部分中编写performImageRecognition代码,但是,现在,只需打开Info.plist。将光标悬停在顶部单元格信息属性列表上,然后在出现时单击+按钮两次。

在这两个新条目的密钥字段中,分别添加Privacy – Camera Usage DescriptionPrivacy – Photo Library Usage Description。为每个选择String类型。然后在“值”列中,分别在请求访问其相机和照片库的权限时,输入要向用户显示的任何文本。

构建并运行您的项目。点击Snap / Upload Image按钮,您将看到刚刚创建的UIAlertController。

测试出操作表选项,并在出现提示时授予应用程序访问相机和/或库的权限。按预期确认照片库和相机显示。

注意:如果您在模拟器上运行,则没有可用的物理相机,因此您将看不到“拍照”选项。

实现Tesseract OCR

首先,导入MobileCoreServices以使ViewController可以使用Tesseract框架:

import TesseractOCR

现在,在performImageRecognition(_ :)中,用以下内容替换// TODO注释:

// 1
if let tesseract = G8Tesseract(language: "eng+fra") {
  // 2
  tesseract.engineMode = .tesseractCubeCombined
  // 3
  tesseract.pageSegmentationMode = .auto
  // 4
  tesseract.image = image
  // 5
  tesseract.recognize()
  // 6
  textView.text = tesseract.recognizedText
}
// 7
activityIndicator.stopAnimating()

由于这是本教程的内容,这里有我们一行一行的详细解释:

  1. 使用新的G8Tesseract对象初始化tesseract,该对象将使用英语(“eng”) - 和法语(“fra”) - 训练有素的语言数据。请注意,诗的法语重音字符不在英文字符集中,因此必须包含法语训练数据以便出现这些重音符号
  2. Tesseract提供三种不同的OCR引擎模式:.tesseractOnly,这是最快但最不准确的方法; .cubeOnly,由于它采用了更多的人工智能,因此速度更慢但更准确;和.tesseractCubeCombined,它同时运行.tesseractOnly和.cubeOnly。 .tesseractCubeCombined是最慢的,但由于它是最准确的,你将在本教程中使用它
  3. 默认情况下,Tesseract假定它正在处理统一的文本块,但您的样本图像有多个段落。 Tesseract的pageSegmentationMode让Tesseract引擎知道文本的划分方式。在这种情况下,将pageSegmentationMode设置为.auto以允许全自动页面分段,从而能够识别段落中断
  4. 将所选图像分配给tesseract实例
  5. 告诉Tesseract开始识别你的文字
  6. 将Tesseract的识别文本输出放入textView
  7. 自OCR完成后隐藏活动指示器

现在,是时候测试第一批新代码了!

处理你的第一张图片

在Finder中,导航到Love In A Snap / Resources / Lenore.png以查找示例图像。

Lenore.png是一首写给“Lenore”的爱情诗的形象,但通过一些编辑,你可以把它变成一首肯定会引起你所渴望的人的注意的诗!

虽然您可以打印图像的副本,然后使用应用程序拍摄照片以执行OCR,您可以轻松自己并将图像直接添加到设备的相机胶卷。这消除了人为错误,进一步照明不一致,文本偏斜和打印有缺陷的可能性。毕竟,图像已经是黑暗和模糊的。

注意:如果您使用的是模拟器,只需将图像文件拖放到模拟器上即可将其添加到其照片库中。

构建并运行您的应用程序。点击Snap / Upload Image,点击Choose Existing,然后从照片库中选择样本图像以通过OCR运行它。

注意:您可以放心地忽略TesseractOCR库生成的数百个编译警告。

哦哦!什么都没出现!这是因为当前的图像尺寸太大,以至于Tesseract无法处理。是时候改变了!

在保持纵横比的同时缩放图像

图像的纵横比是其宽度和高度之间的比例关系。从数学上讲,要在不影响纵横比的情况下缩小原始图像的大小,必须保持宽高比不变。

当您知道原始图像的高度和宽度,并且您知道最终图像的所需高度或宽度时,您可以重新排列宽高比等式,如下所示:

由此我们可以得到两个公式: 公式1:当图像的宽度大于其高度时。

Height1/Width1 * width2 = height2

公式2:当图像的高度大于其宽度时。

Width1/Height1 * height2 = width2

现在,将以下扩展和方法添加到ViewController.swift的底部:

// MARK: - UIImage extension

//1
extension UIImage {
  // 2
  func scaledImage(_ maxDimension: CGFloat) -> UIImage? {
    // 3
    var scaledSize = CGSize(width: maxDimension, height: maxDimension)
    // 4
    if size.width > size.height {
      scaledSize.height = size.height / size.width * scaledSize.width
    } else {
      scaledSize.width = size.width / size.height * scaledSize.height
    }
    // 5
    UIGraphicsBeginImageContext(scaledSize)
    draw(in: CGRect(origin: .zero, size: scaledSize))
    let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    // 6
    return scaledImage
  }
}

现在,在performImageRecognition(_ :)的顶部加入:

let scaledImage = image.scaledImage(1000) ?? image

这将尝试缩放图像,使其不超过1,000点宽或长。如果scaledImage()无法返回缩放图像,则常量将默认为原始图像。

然后用下面的代码代替tesseract.image = image

tesseract.image = scaledImage

这会将缩放后的图像指定给Tesseract对象。

从照片库中再次构建:

但很可能你的结果并不完美。还有改进的余地......