前面写过一篇图像处理的文章,最近一直在处理图像,昏了头。表格识别是基于同事的代码上做个小结吧。
其实如果清楚了怎么实现,其实并不难,只是如果不知道,可能就得走很多冤枉路了,而且还可能做不好。
样例图像:
我们所要做的就是将这个表格结构出来,就是要得到每个单元格及其坐标。
表格识别流程
表格识别看着简单,真做起来细节挺多,先上一个识别流程:
这次只说下面几个功能:
- 检测横竖线
- 曲线聚合
- 求曲线方程
检测横竖线
这个主要使用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])
第一个值是斜率,第二个值是截距。
有了斜率,我们其实还可以做角度纠正。
未完待续。。。