├── id_templeate_match.py ├── images ├── credit_card_01.png ├── credit_card_02.png ├── credit_card_03.png ├── credit_card_04.png ├── credit_card_05.png └── ocr_a_reference.png ├── readme.md └── utils.py /id_templeate_match.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import utils 3 | import numpy as np 4 | import argparse 5 | from imutils import contours 6 | 7 | #设置参数 8 | ap=argparse.ArgumentParser() 9 | ap.add_argument("-i","-image",required=True,help="path to input images") 10 | ap.add_argument("-t","--template",required=True,help="path to temlate OCR-A image") 11 | args=vars(ap.parse_args()) 12 | 13 | #指定信用卡类型 14 | FIRST_NUMBER={ 15 | "3":"American Express", 16 | "4":"Visa", 17 | "5":"MasterCard", 18 | "6":"Discover Card" 19 | } 20 | 21 | #绘图展示 22 | def cv_show(name,img): 23 | cv2.imshow(name,img) 24 | cv2.waitKey(0) 25 | cv2.destroyAllWindows() 26 | 27 | #读取一个模板文件 28 | img=cv2.imread(args["template"]) 29 | cv_show("img",img) 30 | #灰度图 31 | ref=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 32 | cv_show('ref',ref) 33 | #二值图像 34 | ref=cv2.threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1] 35 | cv_show('ref',ref) 36 | 37 | #计算轮廓 38 | #cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图) 39 | #cv2.RETR_EXTERNAL只检测外轮廓,cv2.CHAIN_APPROX_SIMPLE只保留终点坐标 40 | #返回的list中每个元素都是图像中的一个轮廓 41 | 42 | ref_,refCnts,hierarchy=cv2.findContours(ref.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) 43 | cv2.drawContours(img,refCnts,-1,(0,0,255),3) 44 | cv_show('img',img) 45 | print(np.array(refCnts).shape) 46 | refCnts=utils.sort_contours(refCnts,method="left-to-right")[0]#排序从左到右,从上到下 47 | digits={} 48 | 49 | ''' 50 | 第一个参数:img是原图 51 | 第二个参数:(x,y)是矩阵的左上点坐标 52 | 第三个参数:(x+w,y+h)是矩阵的右下点坐标 53 | 第四个参数:(0,255,0)是画线对应的rgb颜色 54 | ''' 55 | #遍历每一个轮廓 56 | for(i,c) in enumerate(refCnts): 57 | #计算外接矩形并且resize成合适大小 58 | (x,y,w,h)=cv2.boundingRect(c) 59 | roi=ref[y:y+h,x:x+w] 60 | roi=cv2.resize(roi,(57,58)) 61 | 62 | #每一个数字对应一个模板 63 | digits[i]=roi 64 | 65 | #初始化卷积核 66 | rectKernel=cv2.getStructuringElement(cv2.MORPH_RECT,(9,3)) 67 | sqKernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5)) 68 | 69 | #读取输入图像,预处理 70 | image=cv2.imread(args["image"]) 71 | cv_show('image',image) 72 | image=utils.resize(image,width=300) 73 | gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) 74 | cv_show('gray',gray) 75 | 76 | #礼帽操作,突出更明亮的区域 77 | tophat=cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel) 78 | cv_show('tophat',tophat) 79 | 80 | #计算 81 | gradX=cv2.Sobel(tophat,ddepth=cv2.CV_32F,dx=1,dy=0,ksize=1) 82 | gradX=np.absolute(gradX) 83 | (minVal,maxVal)=(np.min(gradX),np.max(gradX)) 84 | gradX=(255*((gradX-minVal)/(maxVal-minVal))) 85 | gradX=gradX.astype("uint8") 86 | 87 | print(np.array(gradX).shape) 88 | cv_show('gradX',gradX) 89 | 90 | #通过闭操作,(先膨胀,在腐蚀)将数字连在一起 91 | gradX=cv2.morphologyEx(gradX,cv2.MORPH_CLOSE,rectKernel) 92 | cv_show('gradX',gradX) 93 | #THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0 94 | thresh=cv2.threshold(gradX,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1] 95 | cv_show('thresh',thresh) 96 | 97 | #再来一个闭操作 98 | thresh=cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,sqKernel) 99 | cv_show('thresh',thresh) 100 | 101 | #计算轮廓 102 | thresh_,threshCnts,hierarchy=cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) 103 | cnts=threshCnts 104 | cur_img=image.copy() 105 | cv2.drawContours(cur_img,cnts,-1,(0,0,255),3) 106 | cv_show('img',cur_img) 107 | locs=[] 108 | 109 | #遍历轮廓 110 | for (i,c) in enumerate(cnts): 111 | #计算矩形 112 | (x,y,w,h)=cv2.boundingRect(c) 113 | ar=w/float(h) 114 | 115 | #适合合适的区域,根据实际任务来,这里的基本是四个数字一组 116 | if ar>2.5 and ar <4.0: 117 | if(w>40 and w<55) and (h>10 and h<20): 118 | #符合的留下来 119 | locs.append((x,y,w,h)) 120 | #将符合的轮廓从左到右排序 121 | locs=sorted(locs,key=lambda x:x[0]) 122 | output=[] 123 | 124 | #遍历每一个轮廓中的数字 125 | for (i,(gX,gY,gW,gH)) in enumerate(locs): 126 | #initialize the list of group digits 127 | groupOutput=[] 128 | 129 | #根据坐标提取每一个组 130 | group=gray[gY-5:gY+gH+5,gX-5:gX+gW+5] 131 | cv_show('group',group) 132 | #预处理 133 | group=cv2.threshold(group,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1] 134 | cv_show('group',group) 135 | #计算每一个轮廓 136 | group_,digitCnts,hierarchy=cv2.findContours(group.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) 137 | digitCnts=contours.sort_contours(digitCnts,method="left-to-right")[0] 138 | #计算每一组总的每一个数值 139 | for c in digitCnts: 140 | #找到当前数值的轮廓,resize成合适的大小 141 | (x,y,w,h)=cv2.boundingRect(c) 142 | roi=group[y:y+h,x:x+w] 143 | roi=cv2.resize(roi,(57,58)) 144 | cv_show('roi',roi) 145 | 146 | #计算匹配得分 147 | scores=[] 148 | #在模板章中计算每一个得分 149 | for(digit,digiROI) in digits.items(): 150 | #模板匹配 151 | result=cv2.matchTemplate(roi,digiROI,cv2.TM_CCOEFF) 152 | (_,score,_,_)=cv2.minMaxLoc(result) 153 | scores.append(score) 154 | #得到合适的数字 155 | groupOutput.append(str(np.argmax(scores))) 156 | 157 | #画出来 158 | cv2.rectangle(image,(gX-5,gY-5),(gX+gW+5,gY+gH+5),(0,0,255),1) 159 | cv2.putText(image,"".join(groupOutput),(gX,gY-15),cv2.FONT_HERSHEY_SIMPLEX,0.65,(0,0,255),2) 160 | #得到结果 161 | output.extend(groupOutput) 162 | # 打印结果 163 | print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]])) 164 | print("Credit Card #: {}".format("".join(output))) 165 | cv2.imshow("Image", image) 166 | cv2.waitKey(0) -------------------------------------------------------------------------------- /images/credit_card_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y132om/Credit_card_identification/4b7ec965c48761e44f348125b8e2caca658ce297/images/credit_card_01.png -------------------------------------------------------------------------------- /images/credit_card_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y132om/Credit_card_identification/4b7ec965c48761e44f348125b8e2caca658ce297/images/credit_card_02.png -------------------------------------------------------------------------------- /images/credit_card_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y132om/Credit_card_identification/4b7ec965c48761e44f348125b8e2caca658ce297/images/credit_card_03.png -------------------------------------------------------------------------------- /images/credit_card_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y132om/Credit_card_identification/4b7ec965c48761e44f348125b8e2caca658ce297/images/credit_card_04.png -------------------------------------------------------------------------------- /images/credit_card_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y132om/Credit_card_identification/4b7ec965c48761e44f348125b8e2caca658ce297/images/credit_card_05.png -------------------------------------------------------------------------------- /images/ocr_a_reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y132om/Credit_card_identification/4b7ec965c48761e44f348125b8e2caca658ce297/images/ocr_a_reference.png -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 信用卡识别项目说明 2 | ## 开发思路 3 | 将模板文件与读入的图片进行匹配。 4 | ## 步骤: 5 | 6 | 1. 模板图片的操作: 7 | - 读入模板图片 cv2.imread() 8 | - 将模板图片转换为灰度图 cv2.cvtColor() 9 | - 然后转换为二值图 cv2.threshold() 10 | - 做轮廓检测 外轮廓 (要使用图片副本) cv2.findContours() 11 | - 画出轮廓 cv2.drawContours() 12 | - 轮廓排序 思路:根据左上角的点坐标进行排序。 13 | - 遍历每一个模板轮廓。依次将放入集合中。 14 | 2. 读入图片的操作: 15 | - 读入图片 cv2.imread() 16 | - 重置图片大小 cv2.resize() 17 | - 将图片转换为灰度图 cv2.cvtColor() 18 | - 将图片进行礼帽操作,突出更明亮的区域 cv2.morphologyEx(,cv2.MORPH_TOPHAT,) 19 | - Sobel算子,图像梯度计算,进行边缘检测。cv2.Sobel() 20 | - 将上一步计算的图像梯度,进行归一化操作。 21 | - 闭操作 将数字连在一起 cv2.morphologyEx(gradX,cv2.MORPH_CLOSE,rectKernel) 22 | - 自适应阈值 cv2.threshold(gradX,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1] 23 | - 在进行一个闭操作 24 | - 计算轮廓 25 | - 画出轮廓 26 | - 遍历轮廓(四个数字一组) 27 | - 遍历每一个轮廓中的数字。计算每一个轮廓中每一个数字的值 28 | 3. 匹配结果 29 | - 匹配每一个数字 matchTemplate 30 | - 打印结果 -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | #参数一:list结构的,轮廓信息 4 | #参数二:要使用的方法 5 | #返回值:处理过后的轮廓和矩形轮廓 6 | def sort_contours(cnts,method="left-to-right"): 7 | reverse=False 8 | i=0 9 | 10 | if method=="right-to-left" or method =="bottom-to-top": 11 | reverse=True 12 | if method=="top-to-bottom" or method=="bottom-to-top": 13 | i=1 14 | 15 | ''' 16 | cv2.boundingRect(c) 17 | 返回四个值,分别是x,y,w,h; 18 | x,y是矩阵左上点的坐标,w,h是矩阵的宽和高 19 | ''' 20 | boundingBoxes=[cv2.boundingRect(c) for c in cnts] #在轮廓信息中找到一个外接矩形 21 | (cnts,boundingBoxes)=zip(*sorted(zip(cnts,boundingBoxes),key=lambda b:b[1][i],reverse=reverse)) 22 | return cnts,boundingBoxes 23 | 24 | #重置大小,用于比较模板和图像中的数字是否一致 25 | #插值方法如下: 26 | #INTER_NEAREST:最邻近插值 27 | #INTER_LINEAR:双线性插值,默认情况下使用该方式进行插值. 28 | #INTER_AREA:基于区域像素关系的一种重采样或者插值方式.该方法是图像抽取的首选方法,它可以产生更少的波纹, 29 | #但是当图像放大时,它的效果与INTER_NEAREST效果相似. 30 | #INTER_CUBIC:4×4邻域双3次插值 31 | #INTER_LANCZOS4:8×8邻域兰索斯插值 32 | def resize(image,width=None,height=None,inter=cv2.INTER_AREA): 33 | dim=None 34 | (h,w)=image.shape[:2] #(200,300,3) 35 | if width is None and height is None: 36 | return image 37 | if width is None: 38 | r=height/float(h) 39 | dim=(int(w*r),height) 40 | else: 41 | r=width/float(w) 42 | dim=(width,int(h*r)) 43 | 44 | resized=cv2.resize(image,dim,interpolation=inter) 45 | return resized --------------------------------------------------------------------------------