Tess4.0手动合并输入数据及模型训练流程

之前,写过一篇文章特殊字符语言包训练流程(新)记录了Tess4.0训练模型的流程。但是由于Tesseract的系统限制,Tess4.0无法自动训练中文正体和英文斜体的混合文本,因此,后来写了文章Tess4.0中英文正体斜体混合训练记录了中文正体和英文斜体的训练流程。然而,这篇文章里暴力地将所有中文正体放在一个页面,英文斜体放在另一个页面。因此,Tess4.0无法学习到中英文文本之间的顺序。为了解决中文正体和英文斜体之间的顺序识别问题,这篇文章记录了手动合并Tess4.0的输入数据的过程。手动合并的数据很好地融合了中文正体和英文斜体之间的顺序信息。这样,Tess4.0后期的LSTM网络结构可以很好地学习到中文正体和英文斜体之间的顺序信息。

Tess输入数据生成步骤

初步分析,Tess4.0的输入数据主要由tif和box两个文件组成,其中tif2文件是Tess待识别的图片,box文件中记录了每个字符在图片中的位置信息。现在我们假设所有的文本信息记录在txt文本中,下面阐述生成tif和box文本的流程。

txt文本信息转存到word文档

由于需要学习txt文本中的英文斜体信息(txt不支持正体和斜体同步出现的情况),我们需要将txt中的所有文本按顺序转存到word中,其中,中文设置为宋体正体,英文设置为Times new Roman斜体。其主要python代码如下:

document = Document()
for eachLine in content_lines.split('\n'):  //content_lines读取的txt文本中的内容
    p = document.add_paragraph()            //所有数据保存在document对象中,读入数据以后保存到相应路径下
    if eachLine == '':
        continue
    for each_word in eachLine:
        if is_alphabet(each_word): //字母以Times New Roman斜体形式保存
            run = p.add_run(each_word + '       ')
            run.font.name = u'Times New Roman'
            run.italic = True
        else:
            run = p.add_run(each_word + '    ')  //其他字符保存宋体正体
            if each_word == '_':
                run.font.bold = True
                run.italic = True
            run.font.name = u'宋体'
            run._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')
document.save('word_path')

word文档转图片

已经得到了中文正体和英文斜体混合的word文档,下面就是将其转化为图片信息。转为图片的目的是截取每个字符的box位置,由于字符之间的间距很小,截取box位置难度较大。为了提高字符的截取精度,在上一步骤转存word文档时,我们故意在每个字符后面添加一些空格,这样字符之间的间距增大,就降低了字符截取的难度。

在word转图片时,首先将word转存为pdf文档,然后利用下面的java代码转为tif图片:

public static void main (String[] args) {
        // TODO Auto-generated method stub
        String filePath = "math_data.pdf";
        File file = new File(filePath);
        try {
            PDDocument doc = PDDocument.load(file);
            PDFRenderer renderer = new PDFRenderer(doc);
            int pageCount = doc.getNumberOfPages();
            for(int i=0;i<pageCount;i++){
                BufferedImage image = renderer.renderImageWithDPI(i, 296);
                String saveFilePath = "./test_data/";
                saveFilePath += "image";
                saveFilePath += "_";
                saveFilePath += filePath;
                saveFilePath += "_";
                saveFilePath += i;
                saveFilePath += ".tif";
                System.out.println("saveFilePath = " + saveFilePath);
                ImageIO.write(image, "tif", new File(saveFilePath));
            }
        }catch (IOException e) {
            e.printStackTrace();
        }
    }

这些代码基本修改于网络上现成的程序,所以不同的数据处理步骤用了不同的语言(python, java, 后面的代码还有用到C++的)。

box文件提取与图像字符重组

在提取字符box位置时,主要思路是二值化,水平投影截取行,再垂直投影截取字符位置。然后将截取出来的字符重新排列位置,主要调整字符之间的距离,顺序不变,并调整相应的box位置 。代码流程框架见下:

