明月机器学习系列022:表格识别(上)

2021-10-28 10:04:55 浏览数 (1)

前面写过一篇图像处理的文章,最近一直在处理图像,昏了头。表格识别是基于同事的代码上做个小结吧。

其实如果清楚了怎么实现,其实并不难,只是如果不知道,可能就得走很多冤枉路了,而且还可能做不好。

样例图像:

我们所要做的就是将这个表格结构出来,就是要得到每个单元格及其坐标。

表格识别流程


表格识别看着简单,真做起来细节挺多,先上一个识别流程:

这次只说下面几个功能:

  • 检测横竖线
  • 曲线聚合
  • 求曲线方程

检测横竖线


这个主要使用opencv的功能,知道就知道,不知道就不知道了:

代码语言:javascript复制
def detect_col_line(binary, scale):
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, scale))
    eroded = cv2.erode(binary, kernel, iterations=1)
    return cv2.dilate(eroded, kernel, iterations=1)


def detect_row_line(binary, scale):
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (scale, 1))
    eroded = cv2.erode(binary, kernel, iterations=1)
    return cv2.dilate(eroded, kernel, iterations=1)

上面是两个封装好的横线和竖线识别函数。以横线为例,上面的样例图像输入,出来效果大概如下:

表格的横线都识别出来了,但是这里的粗细并不一致,有些地方粗有些地方细,另外,这图上到底有几个横线呢?(这个问题看起来有点傻,但是那是人类进化几百万而来的)

曲线聚合


我们其实是一眼就能看出来,图上有几个横线,可是程序该如何处理呢,可要注意线条各处粗细不一的,可能还有有倾斜。

比较好的方式,应该是聚类,把同一个横线上的点都聚成一类。聚类算法就直接使用DBSCAN,不复杂:

代码语言:javascript复制
def cluster_lines(data):
    db = DBSCAN(eps=3, min_samples=2).fit(data)
    labels = db.labels_
    n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0)
    return n_clusters_, labels

将横线上的点的坐标直接聚类,主要参数就两个eps和min_samples,这是使用DBSCAN经常需要调的参数。

关于DBSCAN,可以参考这个,这两个算法是很相似的。(聚类算法真是挺好用)

求曲线方程


因为每个曲线各处粗细不一,所以想简单就能得到比较好的曲线方程,还是不容易的。不过如果说穿了,也很简单,直接做曲线拟合就能得出比较好的效果。而要做直线拟合也很简单了,很多现成的python可以实现,如下:

代码语言:javascript复制
def fit_line(points):
    X = points[:, 1]
    Y = points[:, 0]
    params = np.polyfit(X, Y, 1)
    return params

拟合曲线就是拟合一个曲线:y = a*x b

上面的函数返回的参数params就是a和b。拟合原理最简单的,例如最小二乘法,很多书或者文章都会有讲,这不赘述。

拟合结果,参数大概如下:

代码语言:javascript复制
 array([0.00909282, 8.71485819]), 
 array([8.83827464e-03, 1.15591622e 02]), 
 array([7.93308162e-03, 2.78301711e 02]), 
 array([7.92396779e-03, 4.91256108e 02]), 
 array([7.76090442e-03, 6.51120107e 02]), 
 array([7.2484639e-03, 7.2118593e 02])

第一个值是斜率,第二个值是截距。

有了斜率,我们其实还可以做角度纠正。

未完待续。。。

0 人点赞