基于Tesseract的OCR识别--身份证

news/2024/11/29 6:36:10/

 

目录

  • 需求背景

  • Tesseract简介及环境搭建

  • 字库训练

  • Tesseract for iOS

  • 总结

需求背景

由于客户端内核的限制,市场上大多数身份证识别都会放在服务器校验,客户端一般只是负责抓取图片,将抓取到的图片上送到服务器识别。这样一来如果客户端抓取到的身份证图片的质量无法保障,服务器也很难识别得出来,会拖慢身份证识别进程,造成用户体验不好的情况。(如我项目的流程是只要摄像头一打开就开始抓取图片上传到服务器去识别,不管抓取到的图片是否是身份证图片,客户端等待服务端返回结果,如果没解析出来则继续抓取图片上传,直至识别出来为止)。

针对客户端抓取身份证图片质量的情况,客户端应先对身份证所必须的字库进行训练(在Tesseract提供的字库中英文库21.9M,中文库52.7M,如果直接使用大大增加APP包大小)。然后将训练好的字库集成进Tesseract框架分别对性别、出生日期、身份证号、有效日期、人像等区域识别并进行校验,都通过校验之后再把得到的高质量图片上传到服务器去识别。

如此一来,大大减少客户端与服务器交互的同时,把高质量图片上传到服务器识别可以增加身份证识别成功率,减少身份证识别时间,提升用户体验。

Tesseract简介及环境搭建

简介

Tesseract的OCR引擎最先由HP实验室于1985年开始研发,至1995年时已经成为OCR业内最准确的三款识别引擎之一。然而,HP不久便决定放弃OCR业务,Tesseract也从此尘封。数年以后,HP意识到,与其将Tesseract束之高阁,不如贡献给开源软件业,让其重焕新生。在2005年,Tesseract由美国内华达州信息技术研究所获得,并委托Google对其进行改进、优化工作。

Tesseract目前已作为开源项目发布在Google Project,它与Leptonica图片处理库结合,可以读取各种格式的图像并将它们转化成超过60种语言的文本,我们还可以不断训练自己的库,使图像转换文本的能力不断增强。如果团队深度需要,还可以以它为模板,开发出符合自身需求的OCR引擎。

环境搭建

安装

1.安装Homebrew

打开Command Line Tool,直接输入下面指令:


ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

2.安装Tesseract

打开Command Line Tool,根据自己的需求选择相应的指令进行安装:


// 安装tesseract的同时安装训练工具brew install --with-training-tools tesseract// 安装tesseract的同时安装所有语言,语言包比较大,如果安装的话时间较长,建议不安装,按需选择brew install --all-languages tesseract// 安装tesseract,并安装训练工具和语言brew install --all-languages --with-training-tools tesseract// 只安装tesseract,不安装训练工具brew install tesseract

下载语言库

根据自己的需求可以到这里选择所需要的语言库,如我们选择的简体中文库是: chi_sim.traineddata,将下载好的文件拷贝到: /usr/local/Cellar/tesseract/3.05.01(tesseract版本号)/share/tessdata目录下。

使用

使用如下指令进行图片文字识别:

// 默认使用eng字库,imageName是图片绝对路径,result是识别结果tesseract imageName result// 指定使用简体中文tesseract -l chi_sim imageName result// 指定多语言,用+号相连tesseract -l chi_sim+eng imageName result// 查看本地存在的语言库tesseract --list-langs

有个地方需要特别注意,参数psm


// 输入命令,查看psm的参数tesseract --help-psm0    Orientation and script detection (OSD) only.1    Automatic page segmentation with OSD.2    Automatic page segmentation, but no OSD, or OCR.3    Fully automatic page segmentation, but no OSD. (Default)4    Assume a single column of text of variable sizes.5    Assume a single uniform block of vertically aligned text.6    Assume a single uniform block of text.7    Treat the image as a single text line.8    Treat the image as a single word.9    Treat the image as a single word in a circle.10    Treat the image as a single character.翻译:0 定向脚本监测(OSD)1 使用OSD自动分页2 自动分页,但是不使用OSD或OCR(Optical Character Recognition,光学字符识别)3 全自动分页,但是没有使用OSD(默认)4 假设可变大小的一个文本列。5 假设垂直对齐文本的单个统一块。6 假设一个统一的文本块。7 将图像视为单个文本行。8 将图像视为单个词。9 将图像视为圆中的单个词。10 将图像视为单个字符

