神刀安全网

基于OCR光学字符识别银行卡

 最近在研究银行卡的识别扫描,借鉴了微信和银联钱包添加银行卡的style,回头研究了一下什么是OCR,它到底是用来做什么的?首先我们先来弄明白什么是OCR:OCR技术是光学字符识别的缩写(Optical Character Recognition),是通过扫描等光学输入方式将各种票据、报刊、书籍、文稿及其它印刷品的文字转化为图像信息,再利用文字识别技术将图像信息转化为可以使用的计算机输入技术。
 还有OCR的软件结构是什么:OCR软件主要是由下面几个部分组成。

 图像输入、预处理:
 图像输入:对于不同的图像格式,有着不同的存储格式,不同的压缩方式,目前有OpenCV,CxImage等开源项目 。

 预处理:主要包括二值化,噪声去除,倾斜较正等

 二值化:
 对摄像头拍摄的图片,大多数是彩色图像,彩色图像所含信息量巨大,对于图片的内容,我们可以简单的分为前景与背景,为了让计算机更快的,更好的识别文字,我们需要先对彩色图进行处理,使图片只前景信息与背景信息,可以简单的定义前景信息为黑色,背景信息为白色,这就是二值化图了。

 噪声去除:
 对于不同的文档,我们对噪声的定义可以不同,根据噪声的特征进行去噪,就叫做噪声去除

 倾斜较正:
 由于一般用户,在拍照文档时,都比较随意,因此拍照出来的图片不可避免的产生倾斜,这就需要文字识别软件进行较正。

 版面分析:
 将文档图片分段落,分行的过程就叫做版面分析,由于实际文档的多样性,复杂性,因此,目前还没有一个固定的,最优的切割模型。

 字符切割:
 由于拍照条件的限制,经常造成字符粘连,断笔,因此极大限制了识别系统的性能,这就需要文字识别软件有字符切割功能。

 字符识别:
 这一研究,已经是很早的事情了,比较早有模板匹配,后来以特征提取为主,由于文字的位移,笔画的粗细,断笔,粘连,旋转等因素的影响,极大影响特征的提取的难度。

 版面恢复:
 人们希望识别后的文字,仍然像原文档图片那样排列着,段落不变,位置不变,顺序不变,的输出到word文档,pdf文档等,这一过程就叫做版面恢复。

 后处理、校对:
 根据特定的语言上下文的关系,对识别结果进行较正,就是后处理。
 到此为止,我们对OCR有了些许的了解了,下面就正式进入今天的《基于OCR光学字符识别银行卡》主话题:通过网罗资料,获取了一款很多人都认为比较好的cardIO-SDK来辅助我完成我的工作,闲话少叙,我们依然直奔主题:
 首先:先去这个网站github.com/card-io/card.io-iOS-SDK去下载cardIO-SDK,
然后:添加到项目里
1、将下载的SDK包里名为CardIO的文件拖到工程里,在TARGETS-Build Phases - Link Binary With Librarys添加下面依赖库

* AudioToolbox * AVFoundation * CoreGraphics * CoreMedia * CoreVideo * Foundation * MobileCoreServices * OpenGLES * QuartzCore * Security * UIKit 

如果是xcode5或者更新的版本,只需要添加下面的库

* AVFoundation * AudioToolbox * CoreMedia * MobileCoreServices 

并且保证Build Settings里面这两项都是YES:

* Enable Modules (C and Objective-C)  * Link Frameworks Automatically 

2、在TARGETS-Build Settings添加 -lc++到Other Linker Flags

其次:就是怎么去使用这个sdk了;

请大家移步到这里。。。。。。。。。。。

图片一所示,已经集成好sdk的项目总览

基于OCR光学字符识别银行卡

image

图片二所示的是创建了一个自定义的ScanCardVC,是基于View的扫描的

基于OCR光学字符识别银行卡

image

然后看下ScanCardVC.m的实现;

