1)先为信用卡上的数字设计一个模板图像(不同信用卡上的数字会有差异,按实际情况进行处 理),以便于之后进行模板匹配
2)对模板图像进行外轮廓检测并得到当前外轮廓的外接矩形
3)对信用卡图像进行外轮廓检测,如下图信用卡的卡号是分为了四组,每组四个数字,故可以通过 图像的闭操作等相关操作对该信用卡的卡号进行分组处理(四大组)
4)得到信用卡的外接矩形,并根据外接矩形的宽高比例的差异排除其它无用的轮廓信息
5)使用for循环,将各组中的四个数字再次进行如上的预处理操作;特别注意要将模板中的数字框 外接矩形与信用卡中数字框的外接矩形resize成同样大小
6)使用for循环依次进行模板匹配
numbers.png
creditcard.png
import cv2
import numpy as np
import myutils #另一个py文件
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
img=cv2.imread('numbers.png')
cv_show('img',img)#转为灰度图
ref=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv_show('ref',ref)
#转为二值图(进行轮廓检测时,只能用二值图)
ref=cv2.threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1]
cv_show('ref',ref)
#计算轮廓
refCnts,hierarchy=cv2.findContours(ref.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,refCnts,-1,(0,0,255),3)
cv_show('img',img)
print(np.array(refCnts).shape)
#对轮廓进行排序
refCnts=myutils.sort_contours(refCnts,'left-to-right')[0]
digits={}
for (i,c) in enumerate(refCnts):
#计算外接矩形并resize成合适大小
(x,y,w,h)=cv2.boundingRect(c)
roi=ref[y:y+h,x:x+w]
roi=cv2.resize(roi,(57,88))
digits[i]=roi
#初始卷积核,默认的卷积核大小为3*3
rectKernel=cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))
sqKernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
#读取输入图像,预处理
image1=cv2.imread('creditcard.png')
cv_show('image1',image1)
image1=myutils.resize(image1,width=300)
gray=cv2.cvtColor(image1,cv2.COLOR_BGR2GRAY)
cv_show('gray',gray)
#礼帽操作,突出更明亮的区域
tophat=cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel)
cv_show('tophat',tophat)
#将信用卡上的边缘提取出来
gradX=cv2.Sobel(tophat,ddepth=cv2.CV_32F,dx=1,dy=0,ksize=-1)#ksize=-1相当于3*3的卷积核
gradX=np.absolute(gradX)#绝对值处理
(minVal,maxVal)=(np.min(gradX),np.max(gradX))
gradX=(255*((gradX-minVal)/(maxVal-gradX)))
gradX=gradX.astype('uint8')
print(np.array(gradX).shape)
cv_show('gradX',gradX)
#将靠近的数字连在一起,从而可以划分出四个数字区域
#通过闭操作(先膨胀,后腐蚀)将数字连在一起
gradX=cv2.morphologyEx(gradX,cv2.MORPH_CLOSE,rectKernel)
cv_show('gradX',gradX)
#THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
thresh=cv2.threshold(gradX,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]
cv_show('thresh',thresh)
#再来一个闭操作
thresh=cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,sqKernel)
cv_show('thresh',thresh)
#计算轮廓,并将轮廓画在原始图片上
threshCnts,hierarchy=cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts=threshCnts
cur_img=image1.copy()
cv2.drawContours(cur_img,cnts,-1,(0,0,255),3)
cv_show('img',cur_img)
#遍历所有的轮廓,按照宽高比将需要的四个数字的轮廓提取出来
locs=[]
for (i,c) in enumerate(threshCnts):
#cv2.boundingRect()返回的是一个元组(x,y,w,h)
(x,y,w,h)=cv2.boundingRect(c)
ar=w/float(h)
# 选择合适的区域,根据实际任务来,这里的基本都是四个字一组
if ar > 2.5 and ar < 4.0:
if (w > 40 and w < 55) and (h > 10 and h < 20):
# 符合的留下来
locs.append((x, y, w, h))
# 将符合的轮廓从左到右排序
locs = sorted(locs, key=lambda x: x[0])
上述操作完成后,有用的卡号轮廓信息已经单独保存下来。将信用卡的卡号信息分为四个大组,每个大组中又分为四个小组,使用嵌套的for循环对每个数字进行模板匹配。当然,在模板匹配前还需要对每一组进行预处理操作,如二值化处理、找出各个数字的轮廓、绘制外接矩形等,步骤与上述找出信用卡上的轮廓相同。
output=[]#用于存放各个数字的数组
for (i,(x,y,w,h)) in enumerate(locs):
groupOutput=[]#用于存放当前组中的数据,每进行一组预处理操作之前先清空该数组
#根据坐标提取每一组
group=gray[y-5:y+h+5,x-5:x+w+5]
cv_show('group',group)
#上述已经进行了灰度处理,下面进行二值化处理等操作(与之前类似)
group=cv2.threshold(group,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]
cv_show('group',group)
#计算每一组中各个数字的轮廓
digitsCnts,hierarchy=cv2.findContours(group,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
digitCnts=myutils.sort_contours(digitCnts,method='left-to right')[0]
for c in digitCnts:
(x1,y1,w1,h1)=cv2.boundingRect(c)
roi=group[y1:y1+h1,x1:x1+w1]
roi=cv2.resize(roi,(57,88))
cv_show('roi',roi)
#计算匹配得分
scores=[]
for (digitCnts,digitROI) in digits.items():
result=cv2.matchTemplate(roi,digitROI,cv2.TM_CCOEFF)
(_,score,_,_)=cv2.minMaxLoc(result)
scores.append(score)
#得到最合适的数字,将分值最大的数字添加至groupOutput数组中
groupOutput.append(str(np.argmax(scores)))
#为信用卡上的四个组绘制外接矩形
cv2.rectangle(image,(x-5,y-5),(x+w+5,y+h+5),(0,0,255),1)
#将信用卡的卡号写在卡号处的上方
cv2.putText(image,''.join(groupOutput),(x,y-15),cv2.FONT_HERSHEY,
0.65,(0,0,255),2)
#将每组中的数字添加到整个卡号数字组中
output.extend(groupOutput)
import cv2
#用于排序
def sort_contours(cnts,method='left-to-right'):
reverse=False
i=0
if method=='right-to-left' or method=='bottom-to-top':
reverse=True
if method=='top-to-bottom' or method=='bottom-to-top':
i=1
#用一个最小的矩形,把找到的形状包起来;boundingBoxes是一个包含(x,y,h,w)的元组
boundingBoxes=[cv2.boundingRect(c) for c in cnts]
#将各轮廓从左至右排序
(cnts,boundingBoxes)=zip(*sorted(zip(cnts,boundingBoxes),key=lambda b:b[1] [i],reverse=reverse))
return cnts,boundingBoxes
#用于调整图片大小
def resize(image,width=None,height=None,inter=cv2.INTER_AREA):
dim=None
(h,w)=image.shape[:2]
if width is None and height is None:
return image
if width is None:
r=height/float(h)
dim=(int(w*r),height)
else:
r=width/float(w)
dim=(width,int(h*r))
resized=cv2.resize(image,dim,interpolation=inter)
return resized
print('credit card type is:{}'.format(FIRST_NUMBER[output[0]]))
print('credit card #:{}'.format(''.join(output)))
cv_show('image',image)
上述内容是通过观看相关课程以及一些博主的内容进行的总结,这都是对图像基本操作的简单运用,但由于其中涉及到的知识还需要额外拓展,所以大家可以根据自己的情况进行学习