IText+OpenCv抽取PDF表格

上期我们使用了IText解析PDF,这期我们需要使用OpenCv来识别PDF中的表格,然后用IText抽取单元格中的文本。

本文适用的条件是“标准的表格”,也就是说,没有隐藏任何表格线的表格,当然,更不能是表格的图片,不然IText根本无法抽取文本。如果希望抽取有隐藏表格线的表格,可以试试PDFLux这款软件。

标准的表格:

不适用的表格:

首先,我们需要使用Apache PDFBox将PDF渲染为png图片,注意的是渲染dpi。IText中抽取出的文字的坐标使用的是72dpi。由于72dpi分辨率过低,可能会导致图片识别不准确,推荐使用144dpi进行渲染,即72的2倍,方便后续的坐标转换。

然后,我们使用OpenCv进行处理,按照惯用套路,我们先将图片转为灰度图,然后二值化,最后反色。反色这个步骤对后续至关重要。

网上你们可以搜到使用腐蚀(erode)和膨胀(dilate)来获取横线和竖线的方法,但是大多没有讲原理,也没有讲参数为什么这么设置,这边简要的说一下。首先,腐蚀和膨胀都需要设置一个m*n的模板,然后使用这个模板从图片左上角开始从左往右从上到下迭代计算,腐蚀是将模板覆盖到的最小像素值替换模板中心相似的值,而膨胀是用最大像素值替换模板中心相似的值。

所以,想要获取“横线”,我们就要使用n*1的模板,因为经过“腐蚀”后,由于横线在n*1的区域内全部为白色(最大像素)而得到保留。n越小,能够被保留的横线就越短,当n小到一定程度时,“一”这种文字中的横线也会被保留。我们希望得到一个阈值,能够过滤掉文字中的横线,但是保留表格中的横线。根据实验,n取页面宽度/20是一个比较不错的阈值。同理,我们需要使用1*m的模板来腐蚀图片获得竖线,由于可能存在2行的表格,所以竖线的阈值肯定比横线要小,经过实验发现,页面高度/50是一个比较不错的阈值。

下面是分别用n*1模板和1*m模板腐蚀后的结果。

细心的同学会发现,“腐蚀”后,横线的宽度比原图横线“短”了。这是因为当这些白色点处于横线边缘时,由于模板会覆盖到左侧或右侧的黑点,从而导致该点被置为黑色。针对这个情况,我们需要对图片使用相同的模板进行“膨胀“操作,抵消掉这个副作用。

下面是使用相同模板膨胀后的情况

然后我们对两张图片使用bitwise_or就可以拼出表格线了

有了表格线以后,我们就可以使用findContours获取轮廓信息。findContours功能很强大,既可以获取最表格的外轮廓,也可以获取单元格的轮廓。

public static void findContours(Mat image, List<MatOfPoint> contours, Mat hierarchy, int mode, int method) 

Mat image 是被识别的图片

List<MatOfPoint> contours 是识别出的轮廓信息

Mat hierarchy 是轮廓的层次信息,是一个n*1*4的高维数组,其中n为contours的长度,第三维的4个元素分别是后一个轮廓、前一个轮廓、父轮廓、包含的轮廓在contours中的编号。我们主要关心的是父轮廓。

int mode 是轮廓的检索模式。CV_RETR_EXTERNAL:只检测最外围轮廓。CV_RETR_LIST:检测所有轮廓,但不建立关系。CV_RETR_CCOMP:检测所有轮廓,只建立2层关系。CV_RETR_TREE:检测所有轮廓,建立树形结构关系

int method 是定义轮廓的近似方法。CV_CHAIN_APPROX_NONE:保存轮廓所有点。CV_CHAIN_APPROX_SIMPLE:仅保留轮廓拐点,不保留直线点。

因为我们只需要获取表格外围和单元格轮廓,我们只需要使用mode=CV_RETR_CCOMP,method=CV_CHAIN_APPROX_SIMPLE

获取到轮廓以后,对于每个轮廓,我们使用approxPolyDP和boundingRect获取到轮廓外包矩形。同时,我们过滤掉那些明显不是表格或单元格的轮廓,例如,面积比一个字符都小(10*10),轮廓使用approxPolyDP后,只剩下4个点以下(很有可能就是条横线或者三角形)

然后我们根据hierarchy组织成(表格外轮廓,List 单元格轮廓)的形式。我们虽然知道了这些单元格属于表格,但是我们还需要单元格的排列信息,以及合并单元格的信息。

对于单元格的排列,我们可以先对y坐标排顺序(因为表格横线获取的单元格y坐标都是对齐的),然后对x坐标排顺序。

那么合并单元格的情况怎么办呢?通过观察,我们发现,没有合并的单元格,不会被表格线的延长线“穿过”,被n根横向/纵向的表格线的延长线“穿过”,那么就横向、纵向合并了n+1个单元格。

这样,我们就解决了从PDF识别表格的问题。只需要最后一步,使用IText获取对应坐标范围的文字填充到表格中。

由于IText使用的是72dpi进行渲染的文字的坐标,以及y轴的原点在页面底部,我们需要对OpenCv获取到的坐标进行转换。

x_itext=x_open_cv/2;

y_itext=(height_open_cv-y_open_cv)/2

 

参考资料:

https://blog.csdn.net/yomo127/article/details/52045146

https://www.cnblogs.com/ssyfj/p/9276999.html

https://blog.csdn.net/qq_42887760/article/details/85928182

https://www.jianshu.com/p/884261b5ab5a

https://blog.csdn.net/laobai1015/article/details/76400725

相关推荐
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页