《深度学习》【项目】OpenCV 信用卡号识别

目录

一、自定义命令行参数

1、什么是自定义命令行参数

2、自定义命令行参数的类型

        1)位置参数(Positional Arguments)

        2)可选参数(Optional Arguments)

        3)子命令(Sub-commands)

3、定义参数时注意

4、代码案例

运行方法:

1)pycharm中右击鼠标,打开修改运行配置

2)终端运行

二、项目实施

1、案例图片

2、自定义参数

3、处理模版图像

运行结果:

4、识别数字轮廓

运行结果:

5、处理信用卡图片

1)读取图片

运行结果:

2)顶帽操作突出亮区域

运行结果:

6、定位卡号轮廓

运行结果:

7、识别卡号的每个数字

运行结果:

8、使用模板匹配,计算匹配得分,并绘制

运行结果:

9、自定义模块myutils.py代码


一、自定义命令行参数

1、什么是自定义命令行参数

        自定义命令行参数是指在编写命令行程序时,由程序开发者定义并期望用户通过命令行界面(CLI)输入的参数。这些参数允许用户向程序传递额外的信息或配置,以便程序能够根据这些信息执行不同的操作或行为。

        在Python中,使用argparse模块可以方便地定义和解析自定义命令行参数。argparse模块提供了丰富的功能来定义参数的类型、默认值、是否必需等属性,并能够在用户没有按预期提供参数时给出友好的帮助信息。

2、自定义命令行参数的类型

        1)位置参数(Positional Arguments)

                位置参数是按照位置顺序提供的参数。在命令行中,它们紧跟在程序名称之后,不需要使用---前缀。位置参数的数量和顺序通常是固定的,用户必须按照程序的要求提供它们。

        2)可选参数(Optional Arguments)

                可选参数,也称为选项或开关,是可选提供的参数。它们通常使用--(长选项)或-(短选项)前缀来标识。可选参数可以指定默认值,这样即使用户没有提供它们,程序也能正常运行。可选参数还可以指定为必需的,这样如果用户没有提供它们,程序就会报错或显示帮助信息。

        3)子命令(Sub-commands)

                子命令允许程序具有多个相关的功能,每个功能都通过一个特定的子命令来调用。子命令是可选参数的一种特殊形式,它们通常用于实现类似git这样的命令行工具的复杂功能。

3、定义参数时注意

  • 参数名称:选择一个简短、直观且易于理解的参数名称。
  • 参数类型:确定参数应该接受的数据类型(如字符串、整数、浮点数等)。
  • 默认值:为可选参数指定一个默认值,以便在用户没有提供参数时程序能够正常运行。
  • 是否必需:确定参数是否必需提供。对于必需参数,如果用户没有提供,程序应该报错或显示帮助信息。
  • 帮助信息:为每个参数提供清晰的帮助信息,以便用户了解参数的作用和用法。

4、代码案例

import argparse  
  
# 创建ArgumentParser对象  
parser = argparse.ArgumentParser(description='This is a sample script.')  
  
# 添加位置参数  
parser.add_argument('name', type=str, help='Your name')  
  
# 添加可选参数  
parser.add_argument('--age', type=int, default=25, help='Your age')  
parser.add_argument('-c', '--city', type=str, help='Your city')  
  
# 解析命令行参数  
args = parser.parse_args()  
  
# 使用解析后的参数  
print(f'Hello, {args.name}!')  
print(f'You are {args.age} years old.')  
if args.city:  
    print(f'You live in {args.city}.')
运行方法:
1)pycharm中右击鼠标,打开修改运行配置

输入参数,格式与定义时一致:

2)终端运行

打开终端,输入python + 代码地址,再输入参数内容,注意参数顺序

二、项目实施

1、案例图片

2、自定义参数

定义参数内容为待识别的信用卡号,以及0-1的数字模版

import numpy as np
import argparse   # py内置参数,用于自定义命令行参数
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")  # 定义一个参数,命名为i或image,require=True表示参数是必须参数,help表示提示作用
ap.add_argument('-t',"--template",required=True,help="path to template OCA-A image")  # 同样定义一个必须参数t或template
args = vars(ap.parse_args())  # parse_args表示解析命令行参数,默认从sys.argv读取,得到一个命名空间,vars表示将这个空间转换为字典类型


FIRST_NUMBER = {   # 定义一个字典,用于存放信用卡类型的标识
    "3":"American Express"
    ,"4":"Visa"
    ,"5":"MasterCard"
    ,"6":"Discover Card"
}

3、处理模版图像

img = cv2.imread(args["template"])  # 读取图片,args["template"]为上述定义的参数用法的字典
cv_show('img',img)  # 调用自定义函数用法,展示原图

ref = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)   # 灰度图
cv_show('ref',ref)
# 对灰度图进行二值化处理,灰度值大于10转变为255,小于10转变为0,cv2.THRESH_BINARY_INV表示反二值化,即黑白颠倒(小于10的转变为255,大于10的转变为0)
# 返回两个参数,第一个是阈值10,一个是二值化图像,使用索引[1]只返回后者
ref = cv2.threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1]
cv_show('ref_thr',ref)
运行结果:

4、识别数字轮廓

# 使用cv2.findContours识别轮廓,ref.copy()表示生成图像副本其为待识别图像
# 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)  # 绘制轮廓,在原图上绘制轮廓,轮廓集为refCnts,-1表示绘制所有轮廓
cv_show('img',img)

