美文网首页
基于Python skimage的表格识别程序

基于Python skimage的表格识别程序

作者: ayusong870 | 来源:发表于2020-05-07 13:52 被阅读0次

    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<cols and c_row+1<rows:
                    imgdot[c_row,c_col+1]=1
                    imgdot[c_row+1,c_col]=1
                    imgdot[c_row+1,c_col+1]=1
                if c_col+2<cols and c_row+2<rows:
                    imgdot[c_row+1,c_col+2]=1
                    imgdot[c_row+2,c_col]=1
                    imgdot[c_row,c_col+2]=1
                    imgdot[c_row+2,c_col+1]=1
                    imgdot[c_row+2,c_col+2]=1
            return imgdot
        img_dot=isolate(img_dot)
        
        io.imsave('png/tmp/table_dot_del.jpg',img_dot)
    

    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的表格识别程序

          本文链接:https://www.haomeiwen.com/subject/hxqhrhtx.html