提取《二十四史全译》扫描版中全部原文译文信息,制作文言文-白话文对照数据集。
- 对于已经扫描好的影印 pdf 文件,使用夸克全能王会经过一个相当冗长的再扫描过程,不仅原 pdf 体积暴增,而且识别速度极慢且单次只能上传 500 页。而使用夸克网盘可以批量上传 pdf,然后运用网盘自带的转 docx 工具即可(二者调用的接口应该是相同的),这样速度更快。(夸克扫描王退钱!)
- 解析 docx:用夸克网盘自带的 pdf 转 word 功能得到的 docx 文件与夸克全能王得到的 docx 文件格式有较大差异,其版面恢复也较完善。我猜测是夸克扫描王其实把 pdf 中的每页都先再扫描成一张图片,然后其实使用的是 png 转 word 功能,因此其效果更差。总之对新得到的 docx 文件,其格式有如下特点:
-
分栏段落基本由文本框包裹,这样即便用 word 打开在页面上也保持版面结构,不像之前得到的 docx 文件只依靠段落边距,破坏版面结构。
-
这样做的坏处是,文本并非都是 paragraph 格式,而是存在冗余结构。
例如:
<w:r> <mc:AlternateContent> <mc:Choice Requires="wps"> <w:drawing> <!-- ... --> </w:drawing> </mc:Choice> <mc:Fallback> <w:pict> <v:shape type="#_x0000_t202" filled="f" stroked="f" style="margin-left:749pt;margin-top:0pt;width:205pt;height:112pt;mso-position-vertical:absolute;mso-position-vertical-relative:text;mso-position-horizontal:absolute;mso-position-horizontal-relative:text;mso-wrap-style:square;position:absolute;v-text-anchor:top;"> <w10:wrap type="topAndBottom" /> <v:textbox inset="0pt,0pt,0pt,0pt" style="mso-fit-shape-to-text:t"> <w:txbxContent> <w:p> <w:pPr> <w:wordWrap w:val="on" /> <w:autoSpaceDE w:val="off" /> <w:autoSpaceDN w:val="off" /> <w:spacing w:before="0" w:after="0" w:line="2240" w:lineRule="atLeast" /> <w:ind w:left="0" w:right="0" /> <w:jc w:val="both" /> <w:textAlignment w:val="auto" /> <w:rPr> <w:sz w:val="136" /> </w:rPr> </w:pPr> <w:r> <w:rPr> <w:rFonts w:ascii="宋体" w:hAnsi="宋体" w:cs="宋体" w:eastAsia="宋体" /> <w:sz w:val="136" /> <w:color w:val="000000" /> <w:b w:val="off" /> <w:i w:val="off" /> <w:strike w:val="off" /> </w:rPr> <w:t>许嘉璐</w:t> </w:r> </w:p> </w:txbxContent> </v:textbox> </v:shape> </w:pict> </mc:Fallback> </mc:AlternateContent> </w:r>
注意到其中
<w:r>
内部嵌套了<w:p>
,这种倒置的层级是借助<w:pict>
实现的,因此语法上仍合规。更麻烦的是,<mc>
系的标签在python-docx
中不对应伪类,因而这里面的<w:p>
无法被其doc.paragraph
属性识别。详见 issue。为此只得考虑先将这种冗余结构剔除,然后再导入。
-
- 使用夸克全能王(🤯) OCR 得到 docx
- 解析 docx:
OCR 得到的 docx 并不能完成全部版面恢复,比如居中格式实际上由缩进代替。通过分析我们有如下观察:
- 所有文本都是 paragraph 格式,没有其他特殊格式如 header 等。
- 页眉所在的段落有阿拉伯数字作为强特征,因此可以作为自然分页。
- 分页后,每页段落总是先排文言文,再排白话文。文言文段落群与白话文段落群都只有一个且连续互不交叉。但因为分页错落,同一段内容可能分隔到两页上。
- 每页第一段必然是文言文,最后一段必然是白话文,且这一段白话文是最后所见的文言文的译文。也就是说到每一页结束,文言文与白话文的正文段落数必然相同——(其中小节标题不计入,续篇续上开篇不重复计入)。这将帮助我们确定文言文与白话文的段落群分界。
- 文言文段落群中包含小节标题,小节标题的特征是仅占一行,左缩进不为 0,首行缩进为 None
- 正文段落的特征是左缩进为 0:其中开篇首行缩进不为 0,但续篇首行缩进为 None。所谓续篇就是被分隔到后一页上的段落。
- 我们需要识别并保留的文本格式及其相应特征:
类 子类 特征 1: 左缩进 特征 2: 首行缩进 其他特征 举例 页眉 页眉 - - 包含阿拉伯数字 1 标题 卷标题 0 None 与页眉在同一 section 中 史記卷一 章标题(标准化) 很大 None 与页眉在同一 section 中 本紀第一 章标题(原题) 很大 None 与页眉在同一 section 中 五帝本紀 小节标题 较大 None 夹杂于文言文正文中 黄帝 正文 开篇 0 较大 每页文言正文与白话正文段落数相同 黄帝者,少典之子,姓公孫,名曰軒轅…… 续篇 0 None 于涿鹿之阿。遷徙往來無常處,以師兵爲營衛……
-
版面分析 (YOLOv8) 参考 jiangnanboy/layout_analysis
很快,效果也好。原仓库提供两个模型,轻量 yolov8n 与中量 yolov8m。GPU 每张图检测约 10ms。 中量版划分更细但有识别区域嵌套问题,最终选择轻量版。 不过有些排布较近的段落没有完全分割开来,因此需要后处理作文本行分割与单字分割。
弃用路线
-
layout-parser:detectron2 效果不好,主要是因为它没有在中文数据集上进行预训练。而中文数据集本身就比较少,这也增加了难度。
该项目发完文章就不维护了,沽名钓誉耳。publish or perish?
-
ppstructure:非常垃圾。环境难配,屎山文档。
能把文档也写成屎山,代码就更不用看了。这就是百度!
-
-
版面分析后处理
-
文本行分割
-
单字分割
为什么采用单字分割?因为我想得到单字的概率值。此外由于需要重新训练模型,我手上又没有现成繁体中文数据集,只好自己制作,而单字数据集制作较容易。此外,网上一些 现成工具 的单字识别效果很好。尽管它们不开源,但说明单字识别非常值得一试。
后处理没啥说的,纯粹雕花。因为缺乏数据集,所以作端到端的文本识别几乎不可能。
-
-
文字识别
我们采用单字识别路线:按照 AstarLight/CPS-OCR-Engine 的思路走。走单字识别路线的 OCR 实在太少了,根本搜不到,如同大海捞针。
- 数据生成
-
确定字表:取古籍印刷通用字規範字形表(2021 年)与 GBK 编码字符交集,这是因为按照原书印刷时间(2004 年),当时出版社使用的应该还是 GBK 编码字体,同时我们排除不应在古籍中出现的非大陆标准的中文字符(如日文、韩文汉字等)。
-
确定字体:通过 相当粗糙的识别,原书使用字体应为:
- 方正书宋 GBK:用于章名与白话文正文
- 方正仿宋 GBK:用于文言文正文
- 方正黑体 GBK:用于卷名与小节标题
- 方正楷体 GBK:用于页眉(忽略不计)
- 方正准圆繁体:用于章名 准圆繁体免费非商用。其余均可免费商用。
-
合成单字数据集: 使用 脚本 进行数据增强。
python gen_printed_char.py --out_dir ./data/chars --font_dir ./data/fonts/training --dict_path ./data/dicts/labels.pkl --width 64 --height 64 --margin 8 --rotate 30 --rotate_step 1 --need_aug
此外字体方面的数据增强如下:
- 书宋、仿宋:全部字符(共 12328 个字符)
-
- 数据生成
- 虽然验证集上识别率很高,但实际识别率仍然很低。猜测原因有二:1. 类别过多。2. 数据增强不符合实际数据噪声(旋转、盐噪声),实际得到单字分割的文本经过校正后旋转偏移小、同时不存在盐噪声——即扫描件只会引入蚀刻噪声,应当考虑用形态学随机在边缘像素作蚀刻)
-
改变数据增强方法,缩小支持字符类别(一、二级核心字),重新训练数据。
-
补全版面重建代码
RTX2000 Ada 8G