Tesseract OCR框架(二) —— 基于Tesseract OCR iOS框架的图片中的文字识别简单示例(一)

版本记录

版本号 时间
V1.0 2019.05.26 星期日

前言

Tesseract OCR是谷歌Google维护的一个框架,用于从图片中提取文字,有iOSC++多种版本,在很多领域都有广泛的应用。接下来我们就一起走进这个框架(iOS部分)。感兴趣的可以看下面几篇.
1. Tesseract OCR框架(一) —— 基本概览(一)

开始

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

OCR是从图像中电子提取文本的过程。 您以前无疑已经看过它 - 它被广泛用于处理从扫描文档到平板电脑上的手写涂鸦,再到 Word Lens technology in the GoogleTranslate app

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

在本教程中,您将学习如何使用由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中,然后构建并运行入门应用程序。 点击一下即可了解用户界面。

回到Xcode,看一下ViewController.swift。 它已经包含一些@IBOutlets和空的@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) 图像选择器还需要UIImagePickerControllerDelegate来访问图像选择器控制器的代理函数。
  • 3) imagePickerController(_:didFinishPickingMediaWithInfo :)代理函数将返回所选图像。

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


Tesseract’s Limitations

Tesseract OCR非常强大,但确实有以下限制:

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

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


Adding the Tesseract Framework

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

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

sudo gem install cocoapods

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

接下来,cdLove In A Snap入门项目文件夹。 例如,如果您已将Love In A Snap添加到桌面,则可以输入:

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


How Tesseract OCR Works

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

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

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

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

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

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

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

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


Adding Trained Data

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

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

Your poem vil impress vith French! Ze language ov love! *Haugh* *Haugh* *Haugh*

现在,您将tessdata添加到您的项目中。 Tesseract OCR iOS要求您添加tessdata作为引用文件夹。

  • 1) 将tessdata文件夹从Finder拖到Xcode左侧Project导航器中的Love In A Snap文件夹中。
  • 2)选择Copy items if needed
  • 3) 将Added Folders选项设置为Create folder references
  • 4) 单击Finish之前,确认已选择目标。
Add tessdata as a referenced folder

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

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


Loading the Image

首先,您将创建一种从设备的相机或照片库访问图像的方法。

打开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)

在这里,您:

  • 1) 创建将显示在屏幕底部的action sheet alert
  • 2) 如果设备有摄像头,请在操作表中添加Take Photo按钮。
  • 3) 将Choose Existing按钮添加到操作表。
  • 4) 在操作表中添加Cancel按钮。 选择此按钮会删除操作表而不执行操作,因为它的类型为.cancel
  • 5) 最后,present the alert

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

所以当用户点击cameraButton时,这段代码:

  • 1) 显示视图控制器的活动指示器。
  • 2) 创建图像选择器。
  • 3) 将当前视图控制器指定为该图像选择器的委托。
  • 4) 告诉图像选择器作为相机界面呈现给用户。
  • 5) 限制图像选择器的媒体类型,以便用户只能捕获静止图像。
  • 6) 显示图像选择器。
  • 7) 一旦图像选择器完成动画进入视图,就隐藏活动指示器。

同样,在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)
}

在这里,您:

  • 1) 检查info.originalImage键是否包含图像值。 如果没有,则图像选择器将自己从视图中移除,并且该方法的其余部分不会执行。
  • 2) 如果info.originalImage确实包含图像,则在Tesseract完成其工作时显示活动指示符。
  • 3) 在图像选择器动画不在视图之外后,将图像传递给performImageRecognition

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

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

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

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

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

都好? 如果是这样,那么终于可以使用Tesseract了!


Implementing Tesseract OCR

首先,添加import 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假定它正在处理统一的文本块,但您的样本图像有多个段落。 TesseractpageSegmentationModeTesseract引擎知道文本的划分方式。在这种情况下,将pageSegmentationMode设置为.auto以允许全自动页面分段,从而能够识别段落中断。
  • 4) 将所选图像分配给tesseract实例。
  • 5) 告诉Tesseract开始识别你的文字。
  • 6) 将Tesseract的识别文本输出放入textView
  • 7) 自OCR完成后隐藏活动指示器。

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


Processing Your First Image

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

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

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

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

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

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

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


Scaling Images While Preserving Aspect Ratio

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

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

这导致两个公式。

公式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
  }
}

