OpenCV信用卡识别

分为三大步:

        1、处理数字模板

        2、处理信用卡上的数字

        3、将信用卡上的数字与模板进行匹配


数字模板如图(要与信用卡上的数字相像)

 信用卡如图

 


导入工具包

import numpy as np
import cv2
import myutils
import myutils as contours

myutils就是包含定义排序函数 和 按比例resize图像大小的函数,在下面介绍

一、处理数字模板

分为三步:

        1、对图像进行预处理(灰度,二值,)

        2、计算找到每个数字的轮廓并排序

        3、将每个数字的小照片剪切后依次保存到一个字典中(键=值)


1、对图像进行预处理

# 定义函数方便调用,绘图展示
def cv_show(name,img):
	cv2.imshow(name, img)
	cv2.waitKey(0)
	cv2.destroyAllWindows()

img = cv2.imread(args["template"])    # 读取一个模板图像
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)

2、找到数字轮廓并排序

  定义排序函数 和 按比例resize图像大小的函数,此部分由工具包引入使用便捷

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 = [cv2.boundingRect(c) for c in cnts] #用一个最小的矩形,把找到的形状包起来x,y,h,w
    (cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
                                        key=lambda b: b[1][i], reverse=reverse))
                                            #b:b[1][i]是按boundingBoxes中x值排序        
    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
boundingBoxes = [cv2.boundingRect(c) for c in cnts]
等价于	boundingBoxes=[]
		for c in cnts:
			boundingBoxes.append(cv2.boundingRect(c))

 画出轮廓外接矩形,返回矩形左上角数据,利用横坐标x大小排序

# 计算轮廓
#cv2.findContours()函数接受的参数为二值图,黑白的(不是灰度图),cv2.RETR_EXTERNAL只检测外轮廓,cv2.CHAIN_APPROX_SIMPLE只保留终点坐标
#返回的list中每个元素都是图像中的一个轮廓

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, method="left-to-right")[0] #排序,从左到右,从上到下

3、将每个数字的小照片剪切后依次保存到一个字典中

digits = {}    #定义一个空字典

# 遍历每一个轮廓
for (i, c) in enumerate(refCnts):        # 数字的下标和数值是一样的
	# 计算外接矩形并且resize成合适大小
	(x, y, w, h) = cv2.boundingRect(c)
	roi = ref[y:y + h, x:x + w]        #轮廓i的外接矩形
	roi = cv2.resize(roi, (57, 88))    #将外接矩形大小调整为一固定大小
    #cv_show('roi',roi)
	# 每一个数字对应每一个模板
	digits[i] = roi            #i是键,模板是值,并且二者大小相同,添加到字典中

至此,数字模板处理完成

 


二、处理信用卡上的数字

分为三步

        1、对图像进行灰度化并且调整大小

        2、对图像进行处理形态学处理和其他操作,使图像中字符连成块状

        3、找到卡号所在位置,并提取轮廓


1、对图像进行灰度化并且调整大小

# 初始化卷积核
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))

#读取输入图像,预处理
image = cv2.imread('Card.jpg')
cv_show('image',image)

image = myutils.resize(image, width=300)        #调整大小
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)    #灰度化
cv_show('gray',gray)

2、使图像中字符连成块状

 礼帽操作、水平梯度、绝对值,归一化、去掉小数、闭操作、二值化、闭操作


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的Scharr滤波器                                                
gradX = np.absolute(gradX)        #求绝对值
(minVal, maxVal) = (np.min(gradX), np.max(gradX))
gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))    #归一化
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 = cv2.threshold(gradX, 0, 255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] #THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
cv_show('thresh',thresh)

thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel) #再来一个闭操作,去除杂质
cv_show('thresh',thresh)

3、找到卡号所在位置,并提取轮廓

# 计算轮廓
threshCnts,herarchy= cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

