Tesseract OCR框架(二) —— 基于Tesserac
版本记录
版本号 | 时间 |
---|---|
V1.0 | 2019.05.26 星期日 |
前言
Tesseract OCR
是谷歌Google维护的一个框架,用于从图片中提取文字,有iOS
和C++
多种版本,在很多领域都有广泛的应用。接下来我们就一起走进这个框架(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,Windows
和Mac OS X
。
哦哦! 你打算如何在iOS中使用它? Nexor Technology为Tesseract OCR
创建了兼容的Swift包装器。
Adding the Tesseract Framework
首先,您必须通过CocoaPods安装Tesseract OCR iOS,这是一个广泛使用的iOS项目依赖管理器。
如果您尚未在计算机上安装CocoaPods
,请打开终端,然后执行以下命令:
sudo gem install cocoapods
在请求完成CocoaPods
安装时输入您的计算机密码。
接下来,cd
到Love 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/Resources
。 tessdata
文件夹包含一堆英语和法语培训文件。 你将在本教程中处理的爱情诗主要是英文,但也包含一些法语。 Très
浪漫主义!
现在,您将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
之前,确认已选择目标。
您现在应该在导航器中看到一个蓝色的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
假定它正在处理统一的文本块,但您的样本图像有多个段落。Tesseract
的pageSegmentationMode
让Tesseract
引擎知道文本的划分方式。在这种情况下,将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,黑暗或不均匀的照明,图像噪声,倾斜的文本方向和厚厚的暗图像边界都会导致不太完美的结果。
接下来,您将提高图像的质量。
Improving Image Quality
Tesseract iOS
框架曾经有内置的方法来提高图像质量,但是这些方法已被弃用,框架的文档现在建议使用Brad Larson
的GPUImage 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) 初始化
GPUImageAdaptiveThresholdFilter
。 GPUImageAdaptiveThresholdFilter确定像素周围的局部亮度,如果低于该局部亮度则将像素变为黑色,如果高于此值则变为白色。 这对于在不同光照条件下挑选文本非常有用。 - 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
框架的图片中的文字识别简单示例的基本情况,感兴趣的给个赞或者关注~~~