#import "ScanCardVC.h"  #import "CardIO.h" #import "CardIOView.h"  @interface ScanCardVC ()<CardIOViewDelegate>  @property (nonatomic, weak) IBOutlet CardIOView *scanV;  @end @implementation ScanCardVC  /****************************************************************************** **** Customzied Method                                                                                              **** ******************************************************************************/ #pragma mark - #pragma mark Customzied Method  /****************************************************************************** **** CardIOViewDelegate Method                                                                              **** ******************************************************************************/ #pragma mark - #pragma mark  CardIOViewDelegate Method  - (void)cardIOView:(CardIOView *)cardIOView didScanCard:(CardIOCreditCardInfo *)cardInfo { cardInfo.scanned = YES; NSString *cardNum = [NSString stringWithFormat:@"%@", cardInfo.cardNumber]; NSLog(@"cardNum %@",cardNum);  //卡类型 NSString *cardType = [CardIOCreditCardInfo displayStringForCardType:cardInfo.cardType usingLanguageOrLocale:@"zh-Hans"];  //卡logo UIImage *bankLogo = [CardIOCreditCardInfo logoForCardType:cardInfo.cardType];  //持卡人 NSString *cardholderName = cardInfo.cardholderName;  //扫描结果 NSString *redactedCardNumber = cardInfo.redactedCardNumber;      // 卡号 NSString *expiryMonth = [NSString stringWithFormat:@"%lu",(unsigned long)cardInfo.expiryMonth];          // 月 NSString *expiryYear  = [NSString stringWithFormat:@"%lu",(unsigned long)cardInfo.expiryYear];            // 年 NSString *cvv = cardInfo.cvv;                          // CVV 码  // 显示扫描结果  //    NSString *msg = [NSString stringWithFormat:@"Number: %@/n/n expiry: %2@/%@/n/n cvv: %@", [self dealCardNumber:redactedCardNumber], expiryMonth, expiryYear, cvv];  //    [[[UIAlertView alloc] initWithTitle:@"获取卡信息:" //                                message:msg //                              delegate:nil //                      cancelButtonTitle:@"确定" //                      otherButtonTitles:nil, nil] show];  NSLog(@"Received card info. Number: %@, expiry: %02lu/%lu, cvv: %@.", cardInfo.redactedCardNumber, (unsigned long)cardInfo.expiryMonth, (unsigned long)cardInfo.expiryYear, cardInfo.cvv);  NSMutableDictionary *bankInfo = [NSMutableDictionary dictionary];  [bankInfo setValue:cardNum forKey:@"cardNum"]; [bankInfo setValue:expiryYear forKey:@"expiryYear"]; [bankInfo setValue:expiryMonth forKey:@"expiryMonth"]; [bankInfo setValue:cardType forKey:@"cardType"]; [bankInfo setValue:cardholderName forKey:@"cardholderName"]; if (self.delegate) { [self.delegate scanCardVC:self didScanSuceessBankInfo:bankInfo]; } [self.navigationController popViewControllerAnimated:YES]; }   // 对银行卡号进行每隔四位加空格处理,自定义方法 - (NSString *)dealCardNumber:(NSString *)cardNumber { //CardIOCreditCardInfo *info里面包含了银行卡的一些信息,如info.cardNumber是扫描的银行卡号,现实的是完整号码,而info.redactedCardNumber只显示银行卡后四位,前面的用*代替了,返回的银行卡号都没有空格  //可以用下面注释的方法来加空格 NSString *strTem = [cardNumber stringByReplacingOccurrencesOfString:@" " withString:@""]; NSString *strTem2 = @""; if (strTem.length % 4 == 0) { NSUInteger count = strTem.length / 4; for (int i = 0; i < count; i++) { NSString *str = [strTem substringWithRange:NSMakeRange(i * 4, 4)];  strTem2 = [strTem2 stringByAppendingString:[NSString stringWithFormat:@"%@ ", str]]; } } else { NSUInteger count = strTem.length / 4; for (int j = 0; j <= count; j++) { if (j == count) { NSString *str = [strTem substringWithRange:NSMakeRange(j * 4, strTem.length % 4)]; strTem2 = [strTem2 stringByAppendingString:[NSString stringWithFormat:@"%@ ", str]]; } else { NSString *str = [strTem substringWithRange:NSMakeRange(j * 4, 4)]; strTem2 = [strTem2 stringByAppendingString:[NSString stringWithFormat:@"%@ ", str]]; } } } return strTem2; }  /****************************************************************************** **** Default Lifecycle Method                                                                                      **** ******************************************************************************/ #pragma mark - #pragma mark Default Lifecycle Method  //初始化扫描View - (void)initScanView { //设置扫描的语言环境 self.scanV.languageOrLocale = @"zh-Hans";  //设置扫描的代理 self.scanV.delegate = self; self.scanV.hideCardIOLogo = YES;//是否隐藏扫描的logo self.scanV.useCardIOLogo = YES;  //修改扫描框里面的提示文字,设置为nil,则显示sdk默认的文字 self.scanV.scanInstructions = @"请将卡片放置框内进行扫描"; //    self.scanV.frame = self.scanV.cameraPreviewFrame;  // 默认情况下,在相机视图卡指南和按钮总是匹配设备的方向旋转 self.scanV.allowFreelyRotatingCardGuide = YES;  //设置在扫描成功后多长时间展现扫描成功界面 self.scanV.scannedImageDuration = 0.2f; self.scanV.guideColor = [UIColor lightGrayColor]; }  - (void)viewDidLoad { [super viewDidLoad]; self.title = @"扫描银行卡"; [CardIOUtilities preloadCardIO]; [self initScanView]; }  @end 

完了之后我们移步到ScanHomeVC.m里面,主要是学习下基于VC的扫描和基于View的扫描:

