今天做一个小项目,运用已经学到的知识,做一个银行卡的识别。
银行卡的图像如下
这个银行卡是的数字模板是
现在我的任务是识别中间的16个数字
大致思路是:
1:读取模板,获得每个数字的模板信息
2:读取银行卡图像,分割出中间16个数字的轮廓信息
3:进行图像模板匹配,并在原始图像画出来
代码如下:
import cv2
import numpy as npdsize = (55, 88) # 统一尺度# 展示图像,封装成函数
def cv_show_image(name, img):cv2.imshow(name, img)cv2.waitKey(0) # 等待时间,单位是毫秒,0代表任意键终止cv2.destroyAllWindows()def contours_sort(contours, method=0):# x, y, w, h = cv2.boundingRect(contour) 得到某个轮廓的xy坐标和长度和宽度if method == 0:# 升序,根据 每个轮廓的 外界矩形的 x 坐标位置排序contours = sorted(contours, key=lambda x: cv2.boundingRect(x)[0])else:# 升序,根据 每个轮廓的 外界矩形的 x 坐标位置排序contours = sorted(contours, key=lambda x: cv2.boundingRect(x)[0], reverse=True)return contoursdef resize(image, width=None, height=None, inter=cv2.INTER_AREA):h, w = image.shape[:2]if width is None and height is None:return imageif width is None:r = height / float(h)dim = (int(w * r), height)else:r = width / wdim = (width, int(r * h))resized = cv2.resize(image, dim, interpolation=inter)return resized# ===========================================================
# ================== 第一部分: 加载模板图片,得到其中的关于数字模板信息
# ===========================================================template = cv2.imread('images/babn_number_template.png')# 进行灰度值和二值化转换
template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
# cv_show_image('template_gray', template_gray)# 形成二值图像,因为要做轮廓检测,行成黑底白字
ret, template_thresh = cv2.threshold(template_gray, 127, 255, cv2.THRESH_BINARY_INV)
# cv_show_image('template_thresh', template_thresh)# 进行轮廓提取,得到数字的信息,RETR_EXTERNAL 就是只是需要外轮廓。
template_contours, hierarchy = cv2.findContours(template_thresh,cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 将轮廓排序,位置从小到大就是数字的信息
template_contours = contours_sort(template_contours)# 遍历模板,使用cv2.boudingRect获得轮廓的位置,提取位置对应的图片,与数字结合构造成模板字典
dict_template = {}
for i, contour in enumerate(template_contours):# 画出其外接矩阵,获得其位置信息x, y, w, h = cv2.boundingRect(contour)template_img = template_thresh[y:y + h, x:x + w]# 使用cv2.resize变化模板的大小template_img = cv2.resize(template_img, dsize)# cv_show_image('template_img{}'.format(i), template_img)dict_template[i] = template_img # 获得当前图像的位置和ROI区域,并且保存了下来这个区域# ===========================================================
# ================== 第二部分: 加载目标需要检测的银行卡
# ===========================================================bankcard = cv2.imread('images/bank_card.png')
bankcard_h, bankcard_w, bankcard_c = bankcard.shape # 得到目标图像的长宽信息# 进行灰度值和二值化转换
bankcard_gray = cv2.cvtColor(bankcard, cv2.COLOR_BGR2GRAY)
# cv_show_image('bankcard_gray', bankcard_gray)# 形成二值图像,因为要做轮廓检测,行成黑底白字
ret, bankcard_thresh = cv2.threshold(bankcard_gray, 127, 255, cv2.THRESH_BINARY)
# cv_show_image('bankcard_thresh', bankcard_thresh)# 目标图像很明显有一些小毛刺,因此我们可以做个开运算来取消毛刺儿。
kernel = np.ones((3, 3), np.uint8)
bankcard_open = cv2.morphologyEx(bankcard_thresh, cv2.MORPH_OPEN, kernel, iterations=1)
cv_show_image('bankcard_open', bankcard_open)# 使用cv2.findContours进行轮廓的识别,还是只取外轮廓。
bankcard_contours, hierarchy = cv2.findContours(bankcard_open, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
banck_card_cnts = []
draw_img = bankcard.copy() # 阶段性测试查看使用
# 循环轮廓,将在某个区域的contours加入
for i, contour in enumerate(bankcard_contours):x, y, w, h = cv2.boundingRect(contour)# 数字的x 坐标在 一定的位置范围if 0.5 * bankcard_h < y < 0.6 * bankcard_h: # 目测的,数字在这个该范围内banck_card_cnts.append((x, y, w, h))draw_img = cv2.rectangle(draw_img, pt1=(x, y), pt2=(x + w, y + h), color=(0, 0, 255),thickness=2) # 画出这个矩形,会在原图上画
cv_show_image('rectangle_contours_img', draw_img)
del draw_img# ===========================================================
# ================== 第三部分: 进行目标匹配并画出来
# ===========================================================draw_img = bankcard.copy() # 阶段性测试查看使用
for i, locs in enumerate(banck_card_cnts):x, y, w, h = locs[:] # 保留了在原始图像的位置信息dst_img = bankcard_thresh[y:y + h, x:x + w] # 获得当前图像的位置和ROI区域dst_img = cv2.resize(dst_img, dsize) # 进行resize和模板大小一样,统一size,方便匹配# cv_show_image('rectangle_contours_img', dst_img)tm_vals = {}for number, temp_img in dict_template.items():# 模板匹配,采用计算相关性系数,值越大越相关,带有归一化res = cv2.matchTemplate(dst_img, temp_img, cv2.TM_CCOEFF_NORMED)min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)tm_vals[number] = max_val# 计算出哪个更加匹配,采用计算相关性系数,值越大越相关,因此找出最大的那个数值的keynumber_tm = max(tm_vals, key=tm_vals.get)# 在图像上画出结果来draw_img = cv2.rectangle(draw_img, pt1=(x, y), pt2=(x + w, y + h), color=(0, 0, 255),thickness=2) # 画出这个矩形,会在原图上画cv2.putText(draw_img, str(number_tm), (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.65,color=(0, 0, 255), thickness=2)cv_show_image('final_result', draw_img)
del draw_img
效果如下:
找到数值模板图片的轮廓
找到银行卡图片的轮廓
最终的匹配结果是