如何训练专属的OCR文字识别模型_tesseract 中文训练模型-程序员宅基地

技术标签: android  ocr  

1. 背景

在10月24日程序员节,公司决定向每位技术人员发放购物实体卡以示庆祝。然而,手动输入实体卡上的一大串卡密可能是一项繁琐且不那么智能的任务;同时,线上用户在绑定购物卡的时候,同样也是需要手动输入。

基于以上背景,我们决定开发一套能够自动识别卡密的OCR文字识别模型:简单地将卡密放在相机前,便可以无需费心记忆和手动输入卡密,快速完成兑换过程,减少了可能存在的人为错误,以带来更便捷的用户体验

2. 基于通用模型的识别

2.1 OCR框架选型

随着文档数字化的发展,光学字符识别 (OCR) 变得越来越流行。OCR 在处理基于图像的文档中发挥着至关重要的作用。在文字识别方面,主要有两款主流的开源框架Tesseract和EasyOCR。

根据参考文献[1]中进行的一项实验,Tesseract 在字母识别方面做得更好,而 EasyOCR 在数字识别方面做得更好。在我们的实际使用场景下,字母的使用频率要远高于字母,因此我们选择使用Tesseract,它是一款目前由 Google 维护的开源 OCR引擎。

2.2 OCR框架安装&使用

如果你使用的是Mac系统,可以直接使用Homebrew进行安装:

brew install tesseract

框架自带的少量通用模型存储在/usr/local/share/tessdata目录下,后缀名为.traineddata,如果需要更多类型的通用模型,可以从github中下载并导入该目录下。GitHub - tesseract-ocr/tessdata_best: Best (most accurate) trained LSTM models.
模型使用可以直接在命令行中输入:

tesseract <input-pic-path> <output-path> -l <model-name> --psm 7

2.3 通用模型的识别效果

我们使用最通用的英文数字模型eng.traineddata,对一张实体卡的卡密进行识别,效果如下,可以看到,识别效果很一般,错误率较高,不仅把特殊字体的0识别成了B,还可能会把B识别成E。原因就是我们的卡密字体并不是一种常规字体,会造成通用模型的识别困难。
初步识别效果

2.4 交互层面弥补错误识别

针对通用模型识别错误率高的问题,首先想到的是交互方面的优化
将容易识别错的相似元素进行分组,譬如0、8、B、E为一组,R、A为一组等等。当识别到存在组内元素的时候,为用户提供一个用于交互的UI滚轮组件,使用户能够手动修正为组内的其他相似元素。
思考再三,还是觉得这样的解决方案太粗暴了,用户可能会疑惑为什么OCR每次都需要手动修正,这么明显的数字0为什么识别不出来?最终还是放弃了从交互层面来弥补错误的思路。

3. 训练专属的OCR模型

Tesseract除了可以使用官方提供的语言包(traineddata文件),还可以自己训练模型,特别适用于某些官方语言包识别效果不佳的场景下。本章将基于Tesseract-OCR5.0来训练自己的模型、以及如何提高准确率。
由于购物卡的卡密字体在生产过程中是不会改变的,因此我们可以根据它的字体样本,来训练一套专属的OCR模型,来改善通用模型在识别过程中的一些错误,提高OCR的准确率。

3.1 模型训练 - 样本采集准备

训练的前置准备工作就是样本采集,字体的数量千千万,我们无法通过肉眼识别实体卡的卡密到底用了哪种字体。首先我们尝试使用字体识别工具,发现最相似的字体匹配程度也只在70%左右,由于样本本身并不是百分百的匹配,经过后续的训练测试验证,识别效果也确实不理想。
字体寻找
解铃还须系铃人,只能想方设法找购物卡的制造商来解决了。经过沟通,然而制造商回应:
样本寻找
因此我们并不能拿到卡密字体的电子样本,为了不让这次沟通无功而返,我打算让制造商将0~9、A~Z打印出来并拍照。于是便有了下面这张照片,也是仅有的可用于训练的样本。
样本寻找

3.2 模型训练 - 生成用于训练的tif和box文件

首先生成训练用的tif文件,需要将多个训练图片合成到一个tif文件里。tif文件格式是一种用于存储图像数据的文件格式,具有无损压缩、支持多页等特性。我们可以直接使用jTessBoxEditor工具来完成,工具下载地址VietOCR - Browse /jTessBoxEditor at SourceForge.net
打开jTessBoxEditor,选择Tools -> Merge TIFF,进入训练样本所在文件夹,选中要参与训练的样本图片,并将tif文件命名为"eng.myfont.exp0.tif"。

tif文件命名格式 [lang].[fontname].exp[num].tif
lang是语言名,fontname是字体名称,num为自定义版本号

