基于Python skimage的表格识别程序

1. 开发环境

一提到数字图像处理编程,可能大多数人就会想到matlab,但matlab也有自身的缺点:

  1. 不开源,价格贵
  2. 软件容量大。一般3G以上,高版本甚至达5G以上。
  3. 只能做研究,不易转化成软件。

因此,我们这里使用python这个脚本语言来进行数字图像处理。
要使用python,必须先安装python,一般是2.7版本以上,不管是在windows系统,还是linux系统,安装都是非常简单的。要使用python进行各种开发和科学计算,还需要安装对应的包。这和matlab非常相似,只是matlab里面叫工具箱(toolbox),而python里面叫库或包。基于python脚本语言开发的数字图片处理包,其实很多,比如PIL,Pillow, opencv, scikit-image等。

对比这些包,PIL和Pillow只提供最基础的数字图像处理,功能有限;opencv实际上是一个c++库,只是提供了python接口,更新速度非常慢。到现在python都发展到了3.5版本,而opencv只支持到python 2.7版本;scikit-image是基于scipy的一款图像处理包,它将图片作为numpy数组进行处理,正好与matlab一样,因此,我们最终选择scikit-image进行数字图像处理。

1.1. 需要的安装包

因为scikit-image是基于scipy进行运算的,因此安装numpy和scipy是肯定的。要进行图片的显示,还需要安装matplotlib包,综合起来,需要的包有:

Python >= 2.6
Numpy >= 1.6.1
Cython >= 0.21
Six >=1.4
SciPy >=0.9
Matplotlib >= 1.1.0
NetworkX >= 1.8
Pillow >= 1.7.8
dask[array] >= 0.5.0

1.2. 下载并安装 anaconda

由于依赖的包比较多,安装起来非常费事,我们选择一款集成安装环境就行了,在此推荐Anaconda, 它把以上需要的包都集成在了一起,因此我们实际上从头到尾只需要安装Anaconda软件就行了,其它什么都不用装。

1.3. 简单测试

anaconda自带了一款编辑器spyder,我们以后就可以用这款编辑器来编写代码。我们简单编写一个程序来测试一下安装是否成功,该程序用来打开一张图片并显示。首先准备一张图片,然后打开spyder,编写如下代码:

from skimage import io
img=io.imread('png/01.png')
io.imshow(img)

如果右下角“ Ipython console" 能显示出图片,说明我们的运行环境安装成功。


1.4. skimage包的子模块

skimage包的全称是scikit-image SciKit (toolkit for SciPy) ,它对scipy.ndimage进行了扩展,提供了更多的图片处理功能。它是由python语言编写的,由scipy 社区开发和维护。skimage包由许多的子模块组成,各个子模块提供不同的功能。主要子模块列表如下:

子模块名称 主要实现功能
io 读取、保存和显示图片或视频
data 提供一些测试图片和样本数据
color 颜色空间变换
filters 图像增强、边缘检测、排序滤波器、自动阈值等
draw 操作于numpy数组上的基本图形绘制,包括线条、矩形、圆和文本等
transform 几何变换或其它变换,如旋转、拉伸和拉东变换等
morphology 形态学操作,如开闭运算、骨架提取等
exposure 图片强度调整,如亮度调整、直方图均衡等
feature 特征检测与提取等
measure 图像属性的测量,如相似性或等高线等
segmentation 图像分割
restoration 图像恢复
util 通用函数

用到一些图片处理的操作函数时,需要导入对应的子模块,如果需要导入多个子模块,则用逗号隔开,如:

from skimage import io,data,color

2. 图片中的表格识别

  1. 分割单元格。将图片中的表格全部定位出来,然后按单元格裁剪成一个个小图片,以便后续分析及操作;
  2. 聚焦。其实就是将单元格中的文本区域裁剪出来,将多余的空白去掉;
  3. 大图片的识别。对于大图片用图像相似性的算法(phash+汉明距离)做识别;
  4. 小图片的识别。对于小图片,做字符分割,然后用NN做分类识别;
  5. 识别结果输出到txt;
  6. txt输出到excel。将全部txt按照目标表格的格式,解析输出到excel。

2.1. 分割单元

既然只关心表格区域,所以第一步先将各个单元格拆分出来,截取成一个个小图片。尝试用图像的膨胀、腐蚀来定位表格区域,图像处理包skimage,最后算是定位出了表格区域,也分割出了各个单元格图片,其中部分中间过程的图片如下:



分割单元格这一块的基本流程是:

  1. 读取图像;
  2. 二值化处理;
  3. 横向、纵向的膨胀、腐蚀操作,得到横线图img_row和竖线图img_col;
  4. 得到点图,img_row + img_col=img_dot;
  5. 得到线图,img_row × img_col=img_line(线图只是拿来看看的,后续没有用到);
  6. 浓缩点团到单个像素;
  7. 开始遍历各行的点,将各个单元格从二值图像上裁剪出来,保存到temp文件夹。

2.1.1. 读取图像、二值化

#读取图像
from skimage import io
img=io.imread('png/01.png')
io.imshow(img)

#二值化
bi_th=0.81
img[img<=bi_th]=0
img[img>bi_th]=1
io.imshow(img)

