银行/信用卡识别全代码详细注释(Python)

直接上源码,需要的copy,运行结果会在原图上打印出来,卡号也会在控制台上打印出来,注释很详细的,可以先看看,不懂的可以在下面留言,博主看到了会第一时间讲解的。

import numpy as np
import cv2
import myutils # 这是另一个py文件,代码贴在下方


# 绘图展示
def cv_show(name, img):
    cv2.imshow(name, img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


# 读取图像进行灰度处理,这里没有输出原图
img = cv2.imread('C:\\Users\\86151\\desktop\\test02\\template02.png')
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 对图像的二值化处理需要输入灰度图
cv_show('gray', ref)
# 进行二值化处理
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]
cv_show('2', ref)
# 计算轮廓 contours:函数处理的图像 cnts:轮廓的点集   hierarchy:各层轮廓索引
contours, cnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, cnts, -1, (0, 0, 255), 3)  # 这里使用了原本图像进行了轮廓描边
cv_show('img', img)
# 给每组数字画矩形边,使用函数boundingRect() 返回的是四个值x,y,w,h分别是(矩形左上角点的坐标,还有矩形的宽度和高度)
# 用boundingBoxes来接受这四个值,这个变量为一个列表
boundingBoxes = [cv2.boundingRect(c) for c in cnts]  # cnts就是每个轮廓的序号列表
# 将每个矩形的点集(列表)与其(左上角点坐标和高、宽)(元组)一起组成了新的列表 ,
# 然后按照升序排序,也就是将数字0~9的矩形位置进行了升序排序,有利于后续匹配字样时候的查找
cnts = myutils.sort_contours(cnts, method='left-to-right')[0]

digits = {}  # 存储模板

# i表示矩形的序号,从0开始,刚好可以代替0~9着十个数字
for (i, c) in enumerate(cnts):
    (x, y, w, h) = cv2.boundingRect(c)
    # ref是二值化处理图像 roi存入了所有包围中最小的框
    roi = ref[y:y + h, x:x + w]
    # 将模板扩大成一定大小的数字,后续输入的数字也要一样大
    roi = cv2.resize(roi, (57, 88))

    digits[i] = roi
# 返回一个9*3的矩形和5*5的正方形元素,这就是卷积核大小,用在腐蚀/膨胀操作
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))

# 设置卷积核的大小,进要输入银行卡的读取
image = cv2.imread('C:\\Users\\86151\\Desktop\\test02\\card03.png')
image = myutils.resize(image, width=300)

# 进行灰度化
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv_show('image', gray)

# 进行礼帽操作,morphologyEx只是形态学变换函数,礼貌操作指的是:MORPH_TOPHAT
# 顶帽:原图像-开运算图,突出原图像中比周围亮的区域,为了更好的找出数字组
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) # 顶帽:原图像 - 开运算图像
cv_show('tophat', tophat)

# 进行sobel算子操作 ksize=-1相当于用3*3的卷积核进行筛选ll(内置的卷积核)
gradx = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1) # 检索图像的边界,gradx是经过Sobel算子处理后的图像的像素点矩阵
gradx = np.absolute(gradx) # 对数组中每一个元素求绝对值
(minVal, maxVal) = (np.min(gradx), np.max(gradx)) # 找到最大边界差值和最小边界插值
gradx = (255 * ((gradx - minVal) / (maxVal - minVal)))  # 归一化公式,将图像像素数据限制在0-1之间,便于后续的操作
gradx = gradx.astype("uint8") # 将gradx的矩阵元素改为数据类型uint8,一般图像的像素点类型都是uint8
print(np.array(gradx).shape)  # 打印gradx的shape
cv_show('gradx', gradx)

# 进行闭操作,把相近的数字连在一起 闭操作:先腐蚀后膨胀
gradx = cv2.morphologyEx(gradx, cv2.MORPH_CLOSE, sqKernel)
cv_show('gradx', gradx)

# 让opencv自己寻找合适的阈值,然后进行处二值化理
thres = cv2.threshold(gradx, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show('thres', thres)

# 再进行一次闭操作 让图片更圆满
thres = cv2.morphologyEx(thres, cv2.MORPH_CLOSE, sqKernel)
cv_show('thres2', thres)

thres = cv2.morphologyEx(thres, cv2.MORPH_DILATE, sqKernel)
cv_show('thres2', thres)

thres = cv2.morphologyEx(thres, cv2.MORPH_DILATE, sqKernel)
cv_show('thres2', thres)

# 计算轮廓,标出原图片中的数字
contours, threshCnts, hierarchy = cv2.findContours(thres.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 在原图进行展示
cnts = threshCnts # 代表图像轮廓的点集
cur_img = image # image:银行卡原图
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 (2 < ar) and (ar < 6.0):
        if (30 < w < 65) and (10 < h < 40):
            # 符合我们需要的条件,留下来,保存到locs数组
            locs.append((x, y, w, h)) # 向数字中加入这个找到的模板
# 根据四组模板的不同横坐标位置进行排序,按照升序
locs = sorted(locs, key=lambda x: x[0], reverse=False)
output = []

# 遍历轮廓中的每个数字
for (i, (gx, gy, gw, gh)) in enumerate(locs):
    groupOut = []  # 用来存储模板匹配的最大得分

    # 将四个小组的每一组分别展现出来,gx,gy,gh,gw是近似矩形的左上角顶点坐标以及高度和宽度
    group = gray[gy - 5:gy + gh + 5, gx - 5:gx + gw + 5]
    cv_show('small_group', group)

    # 对小组图像进行阈值(二值化)处理
    group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
    cv_show('small_group_2', group)

    # 找到每小组中的单个数字轮廓的点集 group.copy()指的是每一个小组(包含着四个数字)
    contours, cnts, hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = myutils.sort_contours(cnts, method="left-to-right")[0]

    # 利用找到的点集合展现出每一个数字
    for c in cnts:  # 一个c就是一个具体数字的点集,cnts是四个数字的点集
        (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 = []
        # 利用近似矩阵的方法,将需要匹配的数字与我们先前设置好的十个数字的模板进行匹配,计算得分,加入到score[]
        for (digit, digiFact) in digits.items(): # digits是模板的十个数字的字典集合
            result = cv2.matchTemplate(roi, digiFact, cv2.TM_CCOEFF) # digiFact就是模板 result是一个矩阵
            min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) # 最小值、最大值、最小值位置、最大值位置

            scores.append(max_val)
            # argmax():取出列表中最大值对应的索引
        groupOut.append(str(np.argmax(scores))) # groupOut中一个数字(总共数字是银行卡上所有的数字)就是一个数字匹配十个模板的最大的分数
        # 将结果画出来
        cv2.rectangle(image, (gx - 5, gy - 5), (gx + gw + 5, gy + gh + 5), (0, 0, 255), 2)
        cv2.putText(image, "".join(groupOut), (gx, gy - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
        # 控制台输出卡号,别忘了开头部分我们已经把模板的实际数值与他们的位置对应起来了,比如在第三个位置的模板数字就是数字3
        # groupOut里面装的又是每个数字的最大匹配分数的  模板  所在的位置,那么也就代表着数字实际的值
    output.extend(groupOut)

print("Credit Card #: {}".format("".join(output)))
cv_show('result', image)

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 = [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))

    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

运行结果:
在这里插入图片描述
控制台上:
在这里插入图片描述

SERENDIPITY

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值