4 Commits

Author SHA1 Message Date
Michael 3abed693d4 fix createTesseract 2016-10-16 14:53:20 +04:00
Michael 02b9c1b3b9 swift 3 to 2.3 reinit 2016-10-12 00:15:46 +03:00
Michael a80b08a3eb change tesseracts initializer for pod validating 2016-10-11 16:40:17 +03:00
Michael d398597796 simple pod installation 2016-10-11 15:43:13 +03:00
554 changed files with 2477 additions and 47522 deletions
+1 -1
View File
@@ -43,7 +43,7 @@ playground.xcworkspace
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
Pods/
# Carthage
#
Regular → Executable
View File
Regular → Executable
View File
Regular → Executable
+1 -3
View File
@@ -8,7 +8,7 @@
Pod::Spec.new do |s|
s.name = 'DocumentsOCR'
s.version = '1.0.2'
s.version = '1.0.4'
s.summary = 'A Swift framework for machine readable documents recognition'
# This description is used to generate tags and improve search results.
@@ -42,6 +42,4 @@ Pod::Spec.new do |s|
s.dependency 'TesseractOCRiOS', '~> 4.0.0'
s.dependency 'PodAsset'
s.dependency 'GPUImage'
s.pod_target_xcconfig = { 'ENABLE_BITCODE' => 'NO' }
end
Regular → Executable
View File
Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Regular → Executable
View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File
View File
Regular → Executable
View File
+8 -8
View File
@@ -13,7 +13,7 @@ protocol CameraViewDelegate {
func startTakingPictures()
}
open class CameraOverlayView: UIView {
public class CameraOverlayView: UIView {
@IBOutlet weak var codeBorder: UIView!
@IBOutlet weak var takePhotoButton: UIButton!
@@ -24,14 +24,14 @@ open class CameraOverlayView: UIView {
var delegate: CameraViewDelegate!
@IBAction func cancelButtonClicked(_ sender: UIButton) {
@IBAction func cancelButtonClicked(sender: UIButton) {
resetViews()
delegate.stopTakingPictures()
}
@IBAction func scanButtonClicked(_ sender: UIButton) {
takePhotoButton.isHidden = true
progressViewContainer.isHidden = false
@IBAction func scanButtonClicked(sender: UIButton) {
takePhotoButton.hidden = true
progressViewContainer.hidden = false
delegate.startTakingPictures()
}
@@ -41,13 +41,13 @@ open class CameraOverlayView: UIView {
required public init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.isOpaque = false
self.opaque = false
}
func resetViews() {
takePhotoButton.isHidden = false
takePhotoButton.hidden = false
progressLabel.text = ""
progressView.progress = 0
progressViewContainer.isHidden = true
progressViewContainer.hidden = true
}
}
View File
+10 -10
View File
@@ -8,25 +8,25 @@
import Foundation
let simpleFormatter: DateFormatter = {
let formatter = DateFormatter()
let simpleFormatter: NSDateFormatter = {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)
return formatter
}()
extension Date {
static func dateFromPassportDateCode(_ code: String) -> Date? {
extension NSDate {
static func dateFromPassportDateCode(_ code: String) -> NSDate? {
let year = code.substring(from: 0, to: 3)
let month = code.substring(from: 4, to: 5)
let day = code.substring(from: 6, to: 7)
let year = code.substring(0, to: 3)
let month = code.substring(4, to: 5)
let day = code.substring(6, to: 7)
return simpleFormatter.date(from: "\(year)-\(month)-\(day)")
return simpleFormatter.dateFromString("\(year)-\(month)-\(day)")
}
var stringDate: String {
return simpleFormatter.string(from: self)
return simpleFormatter.stringFromDate(self)
}
}
+2 -2
View File
@@ -10,8 +10,8 @@ import Foundation
extension NSTextCheckingResult {
func group(atIndex index: Int, fromSource source: String) -> String {
let range = self.rangeAt(index)
return (source as NSString).substring(with: range).trimmingCharacters(in: CharacterSet(charactersIn: "<"))
let range = self.rangeAtIndex(index)
return (source as NSString).substringWithRange(range).stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: "<"))
}
}
+58 -21
View File
@@ -3,32 +3,51 @@ import Foundation
extension String {
subscript (i: Int) -> Character {
let index = self.index(self.startIndex, offsetBy: i)
let index = self.startIndex.advancedBy(i)
return self.characters[index]
}
func substring(from: Int, to: Int) -> String {
let fromIndex = self.index(self.startIndex, offsetBy: from)
let toIndex = self.index(self.startIndex, offsetBy: to + 1)
return self.substring(with: fromIndex ..< toIndex)
let fromIndex = self.startIndex.advancedBy(from)
let toIndex = self.startIndex.advancedBy(to + 1)
return self.substringWithRange(fromIndex ..< toIndex)
}
func replaceNumbers() -> String {
var result = ""
for char in self.characters {
switch char {
case "0": result.append("O")
case "1": result.append("I")
case "2": result.append("S")
case "3": result.append("S")
case "4": result.append("A")
case "5": result.append("S")
case "6": result.append("G")
case "7": result.append("Z")
case "0":
result.appendContentsOf("O")
break;
case "1":
result.appendContentsOf("I")
break;
case "2":
result.appendContentsOf("S")
break;
case "3":
result.appendContentsOf("S")
break;
case "4":
result.appendContentsOf("A")
break;
case "5":
result.appendContentsOf("S")
break;
case "6":
result.appendContentsOf("G")
break;
case "7":
result.appendContentsOf("Z")
break;
case "8",
"9": result.append("B")
"9":
result.appendContentsOf("B")
break;
default: result.append(char)
default:
result.append(char)
}
}
@@ -40,16 +59,34 @@ extension String {
for char in self.characters {
switch char {
case "O",
"D": result.append("0")
"D":
result.appendContentsOf("0")
break;
case "I",
"L": result.append("1")
"L":
result.appendContentsOf("1")
break;
case "S": result.append("5")
case "A": result.append("4")
case "G": result.append("6")
case "Z": result.append("7")
case "B": result.append("8")
case "S":
result.appendContentsOf("5")
break;
case "A":
result.appendContentsOf("4")
break;
case "G":
result.appendContentsOf("6")
break;
case "Z":
result.appendContentsOf("7")
break;
case "B":
result.appendContentsOf("8")
break;
default: result.append(char)
}
+6 -8
View File
@@ -12,20 +12,18 @@ import GPUImage
extension UIImage {
func croppedImageWithSize(_ rect: CGRect) -> UIImage {
let imageRef = CGImageCreateWithImageInRect(self.CGImage!, rect)
let imageRef: CGImage! = self.cgImage!.cropping(to: rect)
let croppedImage: UIImage = UIImage(cgImage: imageRef, scale: self.scale, orientation: self.imageOrientation)
let croppedImage: UIImage = UIImage(CGImage: imageRef!, scale: self.scale, orientation: self.imageOrientation)
let selectedFilter = GPUImageTransformFilter()
selectedFilter.setInputRotation(kGPUImageNoRotation, at: 0)
let image: UIImage = selectedFilter.image(byFilteringImage: croppedImage)
selectedFilter.setInputRotation(kGPUImageNoRotation, atIndex: 0)
let image = selectedFilter.imageByFilteringImage(croppedImage)
return image
}
func save(_ path: String) {
let png = UIImagePNGRepresentation(self)
try? png?.write(to: URL(fileURLWithPath: path), options: [.atomic])
var recognitionImage: UIImage {
return UIImage(CGImage: self.CGImage!, scale: self.scale, orientation: UIImageOrientation.Left)
}
}
+1 -1
View File
@@ -29,7 +29,7 @@ public struct DOConstants {
var result = ""
for i: UInt32 in (0 ..< 26) {
result.append(Character(UnicodeScalar(aCode + i)!))
result.append(Character(UnicodeScalar(aCode + i)))
}
return result
+33 -32
View File
@@ -10,90 +10,91 @@ import Foundation
import TesseractOCR
import PodAsset
open class Utils {
public class Utils {
fileprivate static let bundle = PodAsset.bundle(forPod: "DocumentsOCR")!
private static let bundle = PodAsset.bundleForPod("DocumentsOCR")!
static let passportPattern: String! = Utils.stringFromTxtFile("passportPattern", inBundle: bundle)
fileprivate static let tesseract = createTesseract()
private static let tesseract = Utils.createTesseract()
open static func stringFromTxtFile(_ fileName: String, inBundle bundle: Bundle = Bundle.main) -> String? {
let filePath = bundle.path(forResource: fileName, ofType: "txt")
let contentData = FileManager.default.contents(atPath: filePath!)
return NSString(data: contentData!, encoding: String.Encoding.utf8.rawValue) as? String
public static func stringFromTxtFile(_ fileName: String, inBundle bundle: NSBundle = NSBundle.mainBundle()) -> String? {
let filePath = bundle.pathForResource(fileName, ofType: "txt")
let contentData = NSFileManager.defaultManager().contentsAtPath(filePath!)
return String(data: contentData!, encoding: NSUTF8StringEncoding)
}
static func mrCodeFrom(image: UIImage, tesseractDelegate: G8TesseractDelegate? = nil) -> String? {
let path = bundle.path(forResource: "eng", ofType: "traineddata")
tesseract.delegate = tesseractDelegate!
tesseract.image = image
tesseract.image = image.recognitionImage
tesseract.recognize()
if let recognizedText = tesseract.recognizedText {
NSLog("Recognized: \(recognizedText)")
let text = recognizedText.replacingOccurrences(of: " ", with: "")
let text = recognizedText.stringByReplacingOccurrencesOfString(" ", withString: "")
let regex = try? NSRegularExpression(pattern: passportPattern, options: [])
let range = NSRange(location: 0, length: text.characters.count)
if let result = regex!.firstMatch(in: text, options: [], range: range) {
if let result = regex!.firstMatchInString(text, options: [], range: range) {
let code = (text as NSString).substring(with: result.range)
let code = (text as NSString).substringWithRange(result.range)
return fixFirstRowIn(code: code)
return fixFirstRowIn(code)
}
}
return nil
}
fileprivate static func fixFirstRowIn(code: String) -> String {
private static func fixFirstRowIn(code: String) -> String {
let pattern = "(?<FirstLine>(?<Passport>[A-Z0-9])(?<PassportType>.)(?<IssuingCountry>[A-Z0-9]{3})(?<PassportOwner>(?<Surname>[A-Z0-9]+)<<(?<GivenName>(?:[A-Z0-9]+<)+)){1})"
let regex = try? NSRegularExpression(pattern: pattern, options: [])
let range = NSRange(location: 0, length: code.characters.count)
let result = regex!.matches(in: code, options: [], range: range)
var resultFirstRow = (code as NSString).substring(with: result[0].range)
let result = regex!.matchesInString(code, options: [], range: range)
var resultFirstRow = (code as NSString).substringWithRange(result[0].range)
while resultFirstRow.characters.count != 44 {
resultFirstRow.append("<")
resultFirstRow.appendContentsOf("<")
}
let secondRow = code.characters.split(separator: "\n", maxSplits: 2, omittingEmptySubsequences: true)[1]
let secondRow = code.characters.split("\n", maxSplit: 2, allowEmptySlices: true)[1]
return "\(resultFirstRow)\n\(String(secondRow))\n"
}
fileprivate static func createTesseract() -> G8Tesseract {
let trainDataPath = bundle.path(forResource: "eng", ofType: "traineddata")
private static func createTesseract() -> G8Tesseract {
let trainDataPath = bundle.pathForResource("eng", ofType: "traineddata")
let cacheURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
let cacheURL = NSFileManager.defaultManager().URLsForDirectory(.CachesDirectory, inDomains: .UserDomainMask).first!
let tessdataURL = cacheURL.appendingPathComponent("tesseract", isDirectory: true).appendingPathComponent("tessdata", isDirectory: true)
let destinationURL = tessdataURL.appendingPathComponent("eng.traineddata")
let tessdataURL = cacheURL.URLByAppendingPathComponent("tessetact", isDirectory: true)?.URLByAppendingPathComponent("tessdata", isDirectory: true)
if !FileManager.default.fileExists(atPath: destinationURL.path) {
createTessdataFrom(trainDataPath!, toDirectoryURL: tessdataURL, withDestinationURL: destinationURL)
let destinationURL = tessdataURL!.URLByAppendingPathComponent("eng.traineddata")
if !NSFileManager.defaultManager().fileExistsAtPath(destinationURL!.path!) {
createTessdataFrom(trainDataPath!, toDirectoryURL: tessdataURL!, withDestinationURL: destinationURL!)
}
let tesseract = G8Tesseract(language: "eng", configDictionary: [:], configFileNames: [], absoluteDataPath: tessdataURL.path, engineMode: .tesseractOnly, copyFilesFromResources: false)
let tesseract = G8Tesseract(language: "eng", configDictionary: [:], configFileNames: [], absoluteDataPath: tessdataURL!.path, engineMode: .TesseractOnly)
var whiteList = DOConstants.alphabet.uppercased()
whiteList.append("<>1234567890")
var whiteList = DOConstants.alphabet.uppercaseString
whiteList.appendContentsOf("<>1234567890")
tesseract?.charWhitelist = whiteList
tesseract?.setVariableValue("FALSE", forKey: "x_ht_quality_check")
return tesseract!
}
fileprivate static func createTessdataFrom(_ filePath: String, toDirectoryURL tessdataURL: URL, withDestinationURL destinationURL: URL) {
private static func createTessdataFrom(_ filePath: String, toDirectoryURL tessdataURL: NSURL, withDestinationURL destinationURL: NSURL) {
do {
let fileManager = FileManager.default
try fileManager.createDirectory(atPath: tessdataURL.path,
let fileManager = NSFileManager.defaultManager()
try fileManager.createDirectoryAtPath(tessdataURL.path!,
withIntermediateDirectories: true, attributes: nil)
try fileManager.copyItem(atPath: filePath, toPath: destinationURL.path)
try fileManager.copyItemAtPath(filePath, toPath: destinationURL.path!)
}
catch let error as NSError {
assertionFailure("There is no tessdata directory in cache (TesseractOCR traineddata). \(error.localizedDescription)")
+9 -8
View File
@@ -35,13 +35,13 @@ public struct DocumentInfo {
public let nationalityCode: String
/// Date of birth
public let dateOfBirth: Date?
public let dateOfBirth: NSDate?
/// Gender
public let gender: Gender
/// Expiration date of passport
public let expirationDate: Date?
public let expirationDate: NSDate?
/// Personal number (may be used by the issuing country as it desires)
public let personalNumber: String
@@ -56,18 +56,19 @@ public struct DocumentInfo {
let regex = try! NSRegularExpression(pattern: Utils.passportPattern, options: [])
let range = NSRange(location: 0, length: text.characters.count)
if let result = regex.firstMatch(in: text, options: [], range: range) {
if let result = regex.firstMatchInString(text, options: [], range: range) {
mrCode = (text as NSString).substring(with: result.range)
mrCode = (text as NSString).substringWithRange(result.range)
issuingCountryCode = result.group(atIndex: 4, fromSource: text).replaceNumbers()
lastname = result.group(atIndex: 6, fromSource: text).replaceNumbers()
name = result.group(atIndex: 7, fromSource: text).replacingOccurrences(of: "<", with: " ").replaceNumbers()
name = result.group(atIndex: 7, fromSource: text).stringByReplacingOccurrencesOfString("<", withString: " ").replaceNumbers()
passportNumber = result.group(atIndex: 9, fromSource: text)
nationalityCode = result.group(atIndex: 11, fromSource: text).replaceNumbers()
let dayOfBirthCode = result.group(atIndex: 12, fromSource: text).replaceLetters()
dateOfBirth = Date.dateFromPassportDateCode("19" + dayOfBirthCode)
dateOfBirth = NSDate.dateFromPassportDateCode("19" + dayOfBirthCode)
let genderLetter = result.group(atIndex: 17, fromSource: text)
switch genderLetter {
@@ -80,7 +81,7 @@ public struct DocumentInfo {
}
let expiralDateCode = result.group(atIndex: 18, fromSource: text).replaceLetters()
expirationDate = Date.dateFromPassportDateCode("20" + expiralDateCode)
expirationDate = NSDate.dateFromPassportDateCode("20" + expiralDateCode)
personalNumber = result.group(atIndex: 23, fromSource: text)
@@ -98,7 +99,7 @@ public struct DocumentInfo {
}
init?(image: UIImage, tesseractDelegate: G8TesseractDelegate? = nil) {
if let mrCode = Utils.mrCodeFrom(image: image, tesseractDelegate: tesseractDelegate) {
if let mrCode = Utils.mrCodeFrom(image, tesseractDelegate: tesseractDelegate) {
NSLog("Recognized: \(mrCode)")
self.init(recognizedText: mrCode)
}
+35 -32
View File
@@ -23,7 +23,7 @@ public protocol DocumentScannerDelegate: G8TesseractDelegate {
/// - parameter scanner: The document scanner object informing the delegate of this event
/// - parameter progress: progress value from 0.0 to 1.0
func documentScanner(_ scanner: DocumentScanner, recognitionProgress progress: Double)
/// Tells the delegate that scanner finished to recognize machine readable code from camera image and translate it into DocumentInfo struct
///
/// - parameter scanner: The document scanner object informing the delegate of this event
@@ -38,33 +38,33 @@ public protocol DocumentScannerDelegate: G8TesseractDelegate {
func documentScanner(_ scanner: DocumentScanner, didFailWithError error: NSError)
}
open class DocumentScanner: NSObject {
public class DocumentScanner: NSObject {
var imagePicker = UIImagePickerController()
/// View controller, which will present camera image picker for document machine readable code
open var containerViewController: UIViewController!
public var containerViewController: UIViewController!
/// The object that acts as the delegate of the document scanner
open var delegate: DocumentScannerDelegate!
public var delegate: DocumentScannerDelegate!
/// Number of photos to recognize
open var photosCount: UInt8 = 5
public var photosCount: UInt8 = 5
/// Time interval between taking photos
open var takePhotoInterval = 0.2
public var takePhotoInterval = 0.2
/// Recognized document information from RecognitionOperation
open var recognizedDocumentInfo: DocumentInfo? = nil
var timer: Timer!
public var recognizedDocumentInfo: DocumentInfo? = nil
var timer: NSTimer!
var codes = [String]()
var images = [UIImage]()
var recognizedInfo: DocumentInfo? = nil
fileprivate let queue: OperationQueue = {
let queue = OperationQueue()
queue.qualityOfService = .userInitiated
let queue: NSOperationQueue = {
let queue = NSOperationQueue()
queue.qualityOfService = .UserInitiated
queue.name = "ScannerOperationQueue"
return queue
}()
@@ -85,12 +85,13 @@ open class DocumentScanner: NSObject {
/// Present view controller with camera and border for document machine readable code
open func presentCameraViewController() {
if UIImagePickerController.availableCaptureModes(for: .rear) != nil {
public func presentCameraViewController() {
if UIImagePickerController.availableCaptureModesForCameraDevice(.Rear) != nil {
imagePicker = UIImagePickerController()
imagePicker.allowsEditing = false
imagePicker.sourceType = .camera
imagePicker.cameraCaptureMode = .photo
imagePicker.sourceType = .Camera
imagePicker.cameraCaptureMode = .Photo
imagePicker.showsCameraControls = false
imagePicker.delegate = self
@@ -101,11 +102,11 @@ open class DocumentScanner: NSObject {
let frame = CGRect(x: 0, y: 0, width: width, height: height)
overlayView.frame = frame
imagePicker.modalPresentationStyle = .fullScreen
containerViewController.present(imagePicker, animated: true, completion: {
imagePicker.modalPresentationStyle = .FullScreen
containerViewController.presentViewController(imagePicker, animated: true, completion: {
overlayView.codeBorder.layer.borderWidth = 5.0
overlayView.codeBorder.layer.borderColor = UIColor.red.cgColor
//overlayView.codeBorder.layer.borderWidth = 5.0
//overlayView.codeBorder.layer.borderColor = UIColor.redColor().CGColor
overlayView.delegate = self
overlayView.resetViews()
@@ -121,12 +122,12 @@ open class DocumentScanner: NSObject {
}
}
open func cancelRecognizeOperation() {
public func cancelRecognizeOperation() {
queue.cancelAllOperations()
}
fileprivate var cameraOverlayView: CameraOverlayView = {
let bundle = PodAsset.bundle(forPod: "DocumentsOCR")
private var cameraOverlayView: CameraOverlayView = {
let bundle = PodAsset.bundleForPod("DocumentsOCR")
let cameraVC = CameraOverlayViewController(nibName: NibNames.cameraOverlayViewController, bundle: bundle!)
let overlayView = cameraVC.view as! CameraOverlayView
@@ -137,25 +138,27 @@ open class DocumentScanner: NSObject {
extension DocumentScanner: CameraViewDelegate {
func stopTakingPictures() {
timer.invalidate()
containerViewController.dismiss(animated: true, completion: nil)
if timer != nil {
timer.invalidate()
}
containerViewController.dismissViewControllerAnimated(true, completion: nil)
}
func startTakingPictures() {
codes = [String]()
images = [UIImage]()
timer = Timer.scheduledTimer(timeInterval: takePhotoInterval, target: self, selector: #selector(self.timerTick(sender:)), userInfo: nil, repeats: true)
timer = NSTimer.scheduledTimerWithTimeInterval(takePhotoInterval, target: self, selector: #selector(DocumentScanner.timerTick(_:)), userInfo: nil, repeats: true)
}
func timerTick(sender: Timer) {
func timerTick(sender: NSTimer) {
imagePicker.takePicture()
}
}
extension DocumentScanner: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
public func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let editedImage = info[UIImagePickerControllerEditedImage] as? UIImage
let originalImage = info[UIImagePickerControllerOriginalImage] as! UIImage
@@ -179,7 +182,7 @@ extension DocumentScanner: UIImagePickerControllerDelegate, UINavigationControll
let recognizeOperation = RecognizeOperation(scanner: self)
recognizeOperation.completionBlock = {
DispatchQueue.main.async {
dispatch_async(dispatch_get_main_queue()) {
if let info = self.recognizedInfo {
self.delegate.documentScanner(self, didFinishScanningWithInfo: info)
}
@@ -195,8 +198,8 @@ extension DocumentScanner: UIImagePickerControllerDelegate, UINavigationControll
queue.addOperation(recognizeOperation)
}
}
fileprivate func cropImage(_ image: UIImage) -> UIImage {
func cropImage(image: UIImage) -> UIImage {
let viewControllerSize = containerViewController.view.frame.size
let vcWidth = viewControllerSize.width
+9 -9
View File
@@ -8,7 +8,7 @@
import Foundation
class RecognizeOperation: Operation {
class RecognizeOperation: NSOperation {
let scanner: DocumentScanner
var codes = [String]()
@@ -22,16 +22,16 @@ class RecognizeOperation: Operation {
for index in 0 ..< images.count {
let image = images[index]
if isCancelled {
if cancelled {
return
}
if let code = Utils.mrCodeFrom(image: image, tesseractDelegate: scanner.delegate) {
if let code = Utils.mrCodeFrom(image, tesseractDelegate: scanner.delegate) {
codes.append(code)
}
let progress = Double(index + 1) / Double(images.count)
DispatchQueue.main.async {
dispatch_async(dispatch_get_main_queue()) {
self.scanner.delegate.documentScanner(self.scanner, recognitionProgress: progress)
}
}
@@ -44,15 +44,15 @@ class RecognizeOperation: Operation {
let count = codes.first!.characters.count
for index in 0 ..< count {
if isCancelled {
if cancelled {
return
}
let winnerCharacter = chooseCharacterByVotesOn(index: index)
let winnerCharacter = chooseCharacterByVotesOn(index)
resultCode.append(winnerCharacter)
}
if isCancelled {
if cancelled {
return
}
@@ -61,7 +61,7 @@ class RecognizeOperation: Operation {
}
}
fileprivate func chooseCharacterByVotesOn(index: Int) -> Character {
private func chooseCharacterByVotesOn(index: Int) -> Character {
let characters = codes.map({ $0[index] })
var voting = [Character : Int]()
@@ -74,7 +74,7 @@ class RecognizeOperation: Operation {
}
}
let max = voting.values.max()!
let max = voting.values.maxElement()!
for (character, count) in voting {
if count == max {
return character
+4 -4
View File
@@ -541,7 +541,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 2.3;
};
name = Debug;
};
@@ -561,7 +561,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 2.3;
};
name = Release;
};
@@ -579,7 +579,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 2.3;
};
name = Debug;
};
@@ -593,7 +593,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
SWIFT_VERSION = 2.3;
};
name = Release;
};
View File
View File
+12 -5
View File
@@ -1,23 +1,30 @@
{
"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "F58CA8BF9748A2452CAED5EE1AD1443C7F6654F4+++35C8851",
"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "B3A2403691ADE80CF46872AA6E39C39881A146F4+++4805E2C",
"DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
},
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
"F58CA8BF9748A2452CAED5EE1AD1443C7F6654F4+++35C8851" : 9223372036854775807,
"F58CA8BF9748A2452CAED5EE1AD1443C7F6654F4+++39A176C" : 9223372036854775807,
"F58CA8BF9748A2452CAED5EE1AD1443C7F6654F4+++665569A" : 9223372036854775807
"F58CA8BF9748A2452CAED5EE1AD1443C7F6654F4+++665569A" : 9223372036854775807,
"B3A2403691ADE80CF46872AA6E39C39881A146F4+++4805E2C" : 9223372036854775807,
"F58CA8BF9748A2452CAED5EE1AD1443C7F6654F4+++35C8851" : 9223372036854775807
},
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "9E0F3260-D4E0-4DA4-8DD8-16B33507544C",
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
"F58CA8BF9748A2452CAED5EE1AD1443C7F6654F4+++35C8851" : "DocumentsOCR\/",
"F58CA8BF9748A2452CAED5EE1AD1443C7F6654F4+++39A176C" : "",
"F58CA8BF9748A2452CAED5EE1AD1443C7F6654F4+++665569A" : ""
"F58CA8BF9748A2452CAED5EE1AD1443C7F6654F4+++665569A" : "",
"B3A2403691ADE80CF46872AA6E39C39881A146F4+++4805E2C" : "documents-ocr-ios-master\/",
"F58CA8BF9748A2452CAED5EE1AD1443C7F6654F4+++35C8851" : "DocumentsOCR\/"
},
"DVTSourceControlWorkspaceBlueprintNameKey" : "DocumentsOCR",
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Example\/DocumentsOCR.xcworkspace",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/appintheair\/documents-ocr-ios",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "B3A2403691ADE80CF46872AA6E39C39881A146F4+++4805E2C"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/appintheair\/passport-ocr-ios",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+7 -9
View File
@@ -15,31 +15,29 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
func applicationDidFinishLaunching(application: UIApplication) {
}
func applicationWillResignActive(_ application: UIApplication) {
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
View File
View File
View File
View File

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

View File
View File

Before

Width:  |  Height:  |  Size: 566 KiB

After

Width:  |  Height:  |  Size: 566 KiB

View File
View File

Before

Width:  |  Height:  |  Size: 406 KiB

After

Width:  |  Height:  |  Size: 406 KiB

View File
View File

Before

Width:  |  Height:  |  Size: 572 KiB

After

Width:  |  Height:  |  Size: 572 KiB

View File
View File

Before

Width:  |  Height:  |  Size: 209 KiB

After

Width:  |  Height:  |  Size: 209 KiB

View File
View File
View File
Regular → Executable
View File
+10 -10
View File
@@ -8,25 +8,25 @@
import Foundation
let simpleFormatter: DateFormatter = {
let formatter = DateFormatter()
let simpleFormatter: NSDateFormatter = {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)
return formatter
}()
extension Date {
static func dateFromPassportDateCode(_ code: String) -> Date? {
extension NSDate {
static func dateFromPassportDateCode(code: String) -> NSDate? {
let year = code.substring(from: 0, to: 3)
let month = code.substring(from: 4, to: 5)
let day = code.substring(from: 6, to: 7)
let year = code.substring(0, to: 3)
let month = code.substring(4, to: 5)
let day = code.substring(6, to: 7)
return simpleFormatter.date(from: "\(year)-\(month)-\(day)")
return simpleFormatter.dateFromString("\(year)-\(month)-\(day)")
}
var stringDate: String {
return simpleFormatter.string(from: self)
return simpleFormatter.stringFromDate(self)
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ enum TextFieldType {
class PassportTextField: UITextField {
var editType: TextFieldType {
set {
editTypeValue = String(describing: newValue)
editTypeValue = String(newValue)
}
get {
switch editTypeValue {
+19 -19
View File
@@ -29,7 +29,7 @@ class PassportViewController: UITableViewController {
@IBOutlet weak var cameraImageView: UIImageView!
let countries = Utils.stringFromTxtFile("CountryCodes")!.components(separatedBy: "\n")
let countries = Utils.stringFromTxtFile("CountryCodes")!.componentsSeparatedByString("\n")
func dismissKeyboard() {
self.view.endEditing(true)
@@ -38,7 +38,7 @@ class PassportViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! as NSString
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first! as NSString
NSLog("\(paths)")
scanner = DocumentScanner(containerVC: self, withDelegate: self)
@@ -46,7 +46,7 @@ class PassportViewController: UITableViewController {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard))
self.view.addGestureRecognizer(tapGesture)
self.cameraImageView.contentMode = .scaleAspectFit
self.cameraImageView.contentMode = .ScaleAspectFit
// Set up country fields:
@@ -66,8 +66,8 @@ class PassportViewController: UITableViewController {
// Set up date fields:
let datePicker = UIDatePicker()
datePicker.addTarget(self, action: #selector(PassportViewController.datePickerValueChanged(_:)), for: .valueChanged)
datePicker.datePickerMode = UIDatePickerMode.date
datePicker.addTarget(self, action: #selector(PassportViewController.datePickerValueChanged(_:)), forControlEvents: .ValueChanged)
datePicker.datePickerMode = UIDatePickerMode.Date
dobField.delegate = self
expiredDateField.delegate = self
@@ -76,31 +76,31 @@ class PassportViewController: UITableViewController {
expiredDateField.inputView = datePicker
}
func datePickerValueChanged(_ sender: UIDatePicker) {
func datePickerValueChanged(sender: UIDatePicker) {
selectedTextField.text = sender.date.stringDate
}
@IBAction func cameraClicked(_ sender: UIBarButtonItem) {
@IBAction func cameraClicked(sender: UIBarButtonItem) {
scanner.presentCameraViewController()
}
override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
return .portrait
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return .Portrait
}
}
extension PassportViewController: DocumentScannerDelegate {
func documentScanner(_ scanner: DocumentScanner, willBeginScanningImages images: [UIImage]) {
func documentScanner(scanner: DocumentScanner, willBeginScanningImages images: [UIImage]) {
self.cameraImageView.image = images.first!
SVProgressHUD.showProgress(0, status: "Scanning")
}
func documentScanner(_ scanner: DocumentScanner, recognitionProgress progress: Double) {
func documentScanner(scanner: DocumentScanner, recognitionProgress progress: Double) {
SVProgressHUD.showProgress(Float(progress), status: "Scanning")
}
func documentScanner(_ scanner: DocumentScanner, didFinishScanningWithInfo info: DocumentInfo) {
func documentScanner(scanner: DocumentScanner, didFinishScanningWithInfo info: DocumentInfo) {
NSLog("Info: \(info)")
countryField.text = info.issuingCountryCode
@@ -128,7 +128,7 @@ extension PassportViewController: DocumentScannerDelegate {
SVProgressHUD.dismiss()
}
func documentScanner(_ scanner: DocumentScanner, didFailWithError error: NSError) {
func documentScanner(scanner: DocumentScanner, didFailWithError error: NSError) {
NSLog("Ошибка \(error.localizedDescription)")
SVProgressHUD.dismiss()
self.showErrorAlert(withMessage: error.localizedDescription)
@@ -137,7 +137,7 @@ extension PassportViewController: DocumentScannerDelegate {
extension PassportViewController: UITextFieldDelegate {
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
guard let passportField = textField as? PassportTextField else {
NSLog("Ошибка: неверный тип текстового поля")
return true
@@ -151,20 +151,20 @@ extension PassportViewController: UITextFieldDelegate {
extension PassportViewController: UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return countries.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return countries[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
selectedTextField?.text = countries[row].substring(from: 0, to: 2)
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
selectedTextField?.text = countries[row].substring(0, to: 2)
}
}
+4 -4
View File
@@ -3,13 +3,13 @@ import Foundation
extension String {
subscript (i: Int) -> Character {
let index = self.index(self.startIndex, offsetBy: i)
let index = self.startIndex.advancedBy(i)
return self.characters[index]
}
func substring(from: Int, to: Int) -> String {
let fromIndex = self.index(self.startIndex, offsetBy: from)
let toIndex = self.index(self.startIndex, offsetBy: to + 1)
return self.substring(with: fromIndex ..< toIndex)
let fromIndex = self.startIndex.advancedBy(from)
let toIndex = self.startIndex.advancedBy(to + 1)
return self.substringWithRange(fromIndex ..< toIndex)
}
}
+3 -3
View File
@@ -11,10 +11,10 @@ import UIKit
extension UIViewController {
func showErrorAlert(withMessage message: String) {
let alertVC = UIAlertController(title: "Ошибка", message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
let alertVC = UIAlertController(title: "Ошибка", message: message, preferredStyle: .Alert)
let okAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alertVC.addAction(okAction)
present(alertVC, animated: true, completion: nil)
presentViewController(alertVC, animated: true, completion: nil)
}
}
Regular → Executable
+3
View File
@@ -1,6 +1,9 @@
use_frameworks!
target 'DocumentsOCR_Example' do
pod 'TesseractOCRiOS', :git => 'https://github.com/appintheair/Tesseract-OCR-iOS'
pod 'DocumentsOCR', :path => '../'
pod 'SVProgressHUD'
+13 -5
View File
@@ -1,28 +1,36 @@
PODS:
- DocumentsOCR (1.0.0):
- DocumentsOCR (1.0.3):
- GPUImage
- PodAsset
- TesseractOCRiOS (~> 4.0.0)
- GPUImage (0.1.7)
- PodAsset (0.12.0)
- SVProgressHUD (2.0.3)
- TesseractOCRiOS (4.0.0)
- TesseractOCRiOS (4.0.1)
DEPENDENCIES:
- DocumentsOCR (from `../`)
- SVProgressHUD
- TesseractOCRiOS (from `https://github.com/appintheair/Tesseract-OCR-iOS`)
EXTERNAL SOURCES:
DocumentsOCR:
:path: ../
TesseractOCRiOS:
:git: https://github.com/appintheair/Tesseract-OCR-iOS
CHECKOUT OPTIONS:
TesseractOCRiOS:
:commit: a482f067e93e6670d6ea7753c36b6fac90d2c44c
:git: https://github.com/appintheair/Tesseract-OCR-iOS
SPEC CHECKSUMS:
DocumentsOCR: 10e00faee73177a4b2899fe480ac67971b455d06
DocumentsOCR: 6c84b18b230402fa12d7a5f9fea7eca564eca893
GPUImage: 733a5f0fab92df9de1c37ba9df520a833ccb406d
PodAsset: d6e724524e63725ef0c579354819ccc2dcbcf920
SVProgressHUD: b0830714205bea1317ea1a2ebc71e5633af334d4
TesseractOCRiOS: 90638afbe43d082433f2e76b384c7901d23956df
TesseractOCRiOS: 30545db87623f3f643280db372d318fc7355463c
PODFILE CHECKSUM: cd9f61bbc718914f9f971ce0f0248a7f58e7038b
PODFILE CHECKSUM: 18dff8c7466af2f5ef701624329ece0921fcf3b7
COCOAPODS: 1.1.0.rc.2
View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Before

Width:  |  Height:  |  Size: 198 KiB

After

Width:  |  Height:  |  Size: 198 KiB

View File

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 126 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File

Some files were not shown because too many files have changed in this diff Show More