OpenCV自适应阀值二值化表格检测方法(python版)

OCR主要分为三个步骤:检测、分割、文字识别。其中文字识别无论是英文还是中文相对比较成熟。只要检测到位,标准的印刷体识别率还是非常高的。

文书OCR检测主要有文字检测和表格检测。文本段落基于行的检测通过DBNet加人为后期纠正能够获得非常高的准确率(此部分以后再写),反而是表格的检测花费了很多时间,网上常规的方法:先对图像进行二值化,然后使用霍夫变换,检测出其中的直线,并在直线中,找到围成一个矩形的区域,将这块区域提取出来就好了。但是难点在于印刷或打印表格线条的过程中清晰度不高,线条粗细和清晰度不一,再次扫描后更加不清晰,同时有些表格里面的文字是多行的,情况非常复杂,必须一个个方框切割下来进行文字识别。例如下面整个图片,表格线条粗细不一,清晰度欠佳,采用霍夫变换直线检测准确率很低,经过多次实验,我把目前稳定性和准确率最高的方法记录下来。这个算法灵活运用了自适应阀值二值化、腐蚀和二次膨胀,让表格乖乖的现形了

网上随便下载的一个原图

(注意:此图有点斜,为了后面更高的检测和文字识别率,需要先摆正,此文不讲了)

第一步,直接以灰度图读入图片

img=cv2.imread("D:\\Python37\\code\\timg.jpg",0)

第二步是关键,自适应阈值二值化处理

binary = cv2.adaptiveThreshold(~img,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,7, -2)

自适应阈值:

      当同一幅图像上的不同部分的具有不同亮度时。这种情况下我们需要采用自适应阈值。此时的阈值是根据图像上的每一个小区域计算与其对应的阈值。因此在同一幅图像上的不同区域采用的是不同的阈值,从而使我们能在亮度不同的情况下得到更好的结果。

cv2.adaptiveThreshold()参数说明,后面两个参数很重要。

参数1:InputArray类型的src,输入图像,填单通道,单8位浮点类型Mat即可。~img表示黑白对调,变成黑底白字,这里也是注意点

参数2:预设满足条件的最大值。指当像素值高于(有时是小于)阈值时应该被赋予的新的像素值

参数3:指定自适应阈值算法。adaptive_method  指: CV_ADAPTIVE_THRESH_MEAN_C 或 CV_ADAPTIVE_THRESH_GAUSSIAN_C。(具体见下面的解释)。

参数4:指定阈值类型。可选择CV_THRESH_BINARY,CV_THRESH_BINARY_INV两种。(即二进制阈值或反二进制阈值)。

参数5:表示邻域块大小(一个正方形的领域),用来计算区域阈值,一般选择为3、5、7......等。因为我们是直线检测,相邻领域都很小,这样能够把大于这个像素的领域删除,例如本文设为7

参数6:参数C表示与算法有关的参数,它是一个从均值或加权均值提取的常数,可以是负数。(具体见下面的解释)。

对参数3与参数6内容的解释:

自适应阈值化计算大概过程是为每一个象素点单独计算的阈值,即每个像素点的阈值都是不同的,就是将该像素点周围B*B区域内的像素加权平均,然后减去一个常数C,从而得到该点的阈值。B由参数5指定,常数C由参数6指定。

参数4中:

CV_ADAPTIVE_THRESH_MEAN_C ,为局部邻域块的平均值。该算法是先求出块中的均值,再减去常数C。

CV_ADAPTIVE_THRESH_GAUSSIAN_C ,为局部邻域块的高斯加权和。该算法是在区域中(x,y)周围的像素根据高斯函数按照他们离中心点的距离进行加权计算, 再减去常数C。

举个例子:如果使用平均值方法,平均值mean为190,差值delta(即常数C)为30。那么灰度小于160的像素为0,大于等于160的像素为255。

第三步识别横线和竖线

rows,cols=binary.shape

scale = 20

#识别横线

kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(cols//scale,1))

eroded = cv2.erode(binary,kernel,iterations = 1)

#由于图像像素质量的原因,一次膨胀不够,往往有些线无法识别,我用二次膨胀。

dilatedcol = cv2.dilate(eroded,kernel,iterations = 2)

scale = 10

#识别竖线

kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(1,rows//scale))

eroded = cv2.erode(binary,kernel,iterations = 1)

#cv2.imshow("Eroded Image",eroded)

dilatedrow = cv2.dilate(eroded,kernel,iterations =2)

#标识交点

bitwiseAnd = cv2.bitwise_and(dilatedcol,dilatedrow)

#标识表格

merge = cv2.add(dilatedcol,dilatedrow)

#识别黑白图中的白色交叉点,将横纵坐标取出

ys,xs = np.where(bitwiseAnd>0)

mylisty=[] #纵坐标

mylistx=[] #横坐标

#通过排序,获取跳变的x和y的值,说明是交点,否则交点会有好多像素值值相近,我只取相近值的最后一点

#这个10的跳变不是固定的,根据不同的图片会有微调,基本上为单元格表格的高度(y坐标跳变)和长度(x坐标跳变),对于多个点,我取x,y中间值mean

i = 0

myxs=np.sort(xs)

tmpmy=[]

for i in range(len(myxs)-1):

  if(myxs[i+1]-myxs[i]>10):

      tmpmy.append(myxs[i])

      mylistx.append(int(mean(tmpmy)))

      tmpmy=[]

  else:

      tmpmy.append(myxs[i])

      i=i+1

mylistx.append(int(mean(tmpmy))) #要将最后一个点加入

i = 0

myys=np.sort(ys)

tmpmy=[]

for i in range(len(myys)-1):

  if(myys[i+1]-myys[i]>10):

      tmpmy.append(myys[i])

      mylisty.append(int(mean(tmpmy)))

      tmpmy = []

  else:

      tmpmy.append(myys[i])

      i = i + 1

mylisty.append(int(mean(tmpmy))) #要将最后一个点加入

cv2.imshow("bitwiseAnd", bitwiseAnd)

cv2.imshow("merge", merge)

cv2.waitKey(0)

cv2.destroyAllWindows()

识别出来的表格,基本已经有模有样了
准确率100%,注意一定要二次膨胀,同时要调整 scale

第四步,根据交叉点分割出一个个小格用于文字识别

#循环y坐标,x坐标分割表格,返回一个个子方框用于文字识别。真正使用过程中是需要知道每个框是第几行第几列的,我这里没有做处理。

    for i in range(len(mylisty)-1):

      for j in range(len(mylistx)-1):

            #在分割时,第一个参数为y坐标,第二个参数为x坐标

            ROI = image[mylisty[i]+3:mylisty[i+1]-3,mylistx[j]:mylistx[j+1]-3] #减去3的原因是由于我缩小ROI范围

            ROIList.append(ROI)

            j=j+1

        i=i+1

    return ROIList

一个程序只能适应一个模板,大致思路差不多。我本来还想做一个自动膨胀次数检测,以自动达到检测目标、自动适应各种清晰度的程序,但是难度有点大,后续有需要再慢慢做了。

最后:感谢深圳的孙老师带我OCR入门

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342