OCR技術

"起初我寫這篇教程是在情人節,OCR能夠帶給你一全年的愛"。 你以前確定已經見過,OCR技術被應用於在平板電腦上將掃描文件處理成手寫字跡,還被應用於谷歌最近添加到他們的Translate app上的"Word Len"功能。如今你將學習在本身的iPhone app上使用OCR,很是酷,不是嗎? 那麼…OCR是什麼? 什麼是OCR 光學字符識別,或着說OCR,是指用電子的方式從圖片中取出文字而後重用於其餘領域,如文檔編輯、自由文本搜索,或文本比對。 本教程中,你將學習怎樣使用Tesseract,谷歌維護的一個開源OCR引擎。 Tesseract介紹 Tesseract十分強大,但有如下幾點侷限性: 不像其餘OCR引擎(例如美國郵政業用於分類郵件的),Tesseract不能識別手寫,並且只能識別一共大約64中字體的文本。 Tesseract須要一些處理來改善OCR結果,圖像須要被放縮,圖像有很是多的差別,另外還有水平排布的文字。 最後,Tesseract僅僅支持Liuux,Windows,Mac OS X。 wait_what.jpg 怎麼樣在iOS上使用它呢?幸運的是,有一套Tesseract OCR的Objective-C封裝,也能夠被用於swift和iOS上,適配swift版本的已經被包含在咱們的示例工程中了:] 咱們的app:一見傾心 你相信咱們Ray Wenderlich團隊不會讓你在這即將到來的情人節失望,對嗎?固然不會!咱們支持你。咱們成功找到一個有效的辦法來打動你的真愛的心,你將經過編寫一個app來實現這一點。 love-320x320.png U + OCR = LUV 本教程中,你將學習怎樣使用Tesseract--谷歌維護的一個開源OCR引擎。你將編寫"一見傾心"應用,能夠針對一張浪漫愛情詩的照片,將原詩人密切思念的名字替換爲你本身喜歡的人的名字,從而將這首詩變成"你本身的"。很是好,準備好去感動你的Ta吧。 開始 從這裏下載開始工程,解壓到方便的地方。 壓縮包中包含如下文件夾: LoveInASnap:本教程的Xcode起步工程 Tesseract Resources:Tesseract框架和語言數據 Image Resources:樣例圖片,包含着一會你將用到的文字。 看一下如今的LoveinASnap.xcodeproj,能夠注意到一個預先準備好的ViewController.swift和Main.storyboard界面,以及幾個鏈接到控制器上的@IBOutlet和空的@IBAction方法。 在這些空方法下,能夠看到兩個已經寫好的函數,用來處理展現和隱藏視圖的active indicator: 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 } 接下來還有一些方法來移動視圖,防止鍵盤擋住text fields: 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() }) } 最後,剩下的方法根據用戶的操做來觸發鍵盤操做、調用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() } 儘管app的用戶體驗很重要,但這些方法與本教程基本無關,所以實現爲你準備好了,爲了讓你馬上開始真正的有趣的編程。 可是在你寫第一行代碼以前,編譯並運行起步工程,處處點擊一下app,感覺一下UI。目前text view是不可編輯的,點擊text filed只會簡單地調出或關閉鍵盤,你的工做是補全完善這款app。 ocr-first-run-281x500.png 添加Tesseract框架 在你解壓好的壓縮包中有一個Tesseract Resources文件夾,包含着Tesseract框架和tessdata文件夾,裏面有英語和法語識別數據。 在Finder中打開這一文件夾,將Tesseract.framework拖到Xcode的項目導航欄中,添加該框架,注意選中Copy items if needed。 Create_Groups_screenshot-463x320.png 最後點擊Finish結束添加。 接下來你須要將tessdata文件夾做爲"引用文件夾(referenced folder)"添加以便維持整個文件結構。將tessdata文件夾從Finder中拖到項目導航欄中Supporting Files組中。 一樣,確保Copy items if needed選中,同時將Adding Folders選項設置爲Create folder references。 08.png Adding tessdata as a referenced folder 最後,點擊Finish將數據文件添加到工程中,能夠看到在項目導航欄中出現了一個藍色的tessdata文件夾,藍色說明這個文件夾是引用而不是Xcode中的group。 因爲Tesseract須要libstdc++.6.0.9.dylib 和 CoreImage.framework,你還須要把這些庫鏈接到項目中來。 選中LoveInASnap項目文件,選擇LoveInASnap目標,在General欄中找到Linked Frameworks and Libraries。 ocr-frameworks-700x319.png 如今這裏應該只有一個文件:TesseractOCR.framework,就是你剛剛添加的,點擊列表下面的+按鈕,找到libstdc++.dylib和CoreImage.framework,把它們添加到你的工程中。 ocr-addlibs.png 接下來在頂部菜單欄的Build Phases旁邊,點擊Build Settings,經過列表頂部的搜索欄能夠方便地找到Other Linker Flags,在Other Linker Flags的全部已有的key後面添加-lstdc++,而後依舊是在Build Settings中,找到C++ Standard Library並選擇"Compiler Default"。 差很少了,只剩最後一步了… happy-epic-win.png Wipe away those happy tears, Champ! Almost there! One step to go… 最後,由於Tesseract是一個Objective-C庫,你須要建立"Objective-C橋接頭文件"(Objective-C bridging header)來在你的swift app中使用該庫。 建立橋接頭文件而且使其符合全部項目配置的最簡單的方法是把任意Objective-C文件添加到你的工程中。 選擇File\New\File…,選中iOS\Source\Cocoa Touch Class而後點擊Next,鍵入FakeObjectiveCClass做爲類名,選擇NSObject做爲其超類,固然,確保語言選擇爲Objective-C!點擊Next,而後點擊Create。 當提示Would you like to configure an Objective-C bridging header時選擇YES。 你已經成功建立了Objective-C橋接頭文件,如今你能夠從工程中刪除FakeObjectiveCClass.m和FakeObjectiveCClass.h了,由於你只須要橋接頭文件。:] TrashObjC.jpg Toss out those Objective-c classes! 接下來把Tesseract庫導入你新建的橋接頭文件中,在項目導航欄中找到LoveInASnap-Bridging-Header.h,打開並添加下面這一行: 15.png 如今你能夠經過你的工程訪問Tesseract庫了,編譯並運行你的項目,確保一切都能正常經過編譯。 一切正常嗎?那麼如今你能夠開始作有趣的事了! 加載圖像 固然,在你的OCR app中首先須要一個能從程序中加載一張圖片的機制,最簡單的方式是使用UIImagePickerController實例從相機或圖片庫中選擇一張圖片。 打開ViewController.swift,找到takePhoto(),將該方法的實現替換爲下面的代碼: // 1 view.endEditing(true) moveViewDown() // 2 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) 這段代碼給用戶展示兩個或三個選項,這取決於他們設備的能力,下面是這段代碼的具體講解: 一、若是你當前正在編輯textView或者textField,那麼關閉鍵盤並將視圖移動到原來的位置。 二、使用actionSheet樣式建立一個UIAlertController來爲用戶提供一組選項。 三、若是用戶的設備有相機,那麼在imagePickerActionSheet上添加Take Photo按鈕,選中該按鈕時將建立並展現一個UIImagePickerController實例,而且類型爲sourceType .Camera。 四、爲imagePickerActionSheet添加Choose Existing按鈕,選中該按鈕時將建立並展現一個UIImagePickerController實例,而且類型爲sourceType .PhotoLibrary。 五、爲imagePickerActionSheet添加Cancel按鈕,設置其樣式爲.Cancel後,即便你不指定任何actions,選中該按鈕依舊會關閉UIImagePickerController(譯者注:我的感受這裏做者的意思是想說關閉UIAlertController)。 六、最後,展現UIAlertController實例。 編譯並運行你的app,點擊Snap/Upload a picture of your Poem按鈕,你將看到你新添加的UIAlertController,以下圖: ocr-action-sheet-281x500.png 若是你運行在虛擬機上,那麼沒有能夠用的物理相機,因此你不會看到Take Photo選項。 正如以前講Tesseract的侷限時提到的那樣,爲優化OCR結果,圖片必須有必定的大小限制。若是一張圖片太大或者過小,Tesseract可能返回一個錯誤的結果,甚至直接使整個程序崩掉並拋出EXC_BAD_ACCESS錯誤。 所以,你須要一個方法來從新設定圖片大小,固然了不能改變圖片的總體比例,這是爲了使圖片儘量不失真。 保持比例放縮圖片 圖像的總體比例(aspect ratio)是指它的寬高比,從數學定義上來講,爲了減少原圖像的大小而不改變總體比例,你必須維持寬高比爲一常數。 Aspect_Ratio-369x320.png 當你知道原圖像的寬和高,以及最終圖像的目標寬度或者高度時,你能夠像下面同樣從新組織總體比例的等式: 12.jpg 這樣就有了兩個公式:Height1/Width1 * width2 = height2和Width1/Height1 * height2 = width2。你將在你的縮放方法中使用這兩個公式來維持圖片的總體比例。 依然是在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參數的值,接下來基於原總體比例適當地放縮另外一邊,重繪原圖像到新計算的frame中,最後將新建立的放縮好了的圖片返回給調用者。 呼~ 既然咱們已經獲得了咱們須要的一切(此處應有掌聲…),接下來能夠開始實現Tesseract部分了。 實現Tesseract OCR 在ViewController.swift的底部找到UIImagePickerControllerDelegate擴展,在擴展中添加下面的方法: func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) { let selectedPhoto = info[UIImagePickerControllerOriginalImage] as! UIImage let scaledImage = scaleImage(selectedPhoto, maxDimension: 640) addActivityIndicator() dismissViewControllerAnimated(true, completion: { self.performImageRecognition(scaledImage) }) } imagePickerController(_:didFinishPickingMediaWithInfo:) 是 UIImagePickerDelegate的一個代理方法,在info字典對象中返回選中圖片的信息。你能夠從info字典中使用UIImagePickerControllerOriginalImage鍵來獲取選中圖片,而後使用scaleImage(_:maxDimension:)來放縮圖片。 經過調用addActivityIndicator()來在Tesseract工做時打斷用戶交互,向用戶展現活動指示器。接下來關閉UIImagePicker視圖控制器,將圖片傳給performImageRecognition()(稍後你將實現這一方法)來處理。 下面,在類的主聲明中添加下面方法: func performImageRecognition(image: UIImage) { // 1 let tesseract = G8Tesseract() // 2 tesseract.language = "eng+fra" // 3 tesseract.engineMode = .TesseractCubeCombined // 4 tesseract.pageSegmentationMode = .Auto // 5 tesseract.maximumRecognitionTime = 60.0 // 6 tesseract.image = image.g8_blackAndWhite() tesseract.recognize() // 7 textView.text = tesseract.recognizedText textView.editable = true // 8 removeActivityIndicator() } 這裏就是OCR變魔法的地方!由於這是本教程中的核心部分,接下來是每一部分的詳細講解: French1.png Your poem vil impress vith French! Ze language ov love! *Haugh* *Haugh* *Haugh* 一、初始化tesseract爲一個新的G8Tesseract對象。 二、Tesseract將從.traineddata文件中尋找你在該參數中指定的語言,指定爲eng和fra將從"eng.traineddata" 和 "fra.traineddata"包含的數據中分別檢測英文和法文,法語轉換數據(trained data)已經被包含到該工程中了,由於本教程中你將使用的示例詩詞中包含一部分法語(Très romantique!),法語中的重讀符號不在英語字母集中,所以爲了能展現出這些重讀符號,你須要鏈接法語的.traineddata文件。將法語數據包含進來也是很好的,由於.traineddata中有一部分涉及到了語言詞彙。 三、你能夠指定三種不一樣的OCR工做模式:.TesseractOnly是最快但最不精確的方法;.CubeOnly要慢一些,但更精確,由於它使用了更多的人工智能;.TesseractCubeCombined同時使用.TesseractOnly和.CubeOnly來提供最精確的結果,不過這也致使了它成爲三種工做方式中最慢的一種。 四、Tesseract假定處理的文字是均勻的一段文字,可是你的樣例詩中分了多段。Tesseract的pageSegmentationMode可讓它知道文字是怎麼樣被劃分的。因此這裏設置pageSegmentationMode爲.Auto來支持自動頁劃分(automatic page segmentation),這樣Tesseract就有能力識別段落分割了。 五、這裏你經過設定maximumRecognitionTime來限制Tesseract識別圖片的時間爲一有限的時間。不過這樣設定之後,只有Tesseract引擎被限制了,若是你正在使用.CubeOnly 或 .TesseractCubeCombined工做模式,那麼即便Tesseract已經達到了maximumRecognitionTime指定的時間,立體引擎(Cube engine)依然會繼續處理。 六、若是文字和背景相差很大,那麼你將獲得Tesseract處理的最好結果。Tesseract有一個內置的濾鏡,g8_blackAndWhite(),下降圖片顏色的飽和度,增長對比度,減小亮度。這裏你在Tesseract圖像識別過程開始以前,將濾鏡處理後的圖像賦值給Tesseract對象的image屬性。 七、須要注意的是,圖像識別是一個同步的過程,因此此時識別後的文本已是可用的了。你將識別出來的文本填充到textView中,同時設置textView爲可編輯,這樣你的用戶就能夠照他(她)喜歡來編輯了。 八、最後,移除活動指示器來代表OCR已經完成識別圖像的過程,可讓用戶編輯他們的詩詞了。 如今是時候檢測一下你寫的這段代碼,看看會發生些什麼了。 處理你的第一張圖像 本教程的示例圖像,能夠從Image Resources\Lenore.png中找到。找到下面這一張圖片: QQ截圖20150708105208.png Lenore.png圖片包含一首寫給"Lenore"的情詩--不過稍做修改後你能夠獲得一首足以引發你喜歡的人的注意的詩了!:] 儘管你能夠手動打出這張圖片的一份拷貝,而後使用app來截一張圖後執行OCR,不過何不簡化一下呢?把圖片添加到設備的相冊中,以減小其中潛在的人爲錯誤、光照變換、文本扭曲、打印瑕疵等問題。若是你使用模擬器,僅僅把圖片文件拖到模擬器上便可。 編譯並運行你的app,選擇Snap/Upload a picture of your Poem 而後選擇Choose Existing從圖片庫中選取示例圖片並開始Tesseract解析,首次運行時你須要容許app訪問圖片庫,你將看到在你選擇完圖片以後活動指示器開始旋轉。 而後…成了!最終破譯後的文本出如今textView中--看上去Tesseract的OCR成功了。 QQ截圖20150708105233.png 可是若是你的愛人不叫Lenore,他或她可能不會很感謝你的這首詩,而且他們可能想要知道這個Lenore是誰!:] 考慮的Lenore頻繁地出如今掃描的文本中,把這首詩自定成你愛人喜歡地那樣可能要花上一番功夫… 你會問,那怎麼辦?固然了,你能夠實現一個節省時間的方法來找到並替換這些單詞!很是棒的主意!下一部分將向你展現怎麼樣作到這一點。 找到並替換文本 既然OCR引擎已經把圖像轉換爲textView中的文本,你能夠將其當作其餘普通字符串同樣對待。 打開ViewController.swift,能夠看到已經爲你準備好了一個swapText()方法,這個方法被鏈接到Swap按鈕上,很是方便。:] 將swapText()的實現替換爲下面的代碼: @IBAction func swapText(sender: AnyObject) { // 1 if textView.text.isEmpty { return } // 2 textView.text = textView.text.stringByReplacingOccurrencesOfString(findTextField.text, withString: replaceTextField.text, options: nil, range: nil) // 3 findTextField.text = nil replaceTextField.text = nil // 4 view.endEditing(true) moveViewDown() } 上面的代碼很是直接,接下來花一點時間一步一步地看一遍代碼: 一、若是textView爲空,說明沒有須要替換的文本,那麼簡單地跳出該方法便可。 二、不然,在textView中找到你在findTextField中鍵入的字符串,將它們替換爲你在replaceTextField中輸入的字符串。 三、接下來, 一旦替換完成,清空findTextField 和 replaceTextField裏的值。 四、最後,關閉鍵盤,將視圖移回到原來的位置,就像以前在takePhoto()中同樣,確保當鍵盤關閉後視圖被正確放置。 注意:點擊背景也會關閉鍵盤並將視圖移動到原來的位置,這是藉由在界面上其餘元素之下的一個UIButton實現的,該按鈕會觸發ViewController.swift中的backgroundTapped()方法。 編譯並運行app,再次選中示例圖片,讓Tesseract工做。一旦文本出現後,在Find this…輸入框中輸入Lenore(注意待替換文本是大小寫敏感的),而後在Replace with…輸入框中輸入你愛人的名字,點擊Swap按鈕完成替換。 13.png 說變就變--你已經創造了爲你的甜心量身定作、私人訂製的一首情詩。 隨便試着替換一下其餘須要替換的單詞和名字,完成以後--呃,完成以後接下來作些什麼呢?如此一首有創造性的、勇敢的、充滿藝術氣息的詩不該該孤零零地在你的設備上,你須要一種方式來與世界分享你的傑做。 分享最終做品 在最後的這一部分,你將建立一個UIActivityViewController來容許你的用戶分享他們的新做品。 在ViewController.swift中,將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) } 依次看一下上面的數字註釋: 一、若是textView是空的,那就不分享任何東西。 二、不然,建立一個UIActivityViewController的實例,將textView中的文本放在數組中做爲要被分享的項目。 三、UIActivityViewController有一長串內置的活動類型,你能夠不包含UIActivityTypeAssignToContact, UIActivityTypeSaveToCameraRoll, UIActivityTypeAddToReadingList, UIActivityTypePostToFlickr, 和 UIActivityTypePostToVimeo,由於這裏它們沒有什麼意義。 四、最後,展現你的UIActivityViewController來讓用戶分享他們的做品。 再次編譯並運行app,通Tesseract識別圖片,若是你想,你能夠再次執行查找和替換過程。當你對文本滿意以後,點擊share按鈕。 14.png 好了,你的"一見傾心"app已經完成了--確定能夠贏得你愛慕的人的心。 若是你像我同樣,你可能會把Lenore的名字替換爲本身的名字,用其餘帳號把這首詩發到本身的郵件箱中,獨自一人度過情人節的夜晚。吃一碗拌飯,喝一杯紅酒,視線逐漸模糊,而後僞裝這封信是來自英國的女王,來邀請你去豪華的,充滿浪漫、溫馨、神祕和驚喜的夜晚。不過可能也只有我了… 何去何從 你能夠從這裏下載工程的最終版本。 你能夠在github上找到Tesseract的iOS版的壓縮包,地址是https://github.com/gali8/Tesseract-OCR-iOS,你也能夠從谷歌的Tesseract OCR網站上下載更多的語言包,使用版本3.02及以上來保證與當前庫的兼容性。 使用其餘的詩、歌以及文本片斷來試一下;試着使用你的照相機照幾張相;或者使用你的圖片庫中的圖片。你將看到Tesseract在不一樣的圖片中擁有怎樣不一樣的表現。 QualityIssues-700x212.png Examples of potentially problematic image inputs that can be corrected for improved results. Source: Google’s Tesseract OCR site 記住:"錯誤的輸入必然致使錯誤的輸出"。提升輸出質量的最簡單的方式是提升輸入的質量。正如谷歌在他們的Tesseract網站上列出的那樣,有不少方式能夠提升你的輸入質量:黑暗或者微弱的光照,圖片干擾,歪曲文本方向,厚厚的黑邊框都會致使低質量的結果。 你能夠查找圖片預處理的相關資料,或者實現本身的人工智能邏輯,例如神經網絡或者充分利用Tesseract的優化工具,來幫助你的程序糾正錯誤,提升成功概率。又或者,由於即便是很小的圖片亮度、顏色、對比度、曝光等的變化都會致使輸出的不一樣,你能夠將圖片通過多種濾鏡處理,而後對比結果來決定最精確的輸出。經過使用以上一個或幾個策略,你可能獲得最好的輸出結果,因此試一下這些方法看一下哪個對你的應用最好。 Tesseract很是強大,可是OCR的潛能是無限的,當你在你的軟件中使用Tesseract,提升Tesseract OCR的性能時,時刻思考你是否能經過你的眼睛、耳朵甚至指尖來破譯這些字符。你已是一個字符識別的專家了,徹底能夠教你的電腦更多它還不知道的東西。 ocr_expert-244x320.jpg 一樣,若是你對本教程Tesseract,或者說OCR 策略,有任何評論或疑問,請加入下方的討論! http://www.cocoachina.com/ios/20150708/12463.html
相關文章
相關標籤/搜索