OpenCV实战项目---信用卡识别
时间:2022-08-18 13:00:00
给定一张信用卡,最后输出上面的卡号,并在原始图片中圈出卡号的位置。这是一个模板匹配任务,如果我们想让计算机知道数字,我们需要给出一个模板。
这样, 我们只需要在信用卡上找到数字区域,然后将数字区域的数字与模板逐一匹配,看看它是什么数字。 然而,对于信用卡,我们需要找到它的数字区域。对于给定的模板,虽然我们有它的数字区域,但我们必须将它们分成数字来匹配工作。因此,这项任务转换为处理信用卡, 处理模板和模板匹配三个子问题。
一般思路如下:
使用轮廓检测算法,找出每个对象的一般轮廓和外部矩形,即先定位到每个对象。找到对象轮廓后,根据外矩形的长宽比,找到中间的长串数字部分。因为轮廓又长又窄,所以很容易找到。对于这学操作使这一长串数字更加突出,使这一部分更加准确。接下来,对于这部分,再次进行轮廓检测,分成四个小块,对于每个小块进行轮廓检测,可以得到每个具体的数字。对于每个数字,匹配模板(直接有函数可用)就知道了。
下面进行代码展示:
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)