基于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光学字符识别银行卡_第1张图片
image

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

基于OCR光学字符识别银行卡_第2张图片
image

然后看下ScanCardVC.m的实现;

#import "ScanCardVC.h"

#import "CardIO.h"
#import "CardIOView.h"

@interface ScanCardVC ()

@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 ()

@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光学字符识别银行卡_第3张图片
image

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

NSCameraUsageDescription
NSCameraUsageDescription内用内需访问相机(扫描银行卡)

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

你可能感兴趣的:(基于OCR光学字符识别银行卡)