【初识OpenCV】(7) -- 轮廓检测之银行卡号识别

银行卡号识别

经过了几篇关于轮廓检测的学习,本篇我们来尝试完成对银行卡号的检测识别:

目标,识别出下面银行卡的卡号:

在这里插入图片描述

识别主要流程:

  1. 得到每个数字的信息,为卡号的模板匹配做准备
  2. 定位到银行卡上卡号的位置
  3. 遍历卡号的每个数字,进行模板匹配

详细流程

一、设置参数

import numpy as np
import argparse #python内置库不太熟,自行学习
import cv2
import myutils
"""
-i card1.png
-t kahao.png
"""
ap = argparse.ArgumentParser()
ap.add_argument("-i","--image",required=True,
                help="path to input image")
ap.add_argument("-t","--template", required=True,
                help="path to template 0CR-A image")
args = vars(ap.parse_args())# vars()是Python中的一个内置函数,用于返回对象的属性和值的字典。# 指定信用卡类型
FIRST_NUMBER ={"3":"American Express",
               "4":"Visa",
               "5": "MasterCard",
               "6":"Discover Card"}

二、函数准备

  1. 图像展示
def cv_show(name,img):# 绘图展示
    cv2.imshow(name,img)
    cv2.waitKey(0)
  1. 排序
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]
    (cnts,boundingBoxes)=zip(*sorted(zip(cnts,boundingBoxes),
                                     key=lambda b:b[1][i],reverse=reverse))#zip(*...)使用星号操作符解包排序后的元组列表,并将其重新组合成两个列表:一个包含所有轮廓,另一个包含所有边界框。
    return cnts,boundingBoxes
  1. 调整大小
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)#默认为cV2.INTER_AREA,即面积插值,适用于缩放图像。
    return resized

三、具体步骤

1. 得到每个数字的信息

在这里插入图片描述

将以上图片中每个数字的信息都取出,等待进行模板匹配:

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)

# 计算轮廓:cv2.findcontours()函数接受的参数为二值图,即黑白的(不是灰度图)
# cv2.RETR_EXTERNAL只检测外轮廓,cv2.CHAIN_APPROX_SIMPLE只保留终点坐标
_,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)

refCnts = myutils.sort_contours(refCnts,method="left-to-right")[0] # 排序,从左到右,从上到下
digits = {} # 保存模板中每个数字对应的像素值
for (i,c) in enumerate(refCnts): # 遍历每一个轮廓
    (x,y,w,h)=cv2.boundingRect(c) #计算外接矩形并且resize成合适大小
    roi = ref[y:y + h,x:x + w] # 从二值图上截取区域图
    roi=cv2.resize(roi,(57,88))#缩放到指定的大小
    digits[i]= roi # 每一个数字对应每一个模板
print(digits)
-----------------举例输出:数字0对应它的像素信息
{0: array([[0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       ...,
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0]], dtype=uint8)}

2. 银行卡处理

对银行卡进行顶帽处理,突出比周围区域更亮的区域。

image = cv2.imread(args["image"])
cv_show('image',image)
image = myutils.resize(image,width=300)
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv_show('gray',gray)

# cv2.getStructuringElement()获取结构原件
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
# cv2.MORPH_TOPHAT它通过从原始图像中减去其开运算结果来实现,这样可以突出比周围区域更亮的区域。
tophat = cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel)
cv_show('tophat',tophat)

3. 找到数字边框

目标效果:

在这里插入图片描述

通过闭操作将银行卡的信息都连接在一起,这样就可以得到一块块轮廓的区域:

# 通过闭操作(先膨胀后腐蚀)将数字连在一起
closeX = cv2.morphologyEx(tophat,cv2.MORPH_CLOSE,rectKernel)
cv_show('gradX',closeX)

# 二值化
# cv2.THRESH_OTSU会自动寻找合适的阈值,适合双峰,需要把阈值参数设置成0
thresh = cv2.threshold(closeX,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)

# 计算轮廓
t_,threshCnts,h = 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:
        if (w > 40 and w < 55) and (h > 10 and h < 20): # 符合的留下来
            locs.append((x,y,w,h))

4. 模板匹配

遍历每一个符合的轮廓中的每一个数字,明明是一张图片,如何遍历数字呢?

在轮廓中,截取每一个数字所在的区域,将其同之前得到的digits字典对应的元素进行模板匹配,计算匹配得分,从而得到对应的数字。

# 将符合的轮廓,从左到右排序
locs = sorted(locs,key=lambda x:x[0])
output = []
# 遍历每一个轮廓中的数字
for (i,(gX,gY,gW,gH)) in enumerate(locs):# enumerate()返回序列中元素的索引(从0开始)和元素本身
    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)

    # 计算每一组的轮廓
    group_,digitCnts,hierarchy = cv2.findContours(group.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

    digitCnts = myutils.sort_contours(digitCnts,method='left-to-right')[0]

    # 计算每一组中的每一个数值
    for c in digitCnts:
        # 找到当前数值的轮廓,resize成合适的大小
        (x,y,w,h) = cv2.boundingRect(c)
        roi = group[y:y + h,x:x + w]
        # 因为要用于模板匹配,所以大小要同模板数字大小一样哦
        roi = cv2.resize(roi,(57,88))
        cv_show('roi',roi)

        """-----使用模板匹配,计算匹配得分-----"""
        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)))

     # 画出来
    cv2.rectangle(image,(gX - 5,gY - 5),(gX + gY + 5,gY + gH + 5),(0,0,255),1)

    # cv2.putText()是OpenCV库中的一个函数,用于图像上添加文本
    cv2.putText(image,"".join(groupOutput),(gX,gY - 15),cv2.FONT_HERSHEY_SIMPLEX,0.65,(0,0,255),2)

    # 得到结果  将一个列表的元素添加到另一个列表的末尾
    output.extend(groupOutput)
    
print("Credit Card Type:{}".format(FIRST_NUMBER[output[0]]))
print("Creadit Card #:{}".format("".join(output)))
cv2.imshow("Image",image)
cv2.waitKey(0)
---------------------
Credit Card Type:Visa
Creadit Card #:4020340002345678

在这里插入图片描述

这样我们就识别出了一个银行卡的卡号。

总结

本篇介绍了:

检测识别银行卡卡号的完整流程。

  1. 得到每个数字的信息,为卡号的模板匹配做准备
  2. 定位到银行卡上卡号的位置
  3. 遍历卡号的每个数字,进行模板匹配
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值