cnts = threshCnts        #提取图像中的所有轮廓
cur_img = image.copy()
cv2.drawContours(cur_img,cnts,-1,(0,0,255),3)     #画出轮廓
cv_show('img',cur_img)

locs = []    #定义一个空列表

# 遍历所有轮廓
for (i, c) in enumerate(cnts):
	(x, y, w, h) = cv2.boundingRect(c)    # 得到轮廓信息
	ar = w / float(h)    #计算宽与长的比例(不是卡号的话,长宽比一般不一样,可以进行筛选)

	# 选择合适的区域,根据实际任务来,这里的基本都是四个数字一组
	if ar > 2.5 and ar < 4.0 and  (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])    # 将符合的轮廓按横坐标大小从左到右排序这样才和卡号一样

至此,银行卡号四部分轮廓提取完毕。

输入图像                                                                                                                                                                                       灰度化并调整大小


 礼帽操作                                                                         水平图像梯度,绝对值,归一化处理                                                闭操作处理(填充小洞)                    


 二值处理                                                                                   闭操作处理(填充小洞,>_< 好像没什么用)                        画出轮廓

 


 三、将信用卡上的数字与数字模板进行匹配

 分为三步

        1、对得到的数字块轮廓遍历,利用其位置信息得到数字块轮廓在原图像灰度图中的区域截取下来

        2、得到每个小区域中的单个数字

        3、将单个数字与模板中10个数字匹配,得到匹配得分最高的模板图像的索引值


1、对得到的数字块轮廓遍历,将数字块轮廓在原图像灰度图中的区域截取下来

output = []    #定义一个列表,将最终结果依次添加到里面

for (i, (gX, gY, gW, gH)) in enumerate(locs):    # 遍历每一个轮廓中的数字
	
	groupOutput = []    #将每个轮廓中的数字存储起来,存储完添加到最终结果列表中

	# 根据坐标提取每一个组
	group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5]
	cv_show('group',group)
	
	group = cv2.threshold(group, 0, 255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]    #二值处理
	cv_show('group',group)

2、得到每个小区域中的单个数字

(对这些区域进行二值化处理,对区域轮廓按横坐标大小排序,在每个区域里计算每个数字的轮廓信息,得到图像并且resize成数字模板大小)

    # 计算每一组数字的轮廓
	digitCnts,hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
	digitCnts = contours.sort_contours(digitCnts,method="left-to-right")[0]    #对这几组数字轮廓按横坐标大小排序

	# 计算每一组中的每一个数值
	for c in digitCnts:
		(x, y, w, h) = cv2.boundingRect(c)
		roi = group[y:y + h, x:x + w]
		roi = cv2.resize(roi, (57, 88))    #找到当前数值的轮廓,resize成数字模板大小
		cv_show('roi',roi)

3、将单个数字与模板中10个数字匹配,得到匹配得分最高的模板图像的索引值

	
		scores = []    	# 计算匹配得分

		# 在模板中计算每一个得分
		for (digit, digitROI) in digits.items():    	
		
			result = cv2.matchTemplate(roi, digitROI,cv2.TM_CCOEFF)    # 模板匹配
			(_, score, _, _) = cv2.minMaxLoc(result)    #数字越大,分值越高,越匹配
			scores.append(score)

		groupOutput.append(str(np.argmax(scores)))    # 返回scores[]里面最大值的索引添加到列表
	cv2.rectangle(image, (gX - 5, gY - 5),(gX + gW + 5, gY + gH + 5), (0, 0, 255), 1)    #画出外接矩形
	cv2.putText(image, "".join(groupOutput), (gX, gY - 15),cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)    #在外接矩形上面写上匹配的数字

	
	output.extend(groupOutput)    # 得到最终结果

# 打印结果
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Image", image)
cv2.waitKey(0)

完成。


区域数字轮廓                                  二值                                                              ​​​​​​​        单个数字+resize


 


 


 


                                 最终图像

  • 12
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

onlywishes

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值