众所周知世界上有两种书写方向,横的和竖的。个人兴趣上偶尔会读一些竖排的扫描版书籍,感觉非常不习惯,于是就想着有没有这种重排扫描版书籍文字方向的软件……结果上网一看,tm居然没有。那就自己写一个吧。
纵书的文字怎么弄成横书的?理论上很简单,把每个字一个个重新横着放就行了。说得机械一点,就是把每个汉字顺时针旋转九十度,然后再把书页横过来看就行了。
关键是怎么把汉字顺时针旋转九十度。因为计算机是不认识纸页上”字“ 的概念的。不,OCR不行,不少扫描书籍分辨率感人,OCR没有人脑那种上下文脑补能力,实际效果一塌糊涂。
于是我想到了Kindle排版软件k2pdfopt的文本回流(reflow)技术,也就是把在不OCR的前提下,把每个文字切成块再处理,这就有点像强行把雕版印刷弄成活字印刷。
但是问题是在识别。微博上的Libre盖子也提到过他为了方便阅读电路设计图,用OpenCV和python做了一个旋转(英语和数字)文字方向180度的小工具。不过他没有放代码出来,于是我自己上网去找,发现还真有一个现成的用OpenCV的竖排古籍文字识别的side project可以借鉴。
我通读了一下代码,大体来说作者是用投影法,也就是计算x和y方向的像素权重。确定什么地方是文字,什么地方是空白的文字间隙。对于竖排文字呢,就是先纵向投影,确定两列文字间的分隔点,然后再把每一列单独拿出来横向投影,确定单字和单字之间的分割点。这样就能框定出每个汉字的区域。
这种算法虽然简单,但意外地很有效,不用上神经网络这种牛刀,而且效果比OpenCV自带的字块腐蚀/扩张算法要好得多(不会出现过度分割);投影法对于不会互相交叉的印刷体文字效果很好。
好,那么这个切割算法怎么拿来干今天这件事呢?很简单,我需要增加的代码无非就是把识别到的每个文字字块(OpenCV里面叫做兴趣区域 ROI)切下来,都绕着中心点顺时针旋转90度,贴回页面的原来位置就行了。还tm别说,OpenCV就连旋转都要需要用上矩阵映射变换啥的,查代码示例花了我好久。
此外有一些预处理,比如先灰度再二值化,减小页面污渍的干扰。
写完代码之后跑了一下,参数调得不要太离谱的话,效果大概是这样的:
以及最后的转换效果是这样的:
很明显那些被错误旋转的书名号看起来很搞笑,不过总体效果还不赖。我这里还有一个早些时候测试日文书页处理的截图,当时还没加上图像二值化+正片叠底的部分,可以看到字块互相重叠就像是修正纸互相覆盖一样。但问题还是在于里面的标点符号
也就是说直角引号(「」和『』)之类,实际上是不能旋转的,不然会向上面这样横躺着非常难看。(我猜这个地方可能还要另外开一个线程来OCR,识别到类似的符号就针对性地不处理。)
当然这上面是很清晰的扫描版,实际上效果会差一些。比如同学发给我测试的古籍,令人发指的分辨率,加上墨渍干扰简直惨不忍睹:
…………
不展开讲了。
此外有一些参数需要操作者来自己调整,比如横向分割阈值。这个阈值太大了,会把”是“”吕“之类的字横切成两半,而这个参数太小的话又会把两个字识别成一个。我就把这个阈值参数叫做“吕切系数”了,类似地,还有”吅切系数“。这个参数可以通过命令行传入。这和有人热爱开手动挡的车一样,有一些可自己操控的东西是一种乐趣。
为了方便潜在的用户调整这些参数(人还是需要有点梦想的),我用wxpython的可视化UI 设计工具wxformbuilder给软件加上个简单的GUI,主要是可以调阈值并半实时预览了(和命令行版本一样,2~3秒的延迟),此外还写了py2exe的打包脚本,以及浪费一点时间把代码中有关二值化、图像叠合、旋转的部分——之前这部分是OpenCV处理——用PIL库重写了一遍,真的是浪费事件,发现PIL的速度反而没有OpenCV快!那我还是基于OpenCV版来改进程序吧……
上网搜索的时候一看,OpenCV 4都在2019年已经发布了,虽然python里面为了legacy 起见还是”import cv2“。OpenCV3和4有很多高大上看不懂的特性比如支持CUDA并行处理,还可以用OpenML加速啥的。不了不了我觉得还是不要碰这个领域……
虽然现在还是缓慢迭代期(图形化界面甚至还不能用),但总有一天会release的,对吧?(代码现在还没开源,等修得差不多了连同可执行文件一起放出来)
顺便一提这个工具目前叫做”Tateyoko“,也是日语里的”纵横“。我那饱读故纸堆的大学同学建议可以叫做”梨枣“,搜了一下,
“旧时刻书制版多用梨木或枣木,故以“梨枣”作书版的代称。
还挺有……内涵的?