CutWords cutWords = CutWords();
Mat srcImage = imread(path);  // 获取原图
Mat srcImageBin;
srcImageBin = cutWords.horizonProjection(srcImage);  // 二值化并且水平投影
cutWords.combineBlob();  // 融合行处理
list<Mat> lineImgs = cutWords.cutLine(srcImageBin);  // 截取行
//cutWords.regionLines(srcImage);  // 画出行截取的中间结果
cutWords.verticalProjection(lineImgs);  // 对每行进行垂直投影,截取字符
cutWords.combineBox();  // 融合字符处理
//cutWords.regionWords(srcImage); // 画出字符截取结果
//waitKey(0);
srcImageBin = cutWords.adjustBoxLocation(srcImageBin, pageNum);  // 调整字符位置和box文件
sprintf(saveBox, "E:/javaWorkspace/PdfConvertImage/test_box/boxdata_%d.txt", pageNum);
cutWords.writeBoxFile("E:/jiequ.txt", saveBox, pageNum, srcImageBin);  // 画出box文件和新的图片

由代码注释可见主要的处理过程。

box文件后续处理

上一步骤的box文件生成的结果与Tesseract的输入文件存在一定的偏差,需要将具体的文本字符和box文件进行合并,并且添加图片中的行转换提示符。因此需要对box文件后续处理。

处理步骤如下:

  1. 将字符与box文件进行合并,上一步骤中只有box位置信息,但每个位置对应的字符并没有给出,这里需要将字符信息与位置信息对应;
  2. 每个字符之间需要插入一些转行提示符。

具体的处理代码如下:

tag = 0
for file_path in html_dir:
    tag += 1
    fopen = open('E:/数据/math_html/' + file_path, 'rb')
    content_lines = fopen.read().decode('utf-8')
    for eachLine in content_lines.split('\n'):
        each_box_line = ''
        if box_num < len(box_text)-1:
            for each_word in eachLine: # 每行数据进行处理
                print(box_text[box_num])
                if each_word == ' ' or each_word == ' ' or each_word == ' ' or \
                                each_word == ' ' or each_word == '�' or each_word == '': # 排除文本中的特殊字符
                    continue
                if box_num +1 == len(box_text)-1:
                    box_num += 1
                    break
                box_line += each_word + ' ' + box_text[box_num] + '\r\n'  # 合并字符和位置信息
                if len(box_text[box_num+1].split(' ')) > 2:  # 插入转行符
                    if box_text[box_num].split(' ')[1] != box_text[box_num+1].split(' ')[1]:
                        box_line += '   ' + ' ' + str(int(box_text[box_num].split(' ')[0])+60) + ' ' + \
                                    box_text[box_num].split(' ')[1] + ' ' + str(int(box_text[box_num].split(' ')[2])+60) \
                                    + ' ' + box_text[box_num].split(' ')[3] + ' ' + box_text[box_num].split(' ')[4] + '\r\n'
                box_num += 1
            if len(box_text[box_num+1].split(' ')) > 2:
                if box_text[box_num].split(' ')[1] != box_text[box_num+1].split(' ')[1]:
                    box_line += '   ' + ' ' + str(int(box_text[box_num].split(' ')[0])+60) + ' ' + \
                                box_text[box_num].split(' ')[1] + ' ' + str(int(box_text[box_num].split(' ')[2])+60) \
                                + ' ' + box_text[box_num].split(' ')[3] + ' ' + box_text[box_num].split(' ')[4]+ '\r\n'
            box_num += 1

验证box和tif的生成结果

tif和box文件都是由我们自己根据txt文档生成的,生成的结果如何验证很重要。这里我们给出验证数据质量的方法:

  1. 下载安装jTessBoxEditor工具,具体的工具使用方法可以查看日志:android中tesseract-ocr自定义字库的介绍。这里不再赘述;
  2. 提取box文档中的其中一页的box信息,提取方法如下:
    updated_box_file = open('E:/javaWorkspace/PdfConvertImage/math_data.box', 'rb')
    text_content = updated_box_file.read().decode('utf-8').strip()
    
    box_page = 15
    reault = ''
    for each_box in text_content.split('\r\n'):
        try:
            if int(each_box.split(' ')[5]) == box_page:
                reault += each_box.split(' ')[0] + ' ' + each_box.split(' ')[1] + ' ' + each_box.split(' ')[2] + ' ' \
                          + each_box.split(' ')[3] + ' ' + each_box.split(' ')[4] + ' 0' + '\r\n'
        except:
            print(each_box)
    open('E:/javaWorkspace/PdfConvertImage/test_data/image_math_data.pdf_'+str(box_page)+'.box', 'wb').write(reault.encode('utf-8'))
    
  3. 将对应的tif文件和box文件相同命名,并且放入同一目录下,用jTessBoxEditor加载图片,验证box信息是否正确。