根据情况选择不同的psm值,这很重要,如果选择到不恰当的值会导致识别失败。

例如下面这张图应假设为一个统一的文本块:

num.png

使用命令:


tesseract num.png result -l chi_sim打印:Tesseract Open Source OCR Engine v3.05.01 with LeptonicaEmpty page!!Empty page!!

使用命令:


tesseract num.png result -l chi_sim -psm 6打开result.txt文件,成功识别:一二三四一二三四

字库训练

安装jTessBoxEditor

翻墙后,在这里下载 jTessBoxEditorFX-2.0-Beta.zip,解压后得到jTessBoxEditorFX文件夹,由于这是由Java开发的,所以我们应确保运行jTessBoxEditor前先安装JRE(Java Runtime Environment,Java运行环境),由于JRE的安装教程很多,这里就不做过多介绍了。

获取样本文件

由于身份证的字体是比较固定的,所以不需要做太多样本进行训练。像身份证号的数字和X是黑体,性别、生日、有效期等字体是方正黑体简体,所以我们只需要在word上输入身份证上对应字体的文字,然后用切图工具把文字切出来,这样样本就可以获取到了。下面是我获取身份证文字的样本图片:

黑体数字:

黑体数字.jpg

黑体X:

image

华文黑体简体汉字:

image

华文黑体简体数字:

image

【注意】:样本图像文件格式必须为tif/tiff格式

Merge样本文件

进入jTessBoxEditor目录,在终端执行java -Xms128m -Xmx1024m -jar jTessBoxEditorFX.jar命令,会出现如下操作界面:

image

Tools->Merge TIFF,将样本文件全部选上,并将合并文件保存为font.tif,进入该文件所在目录,执行指令


tesseract font.tif font batch.nochop makebox

生成文件名为font.box文件

字符矫正

生成font.box文件后,可以使用jTessBoxEditor对字符进行矫正了。选中Box Editor->Open,打开font.tif,会出现如下操作界面:

image

刚开始对数字和字母识别得比较准确,但是识别的中文应该是乱码,选中文字在Character一栏输入正确的文字进行字符矫正、保存。这样就可以得到一个较为准确的字符集(PS:如果要识别其他字体和文字得获取大量样本进行训练矫正,由于身份证字体比较固定,所以只需识别固定文字即可,创建出来的字库在165KB左右)。

执行批处理文件

生成字符特征

执行指令


tesseract font.tif font nobatch box.train

生成.tr文件,它包含了训练页的每个字符的特征。

计算字符集

执行指令


unicharset_extractor font.box

生成unicharset数据文件,它包含了tesseract需要知道可能要输出的字符集。

字体属性

执行指令


echo 'font 0 0 0 0 0' > font_properties

可以将提供字体形式信息重定向到font_properties文本文件中,通过-F filename选项指定来进行mftraining.

当运行mftraining时,每个.tr文件名必须有相关entry在font_properties文件中,否则将中止mftraining。

聚合

当所有训练页的字符特征被抽取出来时,我们需要将它们聚集起来创建prototype文件。这些字符形状特性可以通过使用shapeclustering、mftraining和cntraining程序进行聚焦。


// shapeclustering通过形状聚集创建了主形状表,并将它写到一个文件中: shapetable.shapeclustering -F font_properties -U unicharset font.tr// mftraining将输出两个其它的数据文件: inttemp(形状原型)和pffmtable(每个字符所希望的特征)mftraining -F font_properties -U unicharset -O font.unicharset font.tr// 输出normproto数据文件cntraining font.tr