#import "ScanHomeVC.h"  #import "ScanCardVC.h" #import "CardIO.h" #import "CardIOPaymentViewController.h" #import "Tools.h"  @interface ScanHomeVC ()<CardIOPaymentViewControllerDelegate,ScanCardVCDelegate>  @property (nonatomic, strong) CardIOPaymentViewController *cardIOVC;  @property (nonatomic, weak) IBOutlet UILabel *cardNumLb;  @end  @implementation ScanHomeVC  /****************************************************************************** **** Customzied Method                                                                                              **** ******************************************************************************/ #pragma mark - #pragma mark Customzied Method  //继承vc扫描 - (IBAction)onScanOneBtnClicked:(id)sender { self.cardIOVC = [[CardIOPaymentViewController alloc]initWithPaymentDelegate:self]; [self setCardIO]; [self presentViewController:self.cardIOVC animated:YES completion:nil]; }  //继承view扫描 - (IBAction)onScanTwoBtnClicked:(id)sender { ScanCardVC *vc = [ScanCardVC new]; vc.delegate = self; [self.navigationController pushViewController:vc animated:YES]; } -    (void)scanCardVC:(ScanCardVC *)scanCardVC didScanSuceessBankInfo:(NSDictionary *)bankInfo { NSString *cardNum = [Tools dealCardNumber:bankInfo[@"cardNum"]]; NSString *cardType = bankInfo[@"cardType"]; NSString *cardholderName = bankInfo[@"cardholderName"]; self.cardNumLb.adjustsFontSizeToFitWidth = YES; self.cardNumLb.text = [NSString stringWithFormat:@"卡号:%@  卡类型:%@ 持卡人:%@",cardNum,cardType,cardholderName]; }  /****************************************************************************** **** CardIOPaymentViewController Delegate Method                                            **** ******************************************************************************/ #pragma mark - #pragma mark CardIOPaymentViewController Delegate Method  //扫描 - (void)userDidProvideCreditCardInfo:(CardIOCreditCardInfo *)cardInfo inPaymentViewController:(CardIOPaymentViewController *)paymentViewController { NSLog(@"cardInfo%@",cardInfo); //扫描结果 NSString *redactedCardNumber = cardInfo.cardNumber;    // 卡号 NSUInteger expiryMonth = cardInfo.expiryMonth;          // 月 NSUInteger expiryYear = cardInfo.expiryYear;            // 年 NSString *cvv = cardInfo.cvv;                          // CVV 码 // 显示扫描结果 //    NSString *msg = [NSString stringWithFormat:@"Number: %@/n/n expiry: %02lu/%lu/n/n cvv: %@", [self dealCardNumber:redactedCardNumber], expiryMonth, expiryYear, cvv];  //    [[[UIAlertView alloc] initWithTitle:@"获取卡信息:" //                                message:msg //                              delegate:nil //                      cancelButtonTitle:@"确定" //                      otherButtonTitles:nil, nil] show];  NSLog(@"Received card info. Number: %@, expiry: %02lu/%lu, cvv: %@.", cardInfo.redactedCardNumber, (unsigned long)cardInfo.expiryMonth, (unsigned long)cardInfo.expiryYear, cardInfo.cvv); // Use the card info... [self.cardIOVC dismissViewControllerAnimated:YES completion:nil]; }  //取消扫描 - (void)userDidCancelPaymentViewController:(CardIOPaymentViewController *)paymentViewController { [self dismissViewControllerAnimated:YES completion:nil]; }  /****************************************************************************** **** Default Lifecycle Method                                                                                    **** ******************************************************************************/ #pragma mark - #pragma mark Default Lifecycle Method  - (void)setCardIO { self.cardIOVC.languageOrLocale = @"zh-Hans"; //隐藏PayPal的logo self.cardIOVC.hideCardIOLogo = YES; self.cardIOVC.useCardIOLogo = NO; self.cardIOVC.keepStatusBarStyleForCardIO = YES; self.cardIOVC.allowFreelyRotatingCardGuide = YES;  //修改扫描边框颜色 self.cardIOVC.guideColor = [UIColor lightGrayColor];  //是否支持显示卡图标 self.cardIOVC.suppressScannedCardImage = YES; self.cardIOVC.detectionMode = CardIODetectionModeAutomatic; }  - (void)viewDidLoad { [super viewDidLoad]; self.title = @"扫描银行卡"; }  - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:YES];  //扫描的预加载 [CardIOUtilities preload]; }  @end 

附上ScanHomeVC.xib

基于OCR光学字符识别银行卡

image

最后忘了一点,就是获取相机的权限:

<key>NSCameraUsageDescription</key> <string>NSCameraUsageDescription内用内需访问相机(扫描银行卡)</string> 

写在最后:这个sdk虽好,但是只能支持扫描信用卡以及一些少数的借记卡,扫描成功率挺高,但是还是要看自己的需求环境了。。。

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » 基于OCR光学字符识别银行卡

分享到:更多 ()