此代码执行以下操作:

  • 1) UIImage扩展允许您直接通过UIImage对象访问它包含的任何方法。
  • 2) scaledImage(_ :)接收返回图像所需的最大尺寸(高度或宽度)。
  • 3) scaledSize变量最初包含一个CGSize,其高度和宽度等于maxDimension
  • 4) 您可以计算UIImage的较小尺寸,以便scaledSize保留图像的纵横比。 如果图像的宽度大于其高度,请使用公式1。否则,使用公式2。
  • 5) 创建图像上下文,将原始图像绘制为大小为scaledSize的矩形,然后在结束上下文之前从该上下文中获取图像。
  • 6) 返回结果图像。

现在,在performImageRecognition(_ :)的顶部,包括:

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

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

然后,将tesseract.image = image替换为:

tesseract.image = scaledImage

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

从照片库中再次构建,运行和选择诗歌。

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


Improving OCR Accuracy

“Garbage In,Garbage Out.”提高输出质量的最简单方法是提高输入质量。 正如谷歌在其Tesseract OCR网站上列出的那样As Google lists on their Tesseract OCR site,黑暗或不均匀的照明,图像噪声,倾斜的文本方向和厚厚的暗图像边界都会导致不太完美的结果。

Examples of potentially problematic image inputs that can be corrected for improved results. Source: Google’s Tesseract OCR site.

接下来,您将提高图像的质量。


Improving Image Quality

Tesseract iOS框架曾经有内置的方法来提高图像质量,但是这些方法已被弃用,框架的文档现在建议使用Brad LarsonGPUImage framework框架。

GPUImage可以通过CocoaPods获得,因此在Podfile中的pod'TesseractOCRiOS'下面,添加:

pod 'GPUImage'

然后,在终端中,重新运行:

pod install

现在应该可以在项目中使用GPUImage

注意:它还会添加数百个编译警告。 您也可以放心地忽略它们。

回到ViewController.swift,添加以下import TesseractOCR以使类中的GPUImage可用:

import GPUImage

直接在scaledImage(_ :)下面,也在UIImage扩展名中,添加:

func preprocessedImage() -> UIImage? {
  // 1
  let stillImageFilter = GPUImageAdaptiveThresholdFilter()
  // 2
  stillImageFilter.blurRadiusInPixels = 15.0
  // 3
  let filteredImage = stillImageFilter.image(byFilteringImage: self)
  // 4
  return filteredImage
}

在这里,您:

  • 1) 初始化GPUImageAdaptiveThresholdFilterGPUImageAdaptiveThresholdFilter确定像素周围的局部亮度,如果低于该局部亮度则将像素变为黑色,如果高于此值则变为白色。 这对于在不同光照条件下挑选文本非常有用。
  • 2) blurRadius表示每个字符的平均模糊度(以像素为单位)。 它默认为4.0,但您可以使用此值来改善OCR结果。 在上面的代码中,您将其设置为15.0,因为当图像宽度为1,000点时,平均字符模糊看起来大约为15.0像素宽。
  • 3) 通过过滤器运行图像以选择性地返回新的图像对象。
  • 4) 如果过滤器返回图像,则返回该图像。

回到performImageRecognition(_ :),紧接在scaledImage常量实例化下面,添加:

let preprocessedImage = scaledImage.preprocessedImage() ?? scaledImage

此代码尝试通过GPUImage过滤器运行scaledImage,但如果preprocessedImage()的过滤器失败,则默认使用未过滤的scaledImage

然后,将tesseract.image = scaledImage替换为:

tesseract.image = preprocessedImage

这要求Tesseract处理缩放和过滤的图像。

现在您已经完成所有这些工作,再次构建,运行并选择图像。

瞧! 希望您的结果现在比以前更完美或更接近完美。

但如果你眼中的苹果没有被命名为“Lenore”,那么他或她可能不会欣赏这首来自你的诗......他们可能想知道这个“Lenore”角色是谁!

“Lenore”替换为您心爱的人的姓名... presto chango! 你已经为你的爱人和你的爱人创作了一首爱情诗。

而已! 你的Love In A Snap应用程序是完整的 - 肯定会赢得你崇拜的人的心脏。

尝试使用其他文本的应用程序,以查看OCR结果如何在源之间变化,并根据需要下载更多语言数据download more language data

您还可以训练Tesseract以进一步提高其输出。 毕竟,如果你能够用眼睛或耳朵甚至指尖解读字符,你就已经成为字符识别方面的可认证专家,并且完全有能力教你的电脑,而不是已经知道的。

后记

本篇主要讲述了基于Tesseract OCR iOS框架的图片中的文字识别简单示例的基本情况,感兴趣的给个赞或者关注~~~

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

推荐阅读更多精彩内容