生成字库

将聚合后得到的normproto、inttemp、pffmtable、shapetable文件重命名为font.normproto、font.inttemp、font.pffmtable、font.shapetable

执行下面指令得到traineddata文件


combine_tessdata font.

最终的文件目录应该如下图所示:

image

生成的font.traineddata就是我们所需要的字库。

Tesseract for iOS

通过前面的介绍我们知道了Tesseract框架是根据我们提供的字库对图片上的文字进行识别,然后转化为文本的形式输出,并且我们也创建了自己的字库。但往往一张图片上不一定只有一个文本块,有可能有多个文本块。例如身份证它就有身份证号、性别、民族、出生年月、姓名等多个区块,那么如何把它们截取为一个个区块,然后将每个区块分别提供给Tesseract框架进行识别呢?

目前找到了两种方法对这些区块进行分离:

图像处理技术

图像处理技术是使用OpenCV库对图像进行灰度化,二值化,腐蚀,轮廓检测等。

1.灰度化处理:图片灰度化处理就是将指定图片每个像素点的RGB三个分量通过一定的算法计算出该像素点的灰度值,使图像只含亮度而不含色彩信息。

image

2.二值化:二值化处理就是将经过灰度化处理的图片转换为只包含黑色和白色两种颜色的图像,他们之间没有其他灰度的变化。在二值图中用255便是白色,0表示黑色。

image

3.腐蚀:图片的腐蚀就是将得到的二值图中的黑色块进行放大。即连接图片中相邻黑色像素点的元素。通过腐蚀可以把身份证上的身份证号码连接在一起形成一个矩形区域。

image

4.轮廓检测:图片经过腐蚀操作后相邻点会连接在一起形成一个大的区域,这个时候通过轮廊检测就可以把每个大的区域找出来,这样就可以定位到身份证上面号码的区域。

image

代码处理:


// 将UIImage转换成Matcv::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;}}

这种方式的优点是不需要用户定点描框,只要用户将身份证放到摄像头内便可自动处理,代码也相对简单,能够很大程度上提升用户体验。缺点是引入OpenCV库会增加了4M包的大小。

坐标计算处理

坐标计算处理原理是在手机上给定一个身份证区域让用户将身份证放入该区域内,通过计算坐标获取指定的区域(如我Demo的效果是这样):

正面

反面

这种方式的优点是不需要引入OpenCV库而导致又一程度上增加包的大小。缺点也是显而易见的,那就是需要用户对准指定区域进行定点描框,要不坐标计算后获取到的错误区域抛给Tesseract框架是识别不出的,用户体验不是很好。

综上所述,在实现方式的选择上可以根据自己的项目具体情况具体分析,由于我司项目是一个金融APP,引入TesseractOCRiOS框架已经增加5.1M包大小,若再引入OpenCV,那么为了一个OCR优化功能增加9M的包大小这是不能够接受的。若是类似于美图秀秀那类型的APP,使用图像处理的地方比较多则比较适合引入OpenCV库。所以下面将着重对坐标计算处理这种方式进行介绍。

实现步骤

导入Tesseract的iOS库

这里通过CocoaPods的方式引入第三方库:


pod 'TesseractOCRiOS'

导入字库

将创建好的字库放入tessdata文件夹并拖进工程,这里要特别注意,因为TesseractOCRiOS这个库寻找字库时不支持路径传递,并且找寻的路径是主Bundle路径下的tessdata文件夹里面的字库。所以一定要使用Create folder references这个选项在主Bundle下创建tessdata文件夹才能够获取到里面的字库。

属性选择

创建OcrDetectView

实时显示摄像头成像,并提供截取图片API供外部调用,它是照片数据源的来源。

布局身份证区域框

这里通过重力感应支持横竖屏切换。这里要特别注意当身份证区域框进行横竖屏切换时截取的文字区域也同步需要进行坐标转换,后面再详细介绍。

