之前,寫過一篇文章特殊字符語言包訓(xùn)練流程(新)記錄了Tess4.0訓(xùn)練模型的流程量承。但是由于Tesseract的系統(tǒng)限制蹭越,Tess4.0無法自動訓(xùn)練中文正體和英文斜體的混合文本,因此棋电,后來寫了文章Tess4.0中英文正體斜體混合訓(xùn)練記錄了中文正體和英文斜體的訓(xùn)練流程振劳。然而椎组,這篇文章里暴力地將所有中文正體放在一個(gè)頁面,英文斜體放在另一個(gè)頁面历恐。因此庐杨,Tess4.0無法學(xué)習(xí)到中英文文本之間的順序。為了解決中文正體和英文斜體之間的順序識別問題夹供,這篇文章記錄了手動合并Tess4.0的輸入數(shù)據(jù)的過程灵份。手動合并的數(shù)據(jù)很好地融合了中文正體和英文斜體之間的順序信息。這樣哮洽,Tess4.0后期的LSTM網(wǎng)絡(luò)結(jié)構(gòu)可以很好地學(xué)習(xí)到中文正體和英文斜體之間的順序信息填渠。
Tess輸入數(shù)據(jù)生成步驟
初步分析,Tess4.0的輸入數(shù)據(jù)主要由tif和box兩個(gè)文件組成,其中tif2文件是Tess待識別的圖片氛什,box文件中記錄了每個(gè)字符在圖片中的位置信息≥汉現(xiàn)在我們假設(shè)所有的文本信息記錄在txt文本中,下面闡述生成tif和box文本的流程枪眉。
txt文本信息轉(zhuǎn)存到word文檔
由于需要學(xué)習(xí)txt文本中的英文斜體信息(txt不支持正體和斜體同步出現(xiàn)的情況)捺檬,我們需要將txt中的所有文本按順序轉(zhuǎn)存到word中,其中贸铜,中文設(shè)置為宋體正體堡纬,英文設(shè)置為Times new Roman斜體。其主要python代碼如下:
document = Document()
for eachLine in content_lines.split('\n'): //content_lines讀取的txt文本中的內(nèi)容
p = document.add_paragraph() //所有數(shù)據(jù)保存在document對象中蒿秦,讀入數(shù)據(jù)以后保存到相應(yīng)路徑下
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文檔轉(zhuǎn)圖片
已經(jīng)得到了中文正體和英文斜體混合的word文檔烤镐,下面就是將其轉(zhuǎn)化為圖片信息。轉(zhuǎn)為圖片的目的是截取每個(gè)字符的box位置棍鳖,由于字符之間的間距很小炮叶,截取box位置難度較大。為了提高字符的截取精度渡处,在上一步驟轉(zhuǎn)存word文檔時(shí)镜悉,我們故意在每個(gè)字符后面添加一些空格,這樣字符之間的間距增大医瘫,就降低了字符截取的難度积瞒。
在word轉(zhuǎn)圖片時(shí),首先將word轉(zhuǎn)存為pdf文檔登下,然后利用下面的java代碼轉(zhuǎn)為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();
}
}
這些代碼基本修改于網(wǎng)絡(luò)上現(xiàn)成的程序,所以不同的數(shù)據(jù)處理步驟用了不同的語言(python, java, 后面的代碼還有用到C++的)叮喳。
box文件提取與圖像字符重組
在提取字符box位置時(shí)被芳,主要思路是二值化,水平投影截取行馍悟,再垂直投影截取字符位置畔濒。然后將截取出來的字符重新排列位置,主要調(diào)整字符之間的距離锣咒,順序不變侵状,并調(diào)整相應(yīng)的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); // 畫出行截取的中間結(jié)果
cutWords.verticalProjection(lineImgs); // 對每行進(jìn)行垂直投影毅整,截取字符
cutWords.combineBox(); // 融合字符處理
//cutWords.regionWords(srcImage); // 畫出字符截取結(jié)果
//waitKey(0);
srcImageBin = cutWords.adjustBoxLocation(srcImageBin, pageNum); // 調(diào)整字符位置和box文件
sprintf(saveBox, "E:/javaWorkspace/PdfConvertImage/test_box/boxdata_%d.txt", pageNum);
cutWords.writeBoxFile("E:/jiequ.txt", saveBox, pageNum, srcImageBin); // 畫出box文件和新的圖片
由代碼注釋可見主要的處理過程趣兄。
box文件后續(xù)處理
上一步驟的box文件生成的結(jié)果與Tesseract的輸入文件存在一定的偏差,需要將具體的文本字符和box文件進(jìn)行合并悼嫉,并且添加圖片中的行轉(zhuǎn)換提示符艇潭。因此需要對box文件后續(xù)處理。
處理步驟如下:
- 將字符與box文件進(jìn)行合并,上一步驟中只有box位置信息蹋凝,但每個(gè)位置對應(yīng)的字符并沒有給出鲁纠,這里需要將字符信息與位置信息對應(yīng);
- 每個(gè)字符之間需要插入一些轉(zhuǎn)行提示符鳍寂。
具體的處理代碼如下:
tag = 0
for file_path in html_dir:
tag += 1
fopen = open('E:/數(shù)據(jù)/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: # 每行數(shù)據(jù)進(jìn)行處理
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: # 插入轉(zhuǎn)行符
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
驗(yàn)證box和tif的生成結(jié)果
tif和box文件都是由我們自己根據(jù)txt文檔生成的改含,生成的結(jié)果如何驗(yàn)證很重要。這里我們給出驗(yàn)證數(shù)據(jù)質(zhì)量的方法:
- 下載安裝jTessBoxEditor工具迄汛,具體的工具使用方法可以查看日志:android中tesseract-ocr自定義字庫的介紹捍壤。這里不再贅述;
- 提取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'))
- 將對應(yīng)的tif文件和box文件相同命名白群,并且放入同一目錄下,用jTessBoxEditor加載圖片硬霍,驗(yàn)證box信息是否正確帜慢。
合并tif和box文件
由于訓(xùn)練數(shù)據(jù)量要求比較大,這里我們生成了很多tif圖片需要進(jìn)一步合并唯卖,box文件也需要相應(yīng)的處理粱玲。具體的處理方法可見Tess4.0中英文正體斜體混合訓(xùn)練文章中的合并中英文數(shù)據(jù)章節(jié)。這里直接給出合并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' // 第一個(gè)文件中的內(nèi)容直接拷貝到box_content變量中
box1_page_num = 0
for line in box_file1.split('\n'): // 獲取第一個(gè)文件中的頁碼數(shù)
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'): // 合并第二個(gè)文件
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!')
訓(xùn)練Tesseract模型
在閱讀本章節(jié)之前建議詳細(xì)閱讀Tess4.0中英文正體斜體混合訓(xùn)練這篇文章拜轨,這里的處理步驟原理與Tess4.0中英文正體斜體混合訓(xùn)練文章中的神經(jīng)網(wǎng)絡(luò)輸入數(shù)據(jù)準(zhǔn)備章節(jié)的處理步驟一致抽减。更詳細(xì)的訓(xùn)練模型的方法步驟可以參考文章特殊字符語言包訓(xùn)練流程(新)或者github中給出的詳細(xì)教程。
tif和box文檔生成的具體步驟
- 將所有待識別的數(shù)據(jù)以txt文檔的格式放入./data/alltxt_input文件夾中橄碾;
- 運(yùn)行./txt2word.py腳本卵沉,生成word_data.docx在./data/目錄下;
- 將./word_data.docx文件手動打開法牲,并存儲為PDF格式保存在相同目錄下史汗;
- 運(yùn)行./PdfConvertImage/src/testConvert.java文件,將pdf文檔轉(zhuǎn)存為tif文件拒垃,并存儲在./data/img_tif/目錄下停撞;
- 運(yùn)行./OCR_Process/OCRMain.cpp文件,截取box并調(diào)整字符圖片位置悼瓮,box文件存儲在./data/txt_box/目錄下戈毒,tif存儲在./data/adjust_img_tif/目錄下;
- 運(yùn)行./boxfile_update.py腳本横堡,合并字符與box信息埋市。
這樣,Tesseract的輸入數(shù)據(jù)就已經(jīng)生產(chǎn)好了命贴。