之前在用
扫描全能王
的时候, 一直在思考这个功能是怎么实现的, 最近找到了一下资料, 和大家一起分享一下.
1.OCR是什么?
OCR(Optical Character Recognition) 光学字符识别, 是从图像中电子扫描提取文本的过程, 可以在文档编辑等多种形式重用它,例如: 文本搜索/压缩等用途。
OCR这是广泛用于你的平板电脑进行扫描文件、手写涂鸦等, 新闻报道(2015/01/14)谷歌将此添加到他们的应用程序之中, 今天我们共同学习在自己的iPhone上如何使用它!
OCR开源库地址---Tesseract;
2. 介绍Tesseract
Tesseract OCR 虽很牛逼,但有以下一些限制:
1. Unlike some OCR engines (like those used by the U.S. Postal Service to sort mail),
Tesseract is unable to recognize handwriting and is limited to about 64 fonts in total.
Tesseract不可以识别出手写的字, 并且字数仅限于约64个 <有些疑惑>;
2. Tesseract需要一些预处理提高OCR结果;图像需要适当比例,尽可能多的形象对比,有水平排列的文本。
3. 最后, Tesseract OCR 只能工作在Linux, Windows, and Mac OS X平台之上;
Paste_Image.png
(ーー゛) 那怎么才能在iOS设备上使用呢??
幸运的是: 有Objective-C对Tesseract OCR包装一层的框架可以利用在swift和iOS设备上,
不要担心, swift兼容版本包括这个套装软件.
3. Love In A Snap
你怎么都不会想到Ray Wenderlich会在蓝色情人节(Valentine’s Day)前夕更新这篇文章, 对吧? 肯定不会! 我们设法找出万全方法打动你内心的欲望。与你共同创建爱的礼物! -_-
在这片教程里, 你将会学到怎么使用Google开源的框架---Tesseract, 你可以通过Love In A Snap来让你拍照的爱情诗和“让它自己的”取代原诗人的缪斯女神的名字与自己的感情的对象的名称。准备留下深刻印象吧!
3.1 开始
你可以从github 下载初始项目到本地;
这个压缩包包含几个文件:
1. LoveInASnap: Xcode项目;
2. Tesseract Resources: Tesseract框架和语言包;
3. Image Resources: 几张样张图片包含的文字之后可以使用.
Paste_Image.png
打开当前工程 LoveinASnap.xcodeproj
,你会发现在从Storyboard
中关联到ViewController.swift
中有一些@IBOutlets
和空白的@IBAction
方法;
在代码中你看到有两个方法来处理展示和关闭View的活动状态;
func addActivityIndicator() {
activityIndicator = UIActivityIndicatorView(frame: view.bounds)
activityIndicator.activityIndicatorViewStyle = .WhiteLarge
activityIndicator.backgroundColor = UIColor(white: 0, alpha: 0.25)
activityIndicator.startAnimating()
view.addSubview(activityIndicator)
}
func removeActivityIndicator() {
activityIndicator.removeFromSuperview()
activityIndicator = nil
}
接下来有一些方法是移动View的位置防止键盘遮到输入框;
func moveViewUp() {
if topMarginConstraint.constant != originalTopMargin {
return
}
topMarginConstraint.constant -= 135
UIView.animateWithDuration(0.3, animations: { () -> Void in
self.view.layoutIfNeeded()
})
}
func moveViewDown() {
if topMarginConstraint.constant == originalTopMargin {
return
}
topMarginConstraint.constant = originalTopMargin
UIView.animateWithDuration(0.3, animations: { () -> Void in
self.view.layoutIfNeeded()
})
}
最后, 剩余的方法依据用户的交互来处理键盘和View的位置, moveViewUp()
和moveViewDown()
@IBAction func backgroundTapped(sender: AnyObject) {
view.endEditing(true)
moveViewDown()
}
func textFieldDidBeginEditing(textField: UITextField) {
moveViewUp()
}
@IBAction func textFieldEndEditing(sender: AnyObject) {
view.endEditing(true)
moveViewDown()
}
func textViewDidBeginEditing(textView: UITextView) {
moveViewDown()
}
尽管重要的是用户体验(UX--User Experience)
,但这些方法是本教程最关键的部分,所以提前准备好了界面, 这样你就可以马上进入有趣的编码阶段。
在编写代码前, 先编译 运行一个项目, 点击一下界面, 感受一下UI, 目前TextView还不可以编辑, 点击输入框可以吊起和关闭键盘, 你可以为这个APP带来生命! Yes .You Can _
Paste_Image.png3.2 添加Tesseract Framework
3.2.1.第一步添加TesseractOCR.framework;
在解压的文件中找到Tesseract Resources
文件夹, 在Xcode中添加TesseractOCR.framework
, 确保添加时选中的是Copy items if needed
, 最终点击Finish
;
3.2.2.第二步添加tessdata
同样的方式添加tessdata
文件夹;
3.2.3.添加系统依赖库, 包括
1. libstdc++.6.0.9.dylib(or libstdc++.6.0.9.tbd);
2. CoreImage.framework;
3. TesseractOCR.framework
3.2.4.配置
1.选中Build Settings
利用上边的搜索框
搜索Other Linker Flags
, 在已经存在的keys
中拼接上-lstdc++
<亲测没改没问题>, 然后使用同样的方法在Build Settings
下找到C++ Standard Library
, 并将之设置为Compiler Default
; 在找到Enable Bitcode
将之设置为NO
;
2.因为Tesseract是一个OC框架, 所以需要创建一个桥接头文件
,
关于iOS OC与Swift混编的问题就不在此描述了;
在LoveInASnap-Bridging-Header.h
中添加
#import <TesseractOCR/TesseractOCR.h>
Paste_Image.png
至此你就可以在项目中使用该框架了.
3.3 加载图片
第一步骤需要在OCR APP 中添加被扫描的图片, 最简单的方法是利用UIImagePickerController
来从相册或相机中获取.
打开ViewController.swift
在takePhoto()
添加代码, 如下:
@IBAction func takePhoto(sender: AnyObject) {
// 1. 关闭键盘, 使得view回到初始位置
view.endEditing(true)
moveViewDown()
// 2. 创建一个sheet样式的UIAlertController
let imagePickerActionSheet = UIAlertController(title: "Snap/Upload Photo",
message: nil, preferredStyle: .ActionSheet)
// 3. 打开相机, 如果在模拟器上,将不会出现该选项
if UIImagePickerController.isSourceTypeAvailable(.Camera) {
let cameraButton = UIAlertAction(title: "Take Photo",
style: .Default) { (alert) -> Void in
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .Camera
self.presentViewController(imagePicker,
animated: true,
completion: nil)
}
imagePickerActionSheet.addAction(cameraButton)
}
// 4 取照片
let libraryButton = UIAlertAction(title: "Choose Existing",
style: .Default) { (alert) -> Void in
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .PhotoLibrary
self.presentViewController(imagePicker,
animated: true,
completion: nil)
}
imagePickerActionSheet.addAction(libraryButton)
// 5 取消
let cancelButton = UIAlertAction(title: "Cancel",
style: .Cancel) { (alert) -> Void in
}
imagePickerActionSheet.addAction(cancelButton)
// 6 展示提示框
presentViewController(imagePickerActionSheet, animated: true,
completion: nil)
}
如前所述列表中的限制条件,必须在一定位置约束才会出现最优OCR图像结果。如果图像太大或太小,Tesseract都可能返回不理想的结果,奇怪的是,崩溃的时候会报出EXC_BAD_ACCESS错误。
为此,需要写个方法来调整图像在尽可能不扭曲图像的前提下改变其宽高比。
3.4缩放图像保持长宽比
图像的宽高比例关系离不开width and height
, 在数学中, 减少原始图像的大小不影响长宽比, 但是你必须保持宽高比不变。
当你知道一个图片的宽和高, 你所期望的最终宽高可以通过公式重新排列比例来计算:
Paste_Image.png由此产生两个公式:
1. Height1/Width1 * width2 = height2;
2. Width1/Height1 * height2 = width2
你将使用这两个公式(formulas)来保证宽高比例;
在ViewController.swift,
添加如下方法
func scaleImage(image: UIImage, maxDimension: CGFloat) -> UIImage {
var scaledSize = CGSize(width: maxDimension, height: maxDimension)
var scaleFactor: CGFloat
if image.size.width > image.size.height {
scaleFactor = image.size.height / image.size.width
scaledSize.width = maxDimension
scaledSize.height = scaledSize.width * scaleFactor
} else {
scaleFactor = image.size.width / image.size.height
scaledSize.height = maxDimension
scaledSize.width = scaledSize.height * scaleFactor
}
UIGraphicsBeginImageContext(scaledSize)
image.drawInRect(CGRectMake(0, 0, scaledSize.width, scaledSize.height))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return scaledImage
}
传入maxDimension,该方法以图像的高度和宽度,哪个更大,设置最大的等于maxDimension。然后计算另一边图像基于比例适当,重绘计算原始图像以适应新框架,最后将新扩展的图像返回给调用的方法。
3.5 实现Tesseract OCR
在ViewController.swift
的下侧找到延展的类UIImagePickerControllerDelegate
, 加入代码, 如下:
func imagePickerController(picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let selectedPhoto = info[UIImagePickerControllerOriginalImage] as! UIImage
let scaledImage = scaleImage(selectedPhoto, maxDimension: 640)
addActivityIndicator()
dismissViewControllerAnimated(true, completion: {
self.performImageRecognition(scaledImage)
})
}
imagePickerController(_:didFinishPickingMediaWithInfo:)
是UIImagePickerDelegate
的代理方法, 返回的是选择图片的信息(一个字典), 从info
中利用key--UIImagePickerControllerOriginalImage
获取到原始图片
, 在利用刚写过的方法scaleImage(_:maxDimension:)
来重新缩放图片;
当Tesseract工作时, 可以通过调用addActivityIndicator()
来关闭用户交互, 显示活动状态; 然后可以关闭UIImagePicker
, 通过performImageRecognition()
进行加工;
添加如下方法, 这里将展示OCR的神奇之处
:
func performImageRecognition(image: UIImage) {
// 1 初始化G8Tesseract的对象tesseract
let tesseract = G8Tesseract()
// 2 Tesseract将会从 .traineddata 文件搜索你指定的语言, 将会搜索fra.traineddata<法语>和eng.traineddata<英语>
tesseract.language = "eng+fra"
// 3 可以指定三种不同的engineMode类型, TesseractCubeCombined: 最快最不精确, .CubeOnly: 慢点但更加精确, 采用人工智能; .TesseractCubeCombined: 同时包含前两者, 最精确也最慢.
tesseract.engineMode = .TesseractCubeCombined
// 4 因为字段不确定, 所以告诉引擎自动判断分段
tesseract.pageSegmentationMode = .Auto
// 5 只有TesseractCubeCombined限制,最长扫描时间
tesseract.maximumRecognitionTime = 60.0
// 6 为了确保图片的对比度满足要求, Tesseract的过滤器<filter>g8_blackAndWhite()会增大对比度, 已达到更好的效果
tesseract.image = image.g8_blackAndWhite()
tesseract.recognize()
// 7 将获取到的文字展示到textView上
textView.text = tesseract.recognizedText
textView.editable = true
// 8 最后移除指示器表示扫描完成
removeActivityIndicator()
}
被扫描的文字语言设置位置---第二条 [tesseract.language = "eng+fra"]
3.6 见证奇迹的时刻到了
找到之前主备的图片<包含英文和法文>, 打开模拟器, 直接将两张测试图片拖入模拟器, 将自动加入相册, 待稍后使用;
运行模拟器, Choose Existing
--选择相册中的测试图片, 出现啦! 编译出来的文字在Textview上展示出来了, Tesseract
确实完成了一个伟大的工作!!! NB
3.7 替换文字
其实吧, 主要获得到文字, 接下来的操作岂不任你摆布^_-, 原文中是一首爱情诗词, 一来在情人节之际表示一下爱慕之情, 二来再补充修改一下, 接下来就开始工作吧!
打开ViewController.swift
, 找到已存在的swapText()
, 修改代码如下:
@IBAction func swapText(sender: AnyObject) {
// 1 保证替换和被替换代码不为空
if let text = textView.text, let findText = findTextField.text,
let replaceText = replaceTextField.text {
// 2 UITextView 的替换方法
textView.text =
textView.text.stringByReplacingOccurrencesOfString(findTextField.text,
withString: replaceTextField.text, options: [], range: nil)
// 3 将输入框置为空
findTextField.text = nil
replaceTextField.text = nil
// 4 结束任务, 取消键盘相应, View复位
view.endEditing(true)
moveViewDown()
}
}
自己可以随便试试, 替换个你喜欢的ta的名字 _;
3.8 分享
可以获取, 可以更改, 怎么发给喜欢的人呢!!
最后这一段就交给分享了!
创建UIActivityViewController
, 来进行操作分享,替换当前的方法sharePoem()
;
@IBAction func sharePoem(sender: AnyObject) {
// 1 判断时候为空
if textView.text.isEmpty {
return
}
// 2 初始化并赋值文字
let activityViewController = UIActivityViewController(activityItems:
[textView.text], applicationActivities: nil)
// 3
let excludeActivities = [
UIActivityTypeAssignToContact,
UIActivityTypeSaveToCameraRoll,
UIActivityTypeAddToReadingList,
UIActivityTypePostToFlickr,
UIActivityTypePostToVimeo]
activityViewController.excludedActivityTypes = excludeActivities
// 4 展示
presentViewController(activityViewController, animated: true,
completion: nil)
}
注释第三步骤: UIActivityViewController
有很多的类型, 进入苹果官方文档进行选择;
Complete! 去赢得一个你崇拜的Ta吧。
你可以把Lenore的名字换成你自己的,把这首诗通过邮件发给自己, 睡眼朦胧的痴呆的在情人节晚上, 举一杯清酒望着明月, 然后假装收到优雅成熟英国女王给你发来的邮件、浪漫、温馨、舒适....
突然电话响了, 就那么一句程序猿顿时从梦中惊醒....
你猜会是啥?
资源下载地址:
1.GitHub地址----Tesseract-OCR-iOS ---- Framework
2. GitHub地址----更多语言包----tesseract-ocr, 请使用3.0.2或更高版本
3. 初始项目地址GitHub---OCR-Tutorial-start
4. 最终项目地址GitHub----OCR-Tutorial-end
由于某些小伙伴下载不了项目,个人就将之上传之GitHub,大家可以在OCR-Tutorial---start/end中【下载压缩包】。
如果觉得好,就东东小手点个赞O(∩_∩)O哈!
结束
Tesseract是很强大,但OCR的潜力是无限的。记住当你使用或提高OCR的功能时,作为传感,思维,如果你能破译字符使用你的眼睛或耳朵甚至指尖,那么字符识别中你是当之无愧的专家,已经完全有能力教学你的电脑去学习更多知识。
英文能力对不起看那么多美剧呀 !
更多精彩内容请关注“IT实战联盟”哦~~~
IT实战联盟.jpg
网友评论
地址: https://github.com/mugworts/iOS-Study
github上显示的issues:
On 7 Mar 2016 10:21, "Shivan Raptor" ***@***.***> wrote:
@DLlearn <https://github.com/DLlearn> Just use apt-get install
tesseract-ocr-chi-sim and other similar packages to install the trained
data. Do not use the latest version of trained data available in Github, as
it will cause the parse error above.
—
Reply to this email directly or view it on GitHub
<#215 (comment)>.
在 returnCode = _tesseract->Recognize(_monitor); 这一行显示EXC_BAD_ACCESS