项目地址:https://gitee.com/Shanyalin/pdf-tranlate。
先说一声抱歉,工作原因无法将所有代码开源,但是到目前所有的处理思路及方案都会在本总结篇中说明。
1.关于版面分析
将paddleocr和docbank分别封装成web服务分开部署,开发时两个项目的运行环境会在某历史版本上有冲突,因此各自用了一套虚拟环境来分开运行。web服务通过页面图片识别返回页面的布局信息。
docbank返回的标签有12个(相关脚本中是13个);paddleocr返回的标签可以自定义,预训练模型里返回的标签是5个,且是docbank返回标签的子集。因此可以将两个预训练模型识别出的布局信息合并处理,选择准确度较高的识别结果,也可以对不同的标签采用不同的阈值进行采用。例如paddleocr中list、table的识别结果可以临时提高阈值,当然这跟实际识别需求有关。
2.关于识别特殊字符
特殊字符在pymupdf中通常被识别为65533,实际上\ud800-\udfff(即55296-57343)之间的符号在mupdf中都无法识别,部分特殊符号(d800-db7f)是用两个来表示特殊符号的。对于这些无法识别的符号,已经确认可以将指定区域转化为pixmap进行图片提取。我们可以先用占位符替换特殊符号,待翻译完毕后对占位符进行替换,在占位符的位置换上图片输出即可,但是文本输出与图片输出需要分开调用,所以需要计算排版。
3.关于文本整合
起初的思路是依赖mupdf在提取时以block为单位,block内的lines及lines下的spans都是采取默认合并的思路,或者通过观察总结规律来对block进行合并,甚至有专门的对table进行识别的方案。但实践证明这种方案错误率较高。
引入机器学习对版面进行分析后,根据要处理的文本来灵活调整阈值,尽管仍有不足,但文本整合的效果确实更好了。两个模型预测结果的交叉对比,使得文本在提取后更容易整合。比如在确定文本是table及其他不需要处理的布局内,可以直接将表格内容跳过;在确定两部分文字同属一个布局,即可对文本进行整合。
文本整合绕不开的文本的提取,get_text的不同参数有不同的提取结果,且会对文本的bbox产生影响,如提取过程中英文的某些字符坐标出现异常,进而span、line的bbox受到影响。
一个例子帮助理解下,字符串“span”在提取时返回的结果是:[{‘bbox’: (10,10,16,18), ’c’:’s’, ’origin’: (10,16)},{‘bbox’: (16,10,38,18), ’c’: ‘p’, ‘origin’: (16,16)},{‘bbox’: (22,10,28,18), ‘c’: ‘a’, ‘origin’: (22,16)},{‘bbox’: (28,10,34,18), ‘c’: ‘n’, ‘origin’: (28,16)}],那line的bbox是(10,10,38,18)还是(10,10,34,18)呢? (10,10,38,18),实际上我们更希望获得的是(10,10,34,18),(10,10,38,18)可能无法用版面分析结果来进行文本的整合。
就目前的试验而言,对英文采用rawdict进行提取,以char为最小单位,提取整合后以span为基本单位进行后续的整合;对中文采用dict进行提取,以span为基本单位进行后续整合,不必使用char重新整合;Span之后的中英文整合逻辑基本一致。
关于rawdict、dict的结构,可查看:TextPage — PyMuPDF 1.19.3 documentation
4.关于文本输出
文本输出包括两部分:一,排版或预排版;二,输出。先说输出,因为排版或预排版是受输出方法影响的。
文本输出常采用shape.insert_textbox和textwriter.fill_textbox.两种方式。这两个方法的源码中都有预排版过程,且都是强耦合,区别在于fill_textbox可以获取最后一个字符的结束位置,而insert_textbox则是返回未输出的部分。
另一个影响输出的问题是字体,pymupdf中内置了不少字体,也可以通过pymupdf-font来引入更多的字体,甚至是引入自定义字体。在部分情况下一种字体很难完成整个pdf的文本输出,尤其是数学类学科的文档,数学符号等在常用字体中是没有相关编码的,直接在mupdf中输出结果大概率是65533。因此在输出的时候需要检测当前字体是否可以完整输出,如果不可以就需要对文本切分,更换输入法。
然后再来说说预排版。通过读pymupdf的源码我们可以对它预排版的思路有所了解。简而言之可以理解成语文考试中写作文,提前画好格子,然后往里边排文字。不同的字号对应了同样大小的范围,每行写的字数。
如果考虑了所有输出中出现的问题,那么预排版是可以进行解耦的,当然这会极大得增加复杂性。在充分阅读了源码之后,我们可以对源码进行扩展来适用我们的需求。提取预排版代码用于计算文本的字体大小,扩展自动检测文本是否需要更换字体输出等功能。
5.关于原样输出
可以采用覆盖的思路,尽量在原位置输出,已证实可行,但需要提高版面分析的精度。