tif
接着,在训练目录下打开cmd,输入下面命令,用现有的eng模型初步识别.tif文件并生成对应的.box文件,执行后在当前路径下生成 “eng.myfont.exp0.box” 文件。

tesseract eng.myfont.exp0.tif eng.myfont.exp0 -l eng --psm 7 batch.nochop makebox

box文件中将记录每个字符在图片上的位置以及识别出的内容。其中,psm参数用于设置识别模式,7代表将图像视为单个文本行,我们的场景使用的是单行统一文本,因此采用参数7。其他psm常用参数所代表的识别模式汇总如下:

0    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.
11    Sparse text. Find as much text as possible in no particular order.
12    Sparse text with OSD.
13    Raw line. Treat the image as a single text line, bypassing hacks that are Tesseract-specific.

3.3 模型训练 - 矫正box文件错误

由于我们使用的是通用模型对tif进行的初步识别,因此box文件中字符的内容和位置大概率是会出错的,且需要来手动调整的。依然使用jTessBoxEditor工具,点击Box Editor -> Open,打开步骤2中生成的“zwp.test.exp0.tif”,会自动关联到“zwp.test.exp0.box”文件,这两文件要求在同一目录下。
下图是对box文件中字符内容和位置的错误纠正示例:
纠错1
纠错2
调整完样本中的错误之后,点击"save"保存修改。box文件中的字符信息也会相应地进行更改,只是jTessBoxEditor工具帮助我们可视化地完成了这件事。

3.4 模型训练 - 生成基于通用模型的lstm文件

从0到1开始训练一个新模型需要海量的样本和超强的算力,如果样本不足容易过拟合,不适合我们快速出结果,因此我们的模型将基于已有通用模型进行微调训练
从步骤2.2中的tessdata_best代码库里,下载所需语言的通用模型.traineddata文件,在本案例中选择下载eng.traineddata文件,并将该文件保存到当前工作目录下。并使用combine_tessdata命令,用当前目录下的eng.traineddata文件生成eng.lstm文件:

combine_tessdata -e eng.traineddata eng.lstm

打印输出:

Extracting tessdata components from eng.traineddata
Wrote eng.lstm
Version:4.00.00alpha:eng:synth20170629:[1,36,0,1Ct3,3,16Mp3,3Lfys64Lfx96Lrx96Lfx512O1c1]
17:lstm:size=11689099, offset=192
18:lstm-punc-dawg:size=4322, offset=11689291
19:lstm-word-dawg:size=3694794, offset=11693613
20:lstm-number-dawg:size=4738, offset=15388407
21:lstm-unicharset:size=6360, offset=15393145
22:lstm-recoder:size=1012, offset=15399505
23:version:size=80, offset=15400517

Tesseract从版本4.0开始,训练方法使用的是基于LSTM的深度学习网络,lstm文件的命名也由此而来,为模型训练的中间产物。

3.5 模型训练 - 基于样本生成lstmf文件

这一步利用tif图片文件生成lstmf文件,如下命令会生成eng.myfont.exp0.lstmf文件,这个是典型tesseract命令:

tesseract eng.myfont.exp0.tif eng.myfont.exp0 -l eng --psm 7 lstm.train

--参数介绍--
0  eng.myfont.exp0.tif   输入图像
1  eng.myfont.exp0       输出的lstmf文件名称
2  -l eng                使用的基础模型名称
3  --psm 7               分割模式,上文已介绍
4  lstm.train            指明进行lstm训练

生成lstmf之后,在同层级目录下新建一个eng.training_files.txt文本文件,将lstmf的绝对路径写入文件,在我本地电脑中的文本文件如下所示:

/Users/calvin/tesseract/training/eng.myfont.exp0.lstmf

3.6 模型训练 - 开始训练

使用lstmtraining命令开始训练,这一步将生成训练中间文件_checkpoint:

lstmtraining 
--model_output="/Users/calvin/tesseract/training/" 
--continue_from="/Users/calvin/tesseract/training/eng.lstm" 
--train_listfile="/Users/calvin/tesseract/training/eng.training_files.txt"
--traineddata="/Users/calvin/tesseract/training/eng.traineddata" 
--debug_interval -1 
--max_iterations 2000 
--target_error_rate 0.01
  • –debug_interval -1 调试打印等级
  • –max_iterations 2000 最大迭代次数
  • –target_error_rate 0.01 期望错误率
  • –continue_from 基于eng字体的lstm文件
  • –model_output 输出checkpoint文件的名称
  • –train_listfile 训练清单文件名称
  • –traineddata 训练使用的字体

输出打印:

