title: ‘OCR:基于OpenCV、Tesseract的银行卡号识别’
type: categories
date: 2016-12-01 16:50:30
categories: OC
tags: [OCR, OpenCV, Tesseract]
由于银行卡的卡面背景色彩千差万别,并且卡号的印制方式(平印、凸印)也不相同,所以这种识别方式的效果并不理想,可以说很差,暂时对平印的单一色彩的银行卡的识别效果还行;
这种方式可以用来做身份证的识别,效果很好,因为身份证背景颜色浅,而且样式一致。
效果图:
本文Demo
https://github.com/zhangzhaopds/OCR_OpenCV_Tesseract_demo.git
思路
1、对预览图进行初步的手动裁剪,缩小OpenCV的处理范围;
2、利用OpenCV对图片进行初步的处理,包括灰度化处理、二阈值处理、膨胀处理等;
3、利用TesseractIOSOCR进行图片的文字识别;
主要实现
依赖库:
pod 'OpenCV', '~> 3.0.0'
pod 'TesseractOCRiOS', '~> 4.0.0'
实现(代码中有解释):
图片的剪裁、识别与结果处理:
// 图片的剪裁、识别与结果处理
- (void)detecteCardWithImage:(UIImage *)cardImage compleate:(CompleteBlock)complete {
/**
相对于身份证来说,银行卡片的背景环境千差万别,有的卡片无需处理而有的则需要灰度值或二阈值重新处理,用一种方式处理千百种环境,结果可想而知;
这里的话就简单的,在图片的不同处理阶段进行多次的文字识别,最后在统一处理;
第一次:卡号所在位置的图片截取之后,进行识别;
第二次:灰度值处理之后,进行识别;
第三次:二阈值处理之后,进行识别;
第四次:腐蚀化重新截图并灰度值处理之后,进行识别;
第五次:腐蚀化重新截图、灰度值并二阈值处理之后,进行识别;
*/
// 将卡号所在的大致位置在图片上截取出来,缩小OpenCV要识别的图片范围,认为的提高识别效率。
UIImage *corpImage = [self cropImageFromImage:cardImage];
if (corpImage == nil) {
complete(nil);
return;
}
// 识别结果的初步处理
__weak typeof(self) weakSelf = self;
self.myBlock = ^(NSString *res) {
// 信用卡16位,储蓄卡19位
if (res.length < 16) {
return;
}
NSString *result = [weakSelf findNumFromStr:res];
NSLog(@"��%@", result);
if (result.length < 16) {
return;
}
complete(result);
};
// 第一次识别:
[self tesseractDetectorWithImage: corpImage withComplete:^(NSString *result) {
NSLog(@"第一次识别:%@", result);
weakSelf.myBlock(result);
}];
// 利用OpenCV,对截取出来的图片进一步处理,并进行类外四次的识别
[self opencvScanCard:corpImage];
}
因为识别的次数增多,所以结果的反馈较慢,可相应减少识别次数。
利用OpenCV对图片的进一步处理
- (void)opencvScanCard:(UIImage *)image {
// 图片转换
cv::Mat resultImage;
UIImageToMat(image, resultImage);
// 灰度处理(去除图片的色彩和光亮)
cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
// 第二次识别:
__weak typeof(self) weakSelf = self;
[self tesseractDetectorWithImage: MatToUIImage(resultImage) withComplete:^(NSString *result) {
NSLog(@"第二次识别:%@", result);
weakSelf.myBlock(result);
}];
// 二阈值处理
cv::threshold(resultImage, resultImage, 100, 255, CV_THRESH_BINARY);
// 第三次识别:
[self tesseractDetectorWithImage: MatToUIImage(resultImage) withComplete:^(NSString *result) {
NSLog(@"第三次识别:%@", result);
weakSelf.myBlock(result);
}];
// 腐蚀:白色背景缩小,黑色扩大
cv::Mat erodeElement = getStructuringElement(cv::MORPH_RECT, cv::Size(25,25)); //3535
cv::erode(resultImage, resultImage, erodeElement);
UIImage *ccc = MatToUIImage(resultImage);
UIImageWriteToSavedPhotosAlbum(ccc, nil, nil, nil);
// 轮廊检测
std::vector<std::vector<cv::Point>> contours;
cv::findContours(resultImage, contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
// 取出卡号区域
std::vector<cv::Rect> rects;
cv::Rect numberRect = cv::Rect(0,0,0,0);
std::vector<std::vector<cv::Point>>::const_iterator itContours = contours.begin();
for ( ; itContours != contours.end(); ++itContours) {
cv::Rect rect = cv::boundingRect(*itContours);
rects.push_back(rect);
if (rect.width > numberRect.width && rect.width > rect.height * 5) {
numberRect = rect;
}
}
if (numberRect.width == 0 || numberRect.height == 0) {
NSLog(@"定位失败");
return;
}
// 定位成功,重新截图
cv::Mat matImage;
UIImageToMat(image, matImage);
resultImage = matImage(numberRect);
// 第二次灰度值处理
cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
// 第四次识别:
[self tesseractDetectorWithImage: MatToUIImage(resultImage) withComplete:^(NSString *result) {
NSLog(@"第四次识别:%@", result);
weakSelf.myBlock(result);
}];
// 第二次二阈值处理
cv::threshold(resultImage, resultImage, 100, 255, CV_THRESH_BINARY);
// 第五次识别:
[self tesseractDetectorWithImage: MatToUIImage(resultImage) withComplete:^(NSString *result) {
NSLog(@"第五次识别:%@", result);
weakSelf.myBlock(result);
}];
}
Tesseract识别
// Tesseract识别
- (void)tesseractDetectorWithImage:(UIImage *)img withComplete:(CompleteBlock)complete {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
G8Tesseract *tesseract = [[G8Tesseract alloc] initWithLanguage:@"eng"];
tesseract.image = [img g8_blackAndWhite];
tesseract.image = img;
[tesseract recognize];
complete(tesseract.recognizedText);
});
}
银行卡片的初次裁剪
// 裁剪银行卡号
- (UIImage *)cropImageFromImage:(UIImage *)img {
static CGFloat cardWidth = 400;
static CGFloat cardHeight = 400/1.59;
CGFloat h = img.size.height * 500 / img.size.width;
UIGraphicsBeginImageContext(CGSizeMake(500, h));
[img drawInRect:CGRectMake(0, 0, 500, h)];
UIImage *scaleImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGFloat y = (scaleImg.size.height - cardHeight) / 2;
CGImageRef sourceImageRef = [scaleImg CGImage];
CGImageRef newImageRef = CGImageCreateWithImageInRect(sourceImageRef, CGRectMake(50, y, cardWidth, cardHeight));
CGImageRef resultImgRef = CGImageCreateWithImageInRect(newImageRef, CGRectMake(0, 130, cardWidth, 50));
UIImage *mm = [UIImage imageWithCGImage:resultImgRef];
/**
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"%@", scaleImg);
NSLog(@"%@", [UIImage imageWithCGImage:newImageRef]);
NSLog(@"%@", mm);
UIImageWriteToSavedPhotosAlbum(scaleImg, nil, nil, nil);
UIImageWriteToSavedPhotosAlbum([UIImage imageWithCGImage:newImageRef], nil, nil, nil);
UIImageWriteToSavedPhotosAlbum(mm, nil, nil, nil);
})
*/
return mm;
}
调用
- (void)clickedDetecteBtn:(UIButton *)sender {
//【点击事件中调用图片识别,防止CPU飙升】
[[DetectorManager shareInstance] detecteCardWithImage:self.myImage compleate:^(NSString *result) {
NSLog(@"识别结果:%@", result);
}];
}
参考文献
OpenCV教程-http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/tutorials.html
SwiftOCR教程-https://github.com/garnele007/SwiftOCR.git
Tesseract教程-https://github.com/tesseract-ocr/tesseract.git