信用卡卡号识别

信用卡卡号识别

大致思想:

​ 1)先为信用卡上的数字设计一个模板图像(不同信用卡上的数字会有差异,按实际情况进行处 理),以便于之后进行模板匹配

​ 2)对模板图像进行外轮廓检测并得到当前外轮廓的外接矩形

​ 3)对信用卡图像进行外轮廓检测,如下图信用卡的卡号是分为了四组,每组四个数字,故可以通过 图像的闭操作等相关操作对该信用卡的卡号进行分组处理(四大组)

​ 4)得到信用卡的外接矩形,并根据外接矩形的宽高比例的差异排除其它无用的轮廓信息

​ 5)使用for循环,将各组中的四个数字再次进行如上的预处理操作;特别注意要将模板中的数字框 外接矩形与信用卡中数字框的外接矩形resize成同样大小

​ 6)使用for循环依次进行模板匹配

模板图像:

信用卡卡号识别_第1张图片

​ numbers.png

信用卡图像:

信用卡卡号识别_第2张图片

​ creditcard.png

1.导入需要的工具包

import cv2
import numpy as np
import myutils  #另一个py文件

2.设置一个展示函数

def cv_show(name,img):
	cv2.imshow(name,img)
	cv2.waitKey(0)
	cv2.destroyAllWindows()

3.读入模板图像并进行一系列预处理

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

4.读入信用卡图像并进行一系列预处理

#初始卷积核,默认的卷积核大小为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)

5.保留卡号信息的轮廓,排除无关轮廓

#遍历所有的轮廓,按照宽高比将需要的四个数字的轮廓提取出来
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])

6.进行模板匹配

上述操作完成后,有用的卡号轮廓信息已经单独保存下来。将信用卡的卡号信息分为四个大组,每个大组中又分为四个小组,使用嵌套的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)

7.myutils.py文件

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

8.展示最终的结果

print('credit card type is:{}'.format(FIRST_NUMBER[output[0]]))
print('credit card #:{}'.format(''.join(output)))
cv_show('image',image)

信用卡卡号识别_第3张图片

上述内容是通过观看相关课程以及一些博主的内容进行的总结,这都是对图像基本操作的简单运用,但由于其中涉及到的知识还需要额外拓展,所以大家可以根据自己的情况进行学习

你可能感兴趣的:(python,opencv,计算机视觉)