Loaded file /Users/calvin/tesseract/training/eng.lstm, unpacking...
Continuing from /Users/calvin/tesseract/training/eng.lstm
Loaded 5/5 lines (1-5) of document /Users/calvin/tesseract/training/eng.myfont.exp0.lstmf
Iteration 0: GROUND  TRUTH : 0124567890
Iteration 0: BEST OCR TEXT : 8124567898
File eng.myfont.exp0.lstmf line 0 :
Mean rms=2.635%, delta=7.463%, train=40%(100%), skip ratio=0%
Iteration 1: GROUND  TRUTH : RSTUVWXYZ
Iteration 1: BEST OCR TEXT : RSTUUWSEYZ
File eng.myfont.exp0.lstmf line 1 :
Mean rms=2.764%, delta=8.649%, train=47.778%(100%), skip ratio=0%
..........
..........
At iteration 15/1100/1100, mean rms=0.033%, delta=0.000%, BCER train=0.000%, BWER train=0.000%, 
skip ratio=0.000%, New best BCER = 0.000 wrote best model:/Users/calvin/tesseract/training/_0.000_15_1100.checkpoint wrote checkpoint.
Finished! Selected model with minimal training error rate (BCER) = 0

3.7 模型训练 - 基于训练结果生成模型文件

使用lstmtraining命令结束训练,这一步将生成traineddata模型文件:

lstmtraining --stop_training 
--traineddata="/Users/calvin/tesseract/training/eng.traineddata" 
--continue_from="/Users/calvin/tesseract/training/_checkpoint" 
--model_output="/Users/calvin/tesseract/training/yx.traineddata"
  • –stop_training 停止训练
  • –traineddata 训练使用的字体
  • –continue_from 训练中间文件名称
  • –model_output 生成模型文件的名称

输出日志:

Loaded file /Users/calvin/tesseract/training/_checkpoint, unpacking...

3.8 模型测试

最后将生成的yx.traineddata文件拷贝到tesseract安装目录的tessdata路径下,即完成字体的安装。再次使用2.2中提及的命令,对我们的模型进行测试:

tesseract <input-pic-path> <output-path> -l yx --psm 7

result
可以看到,在我们的新模型下,图片的OCR识别效果得到了很大的改善,错误率明显降低

4. 模型准确率优化

从前面的训练步骤可以看到,开始训练时需要用到一个已存在的模型eng.traineddata,比如第3.4步抽取它的lstm文件、第3.6步的训练等。既然这个原始字体训练出来的新字体识别的准确率不高,那我们是不是可以用训练好的新字体替代eng.traineddata再训练一次呢,这样产生的第2代的新字体会不会有更好地表现?
基于参考文献[4]中的测试结果,在大量测试数据下,第二次迭代后的识别准确率是要明显高于第一次的,且不受psm识别模式的影响,同时在后续第三次、第四次、第五次迭代后准确率则提升不明显
retrain
因此,在兼顾效率和准确率的前提下,我们选择可以对模型进行二次迭代优化,回到步骤3.4步~步骤3.7,将eng.traineddata相关的内容替换为yx.traineddata,新生成的第2代模型取名为yx2.traineddata,得到更高精度的字体识别模型。

5. 模型在移动端上的落地应用

all
模型在移动端上的落地应用需要分三层工作来完成:

  • 模型的训练放到本地进行,经过反复测试、优化,将训练好的文件模型上传到服务器。
  • 服务器负责为移动端提供接口,提供简单的版本管理和资源下发。
  • 应用端基于Tesseract SDK提供模型解析环境,并将模型应用于具体的OCR场景中。

6. 总结与后续工作

基于上文的训练过程,我们完成了针对特定字体的OCR模型识别训练,和通用模型相比,我们在特定字体场景下具有更高程度的OCR识别准确度。后续的工作主要从以下两方面进行:

  • 大量测试与纠错优化
    使用更多的购物卡对模型进行测试,若出现识别错误,则基于出错样本对已有模型继续进行训练优化。
  • 移动端上的资源下载/管理的能力建设
    生成的模型的大小约15MB左右,说大不大,说小也不小。可以肯定的是,无论如何都不可能把模型内置于移动端的安装包中,原则上是按需下发加载,因此需要移动端上具有资源下载的基础能力,需要考虑资源下载失败、版本更新、完整性校验等工作。最后,模型的下发与否需要遵从用户的意愿,因此在业务层面,需要保留OCR和手动输入两种入口。

7. 参考文献

  1. OCR Engine Comparison — Tesseract vs. EasyOCR

  2. Tesseract OCR in Python with Pytesseract andOpenCV

  3. Tesseract-OCR页面分割模式 (PSM) 使用详解

  4. Tesseract-OCR5.0字体训练以及提高准确率、提升训练效率的方法

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/SEU_Calvin/article/details/134517629

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法