1. 程式人生 > >OCR-基於OpenCV、Tesseract的銀行卡號識別

OCR-基於OpenCV、Tesseract的銀行卡號識別

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);
    }];
}

參考文獻