合并tif和box文件

由于训练数据量要求比较大,这里我们生成了很多tif图片需要进一步合并,box文件也需要相应的处理。具体的处理方法可见Tess4.0中英文正体斜体混合训练文章中的合并中英文数据章节。这里直接给出合并box文件的代码:

box_file1 = open('E:/jTessBoxEditorFX/tesseract-ocr/temp_roman/chi_sim.SIMSUN_roman.exp0.box', 'rb').read().decode('utf-8')
box_file2 = open('E:/jTessBoxEditorFX/tesseract-ocr/temp_roman/math_data.box', 'rb').read().decode('utf-8')

box_content = box_file1 + '\n' // 第一个文件中的内容直接拷贝到box_content变量中
box1_page_num = 0
for line in box_file1.split('\n'): // 获取第一个文件中的页码数
    if len(line) > 0:
        page_num = int(line.split(' ')[len(line.split(' '))-1])
    if page_num > box1_page_num:
        box1_page_num = page_num

box1_page_num += 1
for line in box_file2.split('\r\n'): // 合并第二个文件
    if len(line) > 0:
        page_num1 = int(line.split(' ')[len(line.split(' '))-1])
        sub_line = ''
        for i in range(len(line.split(' '))-1):
            sub_line += line.split(' ')[i] + ' '
        box_content += sub_line + str(page_num1+box1_page_num) + '\n'

open('E:/jTessBoxEditorFX/tesseract-ocr/temp_roman/chi_sim.SIMSUN_roman.exp1.box', 'bw').write(box_content.encode('utf-8'))

print('merge success!')

训练Tesseract模型

在阅读本章节之前建议详细阅读Tess4.0中英文正体斜体混合训练这篇文章,这里的处理步骤原理与Tess4.0中英文正体斜体混合训练文章中的神经网络输入数据准备章节的处理步骤一致。更详细的训练模型的方法步骤可以参考文章特殊字符语言包训练流程(新)或者github中给出的详细教程

tif和box文档生成的具体步骤

  1. 将所有待识别的数据以txt文档的格式放入./data/alltxt_input文件夹中;
  2. 运行./txt2word.py脚本,生成word_data.docx在./data/目录下;
  3. 将./word_data.docx文件手动打开,并存储为PDF格式保存在相同目录下;
  4. 运行./PdfConvertImage/src/testConvert.java文件,将pdf文档转存为tif文件,并存储在./data/img_tif/目录下;
  5. 运行./OCR_Process/OCRMain.cpp文件,截取box并调整字符图片位置,box文件存储在./data/txt_box/目录下,tif存储在./data/adjust_img_tif/目录下;
  6. 运行./boxfile_update.py脚本,合并字符与box信息。

这样,Tesseract的输入数据就已经生产好了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • Tesseract中英文正体斜体混合训练 当我们识别数据中包含中文正体,英文斜体字符时,Tess4.0识别英文斜体...
    RobertY阅读 4,792评论 1 8
  • 个人学习批处理的初衷来源于实际工作;在某个迭代版本有个BS(安卓手游模拟器)大需求,从而在测试过程中就重复涉及到...
    Luckykailiu阅读 4,679评论 0 11
  • 《ilua》速成开发手册3.0 官方用户交流:iApp开发交流(1) 239547050iApp开发交流(2) 1...
    叶染柒丶阅读 10,507评论 0 11
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,594评论 18 139
  • 今天是四点二十起床。 从开春到现在,从第二次说100天早睡早起,又在艰难的坚持中最后不得不放弃。从坚持到勉强在坚持...
    朱泓默阅读 308评论 5 6