2.1.2. 膨胀、腐蚀操作

# 膨胀腐蚀操作
def dil2ero(img,selem):
    img=morphology.dilation(img,selem)
    imgres=morphology.erosion(img,selem)
    return imgres
# 求图像中的横线和竖线
rows,cols=img.shape
scale=80
col_selem=morphology.rectangle(cols//scale,1)
img_cols=dil2ero(img,col_selem)
row_selem=morphology.rectangle(1,rows//scale)
img_rows=dil2ero(img,row_selem)

2.1.3. 得到点图、线图

# 线图
img_line=img_cols*img_rows    
# 点图
img_dot=img_cols+img_rows
img_dot[img_dot>0]=1
io.imsave('png/tmp/table_dot.jpg',img_dot)

2.1.4. 收缩点团为单位像素点(3×3)

   # 收缩点团为单像素点(3×3)
    def isolate(imgdot):
        idx=np.argwhere(imgdot<1) # img值小于1的索引数组
        rows,cols=imgdot.shape    
        for i in range(idx.shape[0]):
            c_row=idx[i,0]
            c_col=idx[i,1]
            if c_col+1

2.1.5. 获得交点

 # print(dot_idxs.size)
    for m in range(img_dot.shape[0]):
        if m > 100: break
        print('col{}'.format(m),end=" ")
        for n in range(img_dot.shape[1]):
            if img_dot[m][n]==0 :
                print(img_dot[m][n], end= " ")
        print("",end="\n")
        
    dot_idxs=np.argwhere(img_dot<1) # img_dot值等于0的索引数组
    for i in range(len(dot_idxs)):
        for j in range(i,len(dot_idxs)):
            if(dot_idxs[i][0]==dot_idxs[j][0]):
                if(dot_idxs[i][1]>dot_idxs[j][1]):
                    tmp = dot_idxs[i][1];
                    dot_idxs[i][1] = dot_idxs[j][1]
                    dot_idxs[j][1] = tmp

在这里我们可以手动检查dot_idxs数据,观察其中的问题,最容易的可以手动修改dot_idxs数据,这样就可以得到正确的单元。

2.1.6. 检验单元格

def checkTableCols(dot_idxs):
    table_cols = [] #记录每行有几个单元格
    table_rows = 1
    table_row_index = dot_idxs[0][0] #第一行点图y值坐标
    colu_dot = 0 # 单元格数量
    
    for n,indx in enumerate(dot_idxs):
        if indx[0] == table_row_index : # 如果点图y值坐标相同则为同一行
            colu_dot = colu_dot + 1
            print(indx,end=' ')
        else :
            table_cols.append(colu_dot-1) # 将行单元格数量加入table_cols列表
            table_row_index = dot_idxs[n+1][0] #记录下一行位置索引
            colu_dot = 1 # 清0后,换行记录单元格数量
            print(' ',end="\n")
            print(indx, end=" ")
    
    print("")
    for n,v in enumerate(table_cols):
        print('row{}:{}'.format(n,v))
return table_cols

2.1.7. 剪裁图片

def cutImage(img,table_cols,dot_idxs):
    #开始识别单元格
    cell_num = 0
    num = 0
    #if min_row == max_row : # 标准表格 n*n
    for n,v in enumerate(table_cols):
        print('row{}:'.format(n),end= '\n')
        row_dot_num = v + 1
        #cell_num += n * row_dot_num
        for i in range(v):        
            cell_a_index = cell_num + i
            cell_b_index = cell_a_index + 1
            cell_c_index = cell_a_index + row_dot_num
            cell_d_index = cell_c_index +1
            print('cell[{}]:a{} b{} c{} d{}'.format(num+i,dot_idxs[cell_a_index],
            dot_idxs[cell_b_index],dot_idxs[cell_c_index],dot_idxs[cell_d_index]),end= ' ')
            x1=dot_idxs[cell_a_index][1]+3 # 加1去除列边框线
            x2=dot_idxs[cell_b_index][1]
            y1=dot_idxs[cell_a_index][0]+3 # 加1去除行边框线
            y2=dot_idxs[cell_c_index][0]
            print(x1,x2,y1,y2)
            roi=img[y1:y2,x1:x2] # 50~100 行,50~100 列(不包括第 100 行和第 100 列)
            io.imsave('png/tmp/pic_{}.jpg'.format(num+i),roi)
            print('', end="\n")
        cell_num += row_dot_num
        num += v
return num

2.2. 训练语言库——数字为例

  1. 训练环境:首先安装jdk-10.0.1_windows-x64_bin.exe,它是java的运行环境。然后下载工具jTessBoxEditor,它是训练样本的工具。
  2. 样本图像:截图如下图像(越多越好,不过总共也就10个数字)。


  3. 合并图像:运行jTessBoxEditor,菜单栏中Tools--Merge TIFF。在弹出的对话框中选择样本图像(按Shift选择多张),合并名为num.font.exp0.tif文件。
  4. 生成Box file文件:


  5. 文字校正:jTessBoxEditor工具打开num.font.exp0.tif,增加未识别的、修改识别错误的数字。


  6. 生成语言库:


你可能感兴趣的:(基于Python skimage的表格识别程序)