截取图片

每隔一定的时间(Demo里为1秒)定时截取图片,由于得到的图片是整个手机屏幕的图片,所以这里需要根据坐标及横竖屏进行图片处理。

坐标计算

由于限定了身份证区域框让用户将身份证放入该区域内进行识别,所以这些坐标是可以获取到的。

如获取身份证区域:


// 获取屏幕缩放比例(我是在6s机型上做的,当时设定的宽度为347.0,屏幕宽度为375.0,所以屏幕缩放比为347.0 / 375.0)CGFloat scale  = 347.0 / 375.0;// 获取身份证区域image- (UIImage *)fetchIDCardImage:(UIImage *)image isLandscape:(BOOL)isLandscape{CGSize  size = [UIScreen mainScreen].bounds.size;CGFloat screenWidth  = size.width;CGFloat screenHeight = size.height;// 图片实际宽高CGFloat width  = screenWidth  * scale;CGFloat height = screenHeight * scale;// image相对于屏幕的缩放比float px = image.size.width / screenWidth;float py = image.size.height / screenHeight;// 根据横竖屏计算x,y,w,hfloat x, y, w, h;if (isLandscape){// 由于切换为横屏实际上是以竖屏的身份证区域框的中心为基点旋转90°// 所以横屏下的x实际上是身份证区域框高度的一半加上竖屏状态下距离屏幕顶部的距离// 再减掉横屏状态下的宽度的一半x = height / 2.0 + idcardBoxTopOffset - width / 2.0;// 同理横屏状态下的y实际上是屏幕宽度减身份证区域高度的一半y = (screenWidth - height) / 2.0;w = width;h = height;image = [UIImage imageWithCGImage:image.CGImagescale:image.scaleorientation:UIImageOrientationUp];}else{x = (screenWidth - width) / 2.0;y = idcardBoxTopOffset;w = width;h = height;}// 身份证区域CGRect cutFrame = CGRectMake(x*py, y*px, w*py, h*px);// 根据传入身份证区域获取相应的imageUIImage *croppedImage = [JKOcrDetectUtils croppedImage:image inRect:cutFrame];croppedImage = [JKOcrDetectUtils adjustImageOrientation:croppedImage];return croppedImage;}

获取身份证号区域:


- (UIImage *)fetchIDCardNoImage:(UIImage *)image isLandscape:(BOOL)isLandscape{CGSize  size = [UIScreen mainScreen].bounds.size;CGFloat screenWidth  = size.width;CGFloat screenHeight = size.height;CGFloat width  = screenWidth  * self.widthScale;CGFloat height = screenHeight * self.heightScale;float px = image.size.width / screenWidth;float py = image.size.height / screenHeight;float x, y, w, h;if (isLandscape){x = height / 2.0 + idcardBoxTopOffset - width / 2.0 + idcardNoOffsetX;y = (screenWidth - height) / 2.0 + idcardNoOffsetY;w = idcardNoWidth;h = idcardNoHeight;image = [UIImage imageWithCGImage:image.CGImagescale:image.scaleorientation:UIImageOrientationUp];}else{x = (screenWidth - width) / 2.0 + idcardNoOffsetX;y = idcardBoxTopOffset + idcardNoOffsetY;w = idcardNoWidth;h = idcardNoHeight;}CGRect cutFrame = CGRectMake(x*py, y*px, w*py, h*px);UIImage *croppedImage = [JKOcrDetectUtils croppedImage:image inRect:cutFrame];croppedImage = [JKOcrDetectUtils adjustImageOrientation:croppedImage];return croppedImage;}

识别

通过坐标计算这个步骤可以获得可供识别的文本块图片样本,在这里我获取了5个区块,分别是性别、出生日期、身份证号、有效日期、人像。

同之前在Mac上识别步骤一样,初始化字库->设置psm等参数->传入待识别的图片->得到识别后的文本->校验文本。

识别身份证号代码:


