今天做一个小项目,运用已经学到的知识,做一个银行卡的识别。
银行卡的图像如下
这个银行卡是的数字模板是
现在我的任务是识别中间的16个数字
大致思路是:
1:读取模板,获得每个数字的模板信息
2:读取银行卡图像,分割出中间16个数字的轮廓信息
3:进行图像模板匹配,并在原始图像画出来
代码如下:
import cv2
import numpy as np
dsize = (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 contours
def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
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 / w
dim = (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
# 计算出哪个更加匹配,采用计算相关性系数,值越大越相关,因此找出最大的那个数值的key
number_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