iOS实践:OpenCV、Tesseract OCR结合 识别图片中文字

前言:

    前天领导问,类似扫描文件识别图中文字的功能如何实现,找一下第三方的开源库,尝试下,于是有了这篇文章;

 

分析:

    识别场景中,识别身份证信息当属典型,查阅了几篇文章,后续的实现中也多导入了其代码;

iOS身份证号码识别

图像识别技术(OCR)在iOS中的简单应用

 

OpenCV:

    OpenCV(开源计算机视觉库)是在BSD许可下发布的,因此对学术和商业使用都是免费的。它有c++、Python和Java接口,支持Windows、Linux、Mac OS、iOS和Android。OpenCV是为计算效率而设计的,并且非常注重实时应用。用优化的C/ c++编写的库可以利用多核处理。使用OpenCL,它可以利用底层异构计算平台的硬件加速。

    OpenCV在全球范围内被采用,拥有超过4.7万的用户社区,估计下载量超过1400万。它用途广泛。

 

OCR:

    OCR (Optical Character Recognition,光学字符识别)是指电子设备(例如扫描仪或数码相机)检查纸上打印的字符,通过检测暗、亮的模式确定其形状,然后用字符识别方法将形状翻译成计算机文字的过程;即,针对印刷体字符,采用光学的方式将纸质文档中的文字转换成为黑白点阵的图像文件,并通过识别软件将图像中的文字转换成文本格式,供文字处理软件进一步编辑加工的技术。如何除错或利用辅助信息提高识别正确率,是OCR最重要的课题,ICR(Intelligent Character Recognition)的名词也因此而产生。衡量一个OCR系统性能好坏的主要指标有:拒识率、误识率、识别速度、用户界面的友好性,产品的稳定性,易用性及可行性等。

 

TesseractOCR:

    TesseractOCR是谷歌开源的一个OCR引擎,github上下载Tesseract的工程和语言包(tessdata),TesseractOCR并没有自带语言包,如果要识别中文,还需要将中文包(名称:chi_sim.traineddata)导入到tessdata中。

 

下载:

    OpenCV下载

    Tesseract OCR iOS下载

 

项目实践:

    新建项目IdentiferIDCard,编辑Podfile;

 

 

项目安装使用:

    使用Cocoapods;安装等待的时间可能会很长;

 

 

Xcode编译前修改:

 


 

关于语言包:

    tessdata 语言包下载

    这个语言包比较大,如果只需要中文的语言包可以只下载中文的;

    字体训练中文包(版本匹配版)

 

遇到的各种问题及解决办法:(参考链接如下)

    编译运行的两个错误是因为NO宏引起的 由于系统不支持 改为CNO即可;

iOS实现图像文字识别功能

身份证识别

 

为了图片更加可辨识,使用了一个第三方的剪裁图片库

 

 

核心代码(摘录):

IdentifierIDCardManager.h

#import <Foundation/Foundation.h>
@class UIImage;

typedef void (^CompleateBlock)(NSString *text);

@interface IdentifierIDCardManager : NSObject

/**
 *  初始化一个单例
 *
 *  @return 返回一个RecogizeCardManager的实例对象
 */
+ (instancetype)recognizeCardManager;

/**
 *  根据身份证照片得到身份证号码
 *
 *  @param cardImage 传入的身份证照片
 *  @param compleate 识别完成后的回调
 */
- (void)recognizeCardWithImage:(UIImage *)cardImage compleate:(CompleateBlock)compleate;

@end

IdentifierIDCardManager.mm

#import "IdentifierIDCardManager.h"

#import <opencv2/opencv.hpp>
#import <opencv2/imgproc/types_c.h>
#import <opencv2/imgcodecs/ios.h>
#import <TesseractOCR/TesseractOCR.h>

@implementation IdentifierIDCardManager

+ (instancetype)recognizeCardManager {
    static IdentifierIDCardManager *recognizeCardManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        recognizeCardManager = [[IdentifierIDCardManager alloc] init];
    });
    return recognizeCardManager;
}

- (void)recognizeCardWithImage:(UIImage *)cardImage compleate:(CompleateBlock)compleate {
    //扫描身份证图片,并进行预处理,定位号码区域图片并返回
    UIImage *numberImage = [self opencvScanCard:cardImage];
    if (numberImage == nil) {
        compleate(nil);
    }
    //利用TesseractOCR识别文字
    [self tesseractRecognizeImage:numberImage compleate:^(NSString *numbaerText) {
        compleate(numbaerText);
    }];
}

//扫描身份证图片,并进行预处理,定位号码区域图片并返回
- (UIImage *)opencvScanCard:(UIImage *)image {
    
    //将UIImage转换成Mat
    cv::Mat resultImage;
    UIImageToMat(image, resultImage);
    //转为灰度图
    cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
    //利用阈值二值化
    cv::threshold(resultImage, resultImage, 100, 255, CV_THRESH_BINARY);
    //腐蚀,填充(腐蚀是让黑色点变大)
    cv::Mat erodeElement = getStructuringElement(cv::MORPH_RECT, cv::Size(26,26));
    cv::erode(resultImage, resultImage, erodeElement);
    //轮廊检测
    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) {
        return nil;
    }
     */
    //不只取身份证 而是取整个照片区域
    cv::Rect numberRect = cv::Rect(0,0,image.size.width,image.size.height);

    //定位成功成功,去原图截取身份证号码区域,并转换成灰度图、进行二值化处理
    cv::Mat matImage;
    UIImageToMat(image, matImage);
    resultImage = matImage(numberRect);
    cvtColor(resultImage, resultImage, cv::COLOR_BGR2GRAY);
    cv::threshold(resultImage, resultImage, 80, 255, CV_THRESH_BINARY);
    //将Mat转换成UIImage
    UIImage *numberImage = MatToUIImage(resultImage);
    return numberImage;
}

//利用TesseractOCR识别文字
- (void)tesseractRecognizeImage:(UIImage *)image compleate:(CompleateBlock)compleate {
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        G8Tesseract *tesseract = [[G8Tesseract alloc] initWithLanguage:@"chi_sim"];
        tesseract.image = [image g8_blackAndWhite];
        tesseract.image = image;
        // Start the recognition
        [tesseract recognize];
        //执行回调
        compleate(tesseract.recognizedText);
    });
}

@end

 

编辑好demo之后,我们编译运行:

    我们在Demo中附上了一张辨识度很高的图片作为测试;

 

实际的测试中,我发现用原生的库识别黑体准确度较高,但是识别其他字体要差很多;

可以通过训练获得自己的字体库来提高识别的准确度,尤其类似对身份证这种具体识别需求的字体库训练;

Tesseract-OCR的简单使用与训练

 

Demo截图:

 

关于Demo:

由于导入的OpenCV很大,导致Demo实在是有点大,没能传到Github上,码云也不OK,有需要的留言吧。

 

 

补充:

        有关图像进一步识别可以参考下面这篇文章,通过对图片的有效处理,最终的识别度很高;

      《OpenCV项目实战日志——检测文字并对比识别

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 35
    评论
评论 35
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值