OCR-基於OpenCV、Tesseract的銀行卡號識別
阿新 • • 發佈:2019-01-09
title: ‘OCR:基於OpenCV、Tesseract的銀行卡號識別’
type: categories
date: 2016-12-01 16:50:30
categories: OC
tags: [OCR, OpenCV, Tesseract]
由於銀行卡的卡面背景色彩千差萬別,並且卡號的印製方式(平印、凸印)也不相同,所以這種識別方式的效果並不理想,可以說很差,暫時對平印的單一色彩的銀行卡的識別效果還行;
這種方式可以用來做身份證的識別,效果很好,因為身份證背景顏色淺,而且樣式一致。
效果圖:
本文Demo
思路
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);
}];
}