一.思路与过程(解决问题)
本文采用模板匹配字符识别算法。
首先需要一个模板,数字样式和银行卡号的数字样式差别不大。
例如:
我有一张建行卡和一张农行卡,农行卡的6和9都带勾,对就像前面打出来的一样,但建行的卡却不带勾,有点像随意的手写。
这样就对模板提出了要求,两种卡应该用两种不同的模板。
操作过程中我找不到合适的模板,我又不会P图,所以我直接在word里敲了0123456789,然后调为初号并加粗最后截了个图。
然后又用电脑自带的画图自行给6和9加上了勾(强颜欢笑),以此来作为农行的模板。
1.模板预处理
转换为灰度图-->数字与背景对比不太明显的话可以腐蚀一下(腐蚀了高亮部分,即加粗了数字)-->二值化图像。
然后提取轮廓-->提取roi区域放入字典中,数字与序号(key)对应。
(1)bug解决:contours is not a numpy array,neither a scalar
在检查时想要画出轮廓,确总使报错,type了一下,这时的轮廓是tuple类型
后来发现应该在imutils后执行,这时传入的轮廓是list类型
2.银行卡预处理
转化为灰度图-->利用顶帽操作将银行卡号显示出来(因为Top-hat操作显示了深色背景下的亮区(即信用卡号),但现在在银行卡和过去的不一样了,我见识少,反正我的几张银行卡卡号都是黑色的,而不是白色或者亮色)-->所以我做了取反操作将银行卡号变成白色-->这之后才进行顶帽操作,效果很好。
3.确定卡号具体位置
归一化-->闭运算-->二值化-->闭操作-->提取轮廓-->遍历轮廓,提取符合条件的轮廓(卡号位置)-->将轮廓从左到右排序。
归一化
必须先进行归一化,要不效果差别很大(这地方我还没弄懂)????????????????????????
数据归一化问题是数据挖掘中特征向量表达时的重要问题,当不同的特征成列在一起的时候,由于特征本身表达方式的原因而导致在绝对数值上的小数据被大数据“吃掉”的情况,这个时候我们需要做的就是对抽取出来的features vector进行归一化处理,以保证每个特征被分类器平等对待。
(0,1)标准化:
这是最简单也是最容易想到的方法,通过遍历feature vector里的每一个数据,将Max和Min的记录下来,并通过Max-Min作为基数(即Min=0,Max=1)进行数据的归一化处理:
就像我代码里那样,只不过我放缩到了[0,255]之间。
转化成uint8类型是为了防止溢出,具体看下面的补充知识。
闭运算-->二值化-->闭操作这一连串的操作分析:
先来一个闭操作(先膨胀,再腐蚀)将数字连在一起(因为是几个相邻的数字一组所以先将同组的轮廓连在一起)
再来一个二值化(findContours函数接收二值图像)
最后再来一个闭运算,我感觉是第一个闭运算经过二值化后确保数字的连接。
# 执行gradX 图像的Otsu和二进制阈值,然后是另一个闭运算,对数字分段
#通过闭操作(先膨胀,再腐蚀)将数字连在一起(因为是几个相邻的数字一组所以先将同组的轮廓连在一起)
gradx = cv.morphologyEx(tophat, cv.MORPH_CLOSE, rectKernel)
#THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
thresh = cv.threshold(gradx, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)[1]
thresh = cv.morphologyEx(thresh, cv.MORPH_CLOSE, sqKernel)#再来一个闭运算确保连接
提取轮廓
在确定卡号位置时,刚开始我用matplotlib显示图片,肉眼观测卡号的w和h(简直憨憨。。),后来才意识到根据轮廓序号(直接确定卡号轮廓)直接输出轮廓信息查看aspect ratio(宽高比),w和h。通过我对几家银行的银行卡的观察,现在的银行卡卡号在卡上的位置差不多,又因为轮廓以范围为确定标准,所以确定的信息具有一定的鲁棒性!
不过现在的银行卡号是19位,而且不同的银行卡号的间隔出现位置也不一样,比如建行是前面四个数字一组,有四组,最后一组是三位数字,但是农行的卡是前六位在一起,出现间隔,然后剩余所有位在一起。所以位置的确定具有灵活性,要视实际情况而定!比如我的就是分了两种情况。如下
for (i, c) in enumerate(cnts):
#计算轮廓的边界框,然后利用边界框坐标推导出高宽比
(x, y, w, h) = cv.boundingRect(c)
ar = w / float(h)
#由于信用卡使用的是4组4位数字的固定大小的字体,因此我们可以基于高宽比来修剪潜在的轮廓
if ar > 2.75 and ar <3.5 :
#轮廓可以进一步修剪最小 / 最大宽度和高度
if (w > 65 and w < 80) and (h > 20 and h < 30):
#如果符合则添加到位置列表中
locs.append((x, y, w, h))
elif ar>2.0 and ar<2.5:
if (w > 50 and w < 60) and (h > 20 and h < 30):
locs.append((x, y, w, h))
4.卡号字符分割&模板匹配
根据上面提取的卡号轮廓信息(已分组),在银行卡上的对应位置截取出固定区域,
上来还是之前的套路预处理(二值化-->提取每组轮廓)
然后循环遍历每组,cv.boundingRect取处每个数字的几何信息,resize成和模板一样的大小。
这时候就可以和之前已经预处理好的模板进行匹配,统计得分,得分最高的那个就是对应的数字。
至此,全部思路已梳理完毕。接下来显示输出就行了!
(1)cvtCOLORC报错:error: (-215) scn == 3 || scn == 4 in function cvtColor
因为转化的图片不是三通道或者四通道。。。
(2)模板匹配cv.matchTemplate时报错(Linux下opencv3.4)
error: (-215) (depth == 0 || depth == 5) && type == _templ.type() && _img.dims() <= 2 in function matchTemplate
imread 默认情况下,将图像文件读取为BGR彩色图像。所以img1是BGR彩色图像,边缘是灰度。
你