OpenCV实战项目---信用卡识别

          给定一个信用卡,最终要输出上面的卡号,且需要在原图中把卡号的位置圈出来。这是一个模板匹配任务,如果想让计算机认识数字,我们需要给定一个模板。


        这样, 我们只要找到信用卡上的数字区域,然后拿着数字区域的数字一一与模板进行匹配,看看到底是啥数字,就能识别出来了。 但是,对于信用卡来说我们需要找到它的数字区域呀,对于给定的模板,我们虽然有它的数字区域,但是也得分割成一个个的数字,才能进行匹配工作,所以该任务,就转成了处理信用卡, 处理模板以及模板匹配三个子问题。

 

大致上思路如下:

        使用轮廓检测算法,找到每个对象的大致轮廓以及外接矩形,即先定位到各个对象。找到对象轮廓之后,根据外接矩形的长宽比例,找到中间的这一长串数字部分,由于这个轮廓比较长比较窄,所以还是比较好找的。对于这一长串数字,用形态学操作使其更加突出,让这部分更加精准。接下来,对于这一部分,再次进行轮廓检测,分割成了四个小块,对于每个小块再进行轮廓检测,就能得到每个具体的数字了。对于每个数字,与模板进行匹配(直接有函数可用),就知道是几了。

下面进行代码展示:

import cv2
import numpy as np

# 读取模板图片
img = cv2.imread("./model.png")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 反二值化处理,常见的二值图像一般前景为白色,背景为黑色
img_gray = cv2.threshold(img_gray, 0, 255, cv2.THRESH_OTSU|cv2.THRESH_BINARY_INV)[1]

# 查找图像的轮廓
# CHAIN_APPROX_NONE保存所有轮廓上的点
# RETR_EXTERNAL=0表示值检测外围轮廓
img_g_con = cv2.findContours(img_gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]
img_g_dst = cv2.drawContours(img.copy(), img_g_con, -1, (0, 0, 255), 2)


# 画模板图片的矩形包围盒
lst_1 = []
for con in img_g_con:
    #最大矩形
    (x, y, w, h) = cv2.boundingRect(con)
    #(x,y)起始坐标
    lst_1.append(x)

# 将其包围盒按照x坐标排序,这样就得到了按序排列的0~9模板数字图片,
# (因为查找得到的模板是无序的,排序后可以确认每个模板对应的数字)
(img_g_con, lst_1) = zip(*sorted(zip(img_g_con, lst_1), key=lambda x: x[1], reverse=False))

# 将模板数字图像分割开
digits = {}

# enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标
# sequence -- 一个序列、迭代器或其他支持迭代对象。
# start -- 下标起始位置的值。
for i, con in enumerate(img_g_con):
    (x, y, w, h) = cv2.boundingRect(con)
    roi = img_gray[y:y + h, x:x + w]
    #     (57,88)为调整后的模板图像的大小,也可以设置为其他大小
    digits[i] = cv2.resize(roi, (57, 88))

# 至此,从0到9的模板图像已经保存在了digits中,模板图像就处理完了。

img_card = cv2.imread("./Card.png")
#灰度化
img_card_gray = cv2.cvtColor(img_card, cv2.COLOR_BGR2GRAY)

# 顶帽操作,原始图像-开运算图像,作用:提取噪音、高亮的区域。
kernel = np.ones((5, 5))
tophat = cv2.morphologyEx(img_card_gray, cv2.MORPH_TOPHAT, kernel)

#求图像梯度
grady = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=0, dy=1, ksize=3)
# Sobel计算出来的像素值类似为float,需要将其规范到(0~255),并且将其格式转为Uint8
grady = cv2.convertScaleAbs(grady)
gradx = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=3)
gradx = cv2.convertScaleAbs(gradx)

# 将X方向的梯度和Y方向的梯度融合。
# 只用单方向梯度也可以达到目的
grad = cv2.addWeighted(gradx, 0.5, grady, 0.5, 0)

# 使用Canny边缘检测,也可以得到类似的效果
# 接下来执行闭操作(先腐蚀后膨胀)一是为了去除一些白色小点噪音,二是将单独的数字给连接成一个小区域
kernel = np.ones((3, 3))
grad_close = cv2.morphologyEx(grad, cv2.MORPH_CLOSE, kernel, iterations=1)

grad_dst = cv2.threshold(grad_close, 0 , 255, cv2.THRESH_OTSU)[1]

# 注意到第三个区域还没有连通,继续进行闭操作
grad_close = cv2.morphologyEx(grad_dst, cv2.MORPH_CLOSE, kernel, iterations=8)

# 目标区域已经完全连通,接下来进行轮廓检测,画包围盒,然后再进行筛选
gradcon = cv2.findContours(grad_close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]
dst = cv2.drawContours(img_card.copy(), gradcon, -1, (0, 0, 255), 2)

# 筛选合适的轮廓,筛选参数值是通过经验获取的,可以将各个轮廓的w,h打印后观察。
losc = []
for con in gradcon:
    (x, y, w, h) = cv2.boundingRect(con)
    ar = w/h
    #print(x,y,w,h)
    if ar > 2.4 and ar < 3.5:
        if w > 155 or w < 85:
            continue
        losc.append((x,y,w,h))
# 对矩形包围框按照坐标进行排序,排序目的和前面对模板数字图片的排序目的相同。

sort_losc = sorted(losc, key=lambda x: x[0], reverse=False)

img_part = []
for i in sort_losc:
    x, y, w, h = i[0], i[1], i[2], i[3]
    con = np.array([[[x, y],[x+w, y], [x+w, y+h], [x, y+h]]])
    img_part.append(img_card[y:y+h, x:x+w])
    # cv2.imshow('7', img_card[y:y+h, x:x+w])

# 接下来获取每个数字。
digital = []
def f(img):
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    img_2 = cv2.threshold(img_gray, 0, 255, cv2.THRESH_OTSU)[1]
#    获取轮廓可以进行先Canny检测,再画轮廓,也可以之前求梯度的方法
#    在这里可以直接进行轮廓查找,因为我们需要查找的是每个数字的轮廓,每个数字都是连通的

    imgcon = cv2.findContours(img_2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]
    new_con = []

    # 筛选
    for con in imgcon:
        (x, y, w, h) = cv2.boundingRect(con)
        if h > 35 or w < 15:
            continue
        new_con.append((x,y,w,h))

    # 排序,目的和前面的类似
    sort_con = sorted(new_con,key = lambda x:x[0])

    for i in sort_con:
        x, y, w, h = i[0], i[1], i[2], i[3]
        con = np.array([[[x, y], [x+w, y], [x+w, y+h], [x, y+h]]])
        roi = img_2[y-1:y+h+1, x-1:x+w+1]
        # 注意数字图片区域和模板图片区域要大小要保持一致
        digital.append(cv2.resize(roi, (57, 88)))

for i in img_part:
    f(i)

# 接下来进行模板匹配
lst_2 = []
for img_dig in digital:
    now = []
    for (dig, digroi) in digits.items():
        res = cv2.matchTemplate(img_dig, digroi, cv2.TM_CCOEFF_NORMED)
        now.append(res.item())
    lst_2.append(now.index(max(now)))
    now.clear()

print(lst_2)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

m0_嘉木

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

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

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

打赏作者

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

抵扣说明:

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

余额充值