- (void)recognizeImageWithTesseract:(UIImage *)image mode:(DetectMode)mode completionBlock:(void(^)(BOOL isRecognized, NSString *recognizedText))completionBlock{// 创建`G8RecognitionOperation`对象异步执行OCR识别并初始化字库G8RecognitionOperation *operation = [[G8RecognitionOperation alloc] initWithLanguage:@"font"];// 设置psm参数operation.tesseract.pageSegmentationMode = G8PageSegmentationModeSingleBlock;// 设置最大识别时间operation.tesseract.maximumRecognitionTime = 1.0;// 设置识别图片operation.tesseract.image = image;__weak JKOcrService *wself = self;operation.recognitionCompleteBlock = ^(G8Tesseract *tesseract) {// 识别后的文本NSString *recognizedText = tesseract.recognizedText;__strong JKOcrService *sself = wself;// 校验文本if ([JKOcrDetectUtils accurateVerifyIDCardNumber:recognizedText]){// 识别成功回调if (completionBlock) completionBlock(YES, recognizedText);}else{// 识别失败回调if (completionBlock) completionBlock(NO, @"");}};// 添加队列[self.operationQueue addOperation:operation];}

校验身份证号代码:


+ (BOOL)accurateVerifyIDCardNumber:(NSString *)value{value = [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];int length = 0;if (!value){return NO;}else{length = (int)value.length;if (length !=15 && length !=18){return NO;}}// 省份代码NSArray *areasArray = @[ @"11", @"12", @"13", @"14", @"15", @"21", @"22", @"23", @"31", @"32", @"33", @"34", @"35", @"36", @"37", @"41", @"42", @"43", @"44", @"45", @"46", @"50", @"51", @"52", @"53", @"54", @"61", @"62", @"63", @"64", @"65", @"71", @"81", @"82", @"91"];NSString *valueStart2 = [value substringToIndex:2];BOOL areaFlag = NO;for (NSString *areaCode in areasArray){if ([areaCode isEqualToString:valueStart2]){areaFlag = YES;break;}}if (!areaFlag){return false;}NSRegularExpression *regularExpression;NSUInteger numberofMatch;int year = 0;switch (length){case 15:year = [value substringWithRange:NSMakeRange(6,2)].intValue +1900;if (year %4 == 0 || (year % 100 == 0 && year % 4 ==0)){regularExpression = [[NSRegularExpression alloc] initWithPattern:@"^[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}$"options:NSRegularExpressionCaseInsensitiveerror:nil];//测试出生日期的合法性}else{regularExpression = [[NSRegularExpression alloc]initWithPattern:@"^[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}$"options:NSRegularExpressionCaseInsensitiveerror:nil];//测试出生日期的合法性}numberofMatch = [regularExpression numberOfMatchesInString:valueoptions:NSMatchingReportProgressrange:NSMakeRange(0, value.length)];if (numberofMatch > 0){return YES;}else{return NO;}case 18:year = [value substringWithRange:NSMakeRange(6,4)].intValue;if (year % 4 ==0 || (year % 100 ==0 && year % 4 ==0)){regularExpression = [[NSRegularExpression alloc] initWithPattern:@"^[1-9][0-9]{5}19[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}[0-9Xx]$"options:NSRegularExpressionCaseInsensitiveerror:nil];//测试出生日期的合法性}else{regularExpression = [[NSRegularExpression alloc] initWithPattern:@"^[1-9][0-9]{5}19[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}[0-9Xx]$"options:NSRegularExpressionCaseInsensitiveerror:nil];//测试出生日期的合法性}numberofMatch = [regularExpression numberOfMatchesInString:valueoptions:NSMatchingReportProgressrange:NSMakeRange(0, value.length)];if(numberofMatch >0){int S = ([value substringWithRange:NSMakeRange(0,1)].intValue + [value substringWithRange:NSMakeRange(10,1)].intValue) *7 + ([value substringWithRange:NSMakeRange(1,1)].intValue + [value substringWithRange:NSMakeRange(11,1)].intValue) *9 + ([value substringWithRange:NSMakeRange(2,1)].intValue + [value substringWithRange:NSMakeRange(12,1)].intValue) *10 + ([value substringWithRange:NSMakeRange(3,1)].intValue + [value substringWithRange:NSMakeRange(13,1)].intValue) *5 + ([value substringWithRange:NSMakeRange(4,1)].intValue + [value substringWithRange:NSMakeRange(14,1)].intValue) *8 + ([value substringWithRange:NSMakeRange(5,1)].intValue + [value substringWithRange:NSMakeRange(15,1)].intValue) *4 + ([value substringWithRange:NSMakeRange(6,1)].intValue + [value substringWithRange:NSMakeRange(16,1)].intValue) *2 + [value substringWithRange:NSMakeRange(7,1)].intValue *1 + [value substringWithRange:NSMakeRange(8,1)].intValue *6 + [value substringWithRange:NSMakeRange(9,1)].intValue *3;int Y = S % 11;NSString *M = @"F";NSString *JYM = @"10X98765432";M = [JYM substringWithRange:NSMakeRange(Y,1)];// 判断校验位if ([M isEqualToString:[value substringWithRange:NSMakeRange(17,1)]]){return YES;// 检测ID的校验位}else{return NO;}}else{return NO;}default:return NO;}}

性别、出生日期、有效日期的识别步骤大同小异,这里就不一一列举了。然后可以通过使用dispatch_group并发分别进行识别,得到一个结果集后再统一进行验证,如果都识别通过的话则表示这是一张高质量的身份证图片,就可以把这张身份证图片上传到服务器进行识别了。


__block BOOL isIDCardRecognized = NO;__block BOOL isGenderRecognized = NO;__block BOOL isBirthdayRecognized = NO;__block BOOL isFaceRecognized = NO;__weak JKOcrService *wself = self;[self recognizeImageWithTesseract:idcardNoImagemode:DetectModeIDCardcompletionBlock:^(BOOL isRecognized, NSString *recognizedText) {__strong JKOcrService *sself = wself;if (isRecognized == YES){isIDCardRecognized = YES;sself.idcardNo = recognizedText;}}];[self recognizeImageWithTesseract:genderImagemode:DetectModeGendercompletionBlock:^(BOOL isRecognized, NSString *recognizedText) {__strong JKOcrService *sself = wself;if (isRecognized == YES){isGenderRecognized = YES;sself.sex = recognizedText;}}];[self recognizeImageWithTesseract:birthdayImagemode:DetectModeBirthdaycompletionBlock:^(BOOL isRecognized, NSString *recognizedText) {__strong JKOcrService *sself = wself;if (isRecognized == YES){isBirthdayRecognized = YES;sself.birthday = recognizedText;}}];[self accurateVerifyFace:faceImagecompletionBlock:^(BOOL isRecognized) {isFaceRecognized = isRecognized;}];dispatch_group_wait(self.group, dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC));if (isGenderRecognized && isIDCardRecognized && isBirthdayRecognized && isFaceRecognized){[self.operationQueue cancelAllOperations];dispatch_async(dispatch_get_main_queue(), ^{if (successHandler){// 高质量的身份证图片UIImage *idcardImage = [self fetchIDCardImage:image isLandscape:isLandscape];// TODO: 识别成功}});}else{// TODO: 识别失败}

据我统计,每个样本识别时间间隔为20ms左右,也就是说,只要样本没问题,逐个识别所用的时间也不到100ms,相对于设定1秒的时间间隔来说是绰绰有余,但是使用异步也没毛病。

总结

通过前面的介绍了解到了Tesseract是什么,可以用于什么样业务场景,如何进行样本训练生成字库。还介绍了如何在Mac OS X上使用Tesseract进行图片识别生成文本,并在这基础之上引入了基于iOS的Tesseract库TesseractOCRiOS,使其能够为移动端服务。

但是引入Tesseract这个库打包出来的APP会增加5.1M包大小,如果再加上系统字库,包大小更是会显著增加。虽然自己训练样本生成字库可以解决这一问题,但是单单为了身份证流程优化这一功能,这样的结果往往还是难以接受的。

既然这样,是否能将Tesseract的作用发挥到极致,例如银行卡自动识别也使用它进行流程优化等等。


http://www.ppmy.cn/news/657773.html

相关文章

移除所有本地应用程序(数据库)加密设置

大家好&#xff0c;才是真的好。 最近我就有这样一个烦恼&#xff0c;要移除Notes本地的所有本地应用程序&#xff08;数据库&#xff09;的加密设置&#xff0c;这样就可以放到Domino服务器上&#xff0c;然后支持其他电脑上不同的Notes访问。毕竟&#xff0c;默认地&#xf…

智能三路CAN总线集线器解决CAN总线集线问题

随着科技的发展&#xff0c;CAN总线通讯再各行各业中应用&#xff0c;再不同的应用中&#xff0c;需要把两路CAN集合到一起&#xff0c;或者把一路CAN分为两路&#xff0c;这时候就需要CAN总线集线器来解决。 CANbridge-300每一路CAN通道都具有数据存储转发至另两路CAN通道的功…

计算机怎么通电启动,怎么让电脑一通电就立即开机?

现在的大多数主板厂商都在自己的主板BIOS里加入了一个独特的电源管理设计&#xff0c;可以让用户选择计算机在停电后再来电时的状态。在BIOS里“Power Management Setup”菜单下有一项“Pwron After PWR-fail”&#xff0c;它有三个设置选项ON(开机)、OFF(关机)和FORMER-STS(回…

windows 安装系统快速启动键大全

文章目录 笔记本&#xff1a;F12F11F10F9F8ESC 台式机 笔记本&#xff1a; F12 - 联想 - Thinkpad - 宏碁 - 戴尔 - 神舟 - 东芝 - 三星 - IBM - 富士通 - 海尔 - 方正 - 清华同方 - eMachines - IntelF11 - 微星 - 顶星 - 华擎 F10 翔升 F10F9 - 惠普 - 冠铭 - …

设置从优盘启动维护系统的热键

优盘启动热键 组装电脑笔记本电脑品牌台式机主板品牌启动按键主板品牌启动按键主板品牌启动按键华硕主板F8联想笔记本F12联想品牌台式F12技嘉主板F12宏碁笔记本F12惠普品牌台式F9或F12微星主板F11华硕笔记本ESC宏碁品牌台式F12映泰主板F9惠普笔记本F9戴尔品牌台式ESC或F12梅捷主…

各种品牌笔记本,主板,启动项,快速设置,快捷键,史上最全

安装系统是必备&#xff0c;装系统者必须知道&#xff0c;可以给你省却设置bios的麻烦 关闭机器&#xff0c;之后按开关&#xff0c;之后不停的按下列键。。会进入启动设备选择界面&#xff0c;之后你可要选择从光驱启动&#xff0c;硬盘启动&#xff0c;USB光驱启动&#xff0…

启用计算机的快捷键,电脑开机启动项选择快捷键大全

在我们安装系统的时候&#xff0c;最基本的就是要设置光驱启动或者U盘启动&#xff0c;选择好了才能通过安装介质来引导安装;现在的主板一般都可以通过开机按快捷键来选择; 下面小编就分享一下各大牌子主板、笔记本的“电脑开机启动项选择快捷键”; 一、主板类(你电脑上装的是什…

朋友准备买笔记本,整理了些东西扫盲

Intel 移动处理器一览 Core 2 命名规则 Core 2 Duo系列采用了全新的命名规则&#xff0c;由一个前缀字母加四位数字组成&#xff0c;形式是Core 2 Duo 字母xxxx&#xff0c;例如Core 2 Duo E6600等等。 前缀字母在编号里代表处理器TDP(热设计功耗)的范围&#xff0c;目前共…