refCnts = myutils.sort_contours(refCnts,method="left-to-right")[0]  # 调用自定义模块中的方法sort_contours对轮廓图进行排序返回索引对应的值和,利用索引[0],返回排序后的轮廓和轮廓的边界信息
digits = {}    # 定义一个空字典,保存模板中每个数字对应的像素值
for (i,c) in enumerate(refCnts):  # 使用numerate将每一个轮廓组成一个索引序列,返回轮廓索引和轮廓,此时i值表示轮廓中的值0-9
    (x,y,w,h) = cv2.boundingRect(c)  # 计算轮廓外接矩形,返回坐标和高宽
    roi = ref[y:y + h,x:x + w]    # 裁剪出每个数字对应的轮廓
    roi = cv2.resize(roi,(57,88))   # 缩放轮廓到指定的大小
    digits[i] = roi   # 每一个数字对应每一个模板,将他们存入字典
运行结果:

5、处理信用卡图片

1)读取图片
image = cv2.imread(args['image'])  # 读取图像
cv_show('image',image)  # 展示图像
image = myutils.resize(image,width=300)   # 缩放图像,将原始图像的宽度变为300像素值,高度根据比例自动改变
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)   # 灰度图
cv_show('gray',gray)   # 展示灰度图
运行结果:

2)顶帽操作突出亮区域
# 顶帽操作,实出更明亮的区域,清除背景图,原因是背景颜色变化小,不被腐蚀掉。
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))   # 初始化卷积核,创建一个9x3的卷积核用于腐蚀膨胀等操作,cv2.MORPH_RECT表示结构为矩形
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
# cv2.morphologyEx表示图像形态学操作
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)   # 对信用卡的灰度图进行顶帽操作,cv2.MORPH_TOPHAT表示顶帽,突出亮色,rectKernel表示卷积核,顶帽 = 厡始图像 - 开运算结果(先腐蚀后膨胀)
cv_show('tophat',tophat)  # 展示顶帽后的图片
运行结果:

6、定位卡号轮廓

# """ 找到数字边框- """
# 图像形态学处理,通过闭操作(先膨胀,再腐蚀)将数字连在一起
closeX =cv2.morphologyEx(tophat, cv2.MORPH_CLOSE, rectKernel)
cv_show('gradX',closeX)
# 将灰度图转变为二值化图像,cv2.THRESH_BINARY | cv2.THRESH_OTSU为阈值类型和方法的组合,前者指定二值化,后者自动寻找合适阈值
thresh = cv2.threshold(closeX,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show('thresh',thresh)
# 再来一个闭操作,sqKernel为上述定义的卷积核
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))   # 将符合条件的轮廓存放进列表
运行结果:

7、识别卡号的每个数字

# # 将符合的轮廓从左到右排序
locs = sorted(locs,key=lambda x:x[0])
output =[]
# 遍历每一个对应的索引和轮廓边界信息
for (i,(gX,gY,gW,gH)) in enumerate(locs):  # enumerate用于返回一个可迭代对象的索引和数据,这里是轮廓索引、x坐标、y坐标、宽高
    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]  # 将每个轮廓进行二值化处理(黑白),返回两个值,一个是阈值,一个是图像,索引[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]  # 调用自定义模块中的函数将识别的数字轮廓进行排序,排序依据为x轴大小,即卡面上的先后顺序,返回排序后的轮廓和轮廓边界

    # 计算每一组中的每一个数值
    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))   # 缩放每个数字轮廓
        cv_show('roi', roi)
运行结果:

8、使用模板匹配,计算匹配得分,并绘制

(此处需缩进,在上述for循环下)

        """  使用模板匹配,计算匹配得分  """

        scores = []
        # 在模板中计算每一个得分
        for (digit,digitROI) in digits.items():  # 此处digits为上述定义并存放这数字模版中数字轮廓及其对应的数字代号
            # 模板匹配
            result = cv2.matchTemplate(roi,digitROI,cv2.TM_CCOEFF)  # 对卡号数字轮廓和数字对应的轮廓进行模板匹配,使用相关系数匹配法
            (_,score,_,_) = cv2.minMaxLoc(result)   # 找到上述模板匹配相关系数最大值,只要score,其他返回值忽略
            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)  # 在image图像上绘制矩形,其后参数表示左上角坐标、右下角坐标
    # 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("Credit Card #:{}".format("".join(output)))
cv2.imshow("Image", image)
cv2.waitKey(0)
运行结果:

9、自定义模块myutils.py代码

import cv2
def sort_contours(cnts ,method='left-to-right'):   # 输入参数轮廓列表,以及method,默认排序方式为由左到右
    reverse = False  # 布尔值,用于控制排序的方向
    i = 0
    if method == 'right-to-left' or method == 'bottom-to-top':  # 判断排序方式,以此来更改reverse
        reverse=True
    if method == 'top-to-bottom' or method == 'bottom-to-top':
        i = 1
    boundingBoxes = [cv2.boundingRect(c) for c in cnts]  # 遍历每一个轮廓图,取出轮廓图的x、y、w、h,将这些信息存放到空列表中
    # 将列表轮廓和轮廓信息组合成一个元组的列表,再通过匿名函数排序这个元组列表,排序依据为轮廓数据的第一位x大小,降序方式,返回两个元素,一个是排序后的轮廓列表,一个是轮廓的边界框
    (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)     # 默认为cV2.INTER_AREA,即面积插值,适用于缩放图像。
    return resized

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

菜就多练_0828

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

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

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

打赏作者

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

抵扣说明:

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

余额充值