通过网上视频学习了信用卡的数字识别案例,根据自己的习惯和方法对其进行了修改,同时备注了自己对其中一些方法的理解,目前该代码是比较“笨”的方法,后续就持续进行优化,欢迎批评指正,谢谢!。
#python需要安装OPENCV、numpy、argparse和imutils模板才能运行此程序
import cv2 as cv
import numpy as np
import argparse
from imutils import contours
# 图像显示函数
def cv_show(name, image):
cv.imshow(name, image)
cv.waitKey(0)
cv.destroyAllWindows()
# 设置参数
parse = argparse.ArgumentParser()
parse.add_argument('-i', '--image', required=True, help='path to input image')
parse.add_argument('-t', '--template', required=True, help='path to input template')
agrs = vars(parse.parse_args())
###### 模板图像处理流程 #########
#读取图像,并转化为灰度图像
img_t = cv.imread(agrs['template'])
gray_t = cv.cvtColor(img_t, cv.COLOR_BGR2GRAY)
#模板图像二值化处理,threshold方法返回的是两个参数
thresh_t = cv.threshold(gray_t, 10, 255, cv.THRESH_BINARY_INV)[1]
# 计算轮廓,opencv4.0返回的是2个参数,3.0版本是返回3个参数
threshCnts_t, hierarchy = cv.findContours(thresh_t.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 画出轮廓
cv.drawContours(img_t, threshCnts_t, -1, (0, 0, 255), 1)
# 轮廓排序,调用imutilscontours中的sort_contours方法,该方法返回两个参数,第一个为返回轮廓,第二个参数返回轮廓外接矩形
sorted_threshCnts_t = contours.sort_contours(threshCnts_t, 'left-to-right')[0]
# 创建存储每个数字模板的字典
digits = {}
# 遍历轮廓,得到模板中每个数字的单独图像,在处理完成后的图像上提取处理的
for (i,c) in enumerate(sorted_threshCnts_t):
x, y, w, h = cv.boundingRect(c)
roi_t = thresh_t[y:y+h, x:x+w]
roi_t = cv.resize(roi_t, (57,88))
digits[i] = roi_t
#########目标图像处理##########
#读取图像,对图像进行统一的尺寸设置,然后将图像转换为灰度图像
img_i = cv.imread(agrs['image'])
img_i = cv.resize(img_i, (516, 334))
gray_i = cv.cvtColor(img_i, cv.COLOR_BGR2GRAY)
# 初始化卷积核,提取准备后面可能会使用到的卷积核
rectKernel1 = np.ones((9, 3), np.uint8)
sqKernel1 = np.ones((3, 3), np.uint8)
sqKernel2 = np.ones((7, 7), np.uint8)
# 图片形态学处理
# 礼帽处理,该步骤是使图像中阴暗背景里较亮部分变得更亮
tophat = cv.morphologyEx(gray_i, cv.MORPH_TOPHAT, sqKernel2)
# 二值化处理
# cv.THRESH_OTSU,该方法能够自动确定二值化的阈值,故而将阈值设置为0
thresh_i = cv.threshold(tophat, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)[1]
# 循环进行闭操作,具体次数视图像而定,将不够明晰的亮点变得更加的清晰和明显
for i in range(4):
close = cv.morphologyEx(thresh_i, cv.MORPH_CLOSE, sqKernel1)
# 计算轮廓
Cnts, hierarchy = cv.findContours(close.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 遍历轮廓
#设置遍历轮廓过程中需要使用的容器
#图片上数字的位置坐标列表
number_loc = []
#匹配得到的数字列表
match_numbers = []
#遍历计算得到的所有轮廓
for (i, c) in enumerate(Cnts):
(x, y, w, h) = cv.boundingRect(c)
#跟进图片的实际情况,通过Y方向的值和轮廓的高度值来过滤得到想要的数字轮廓,并将其坐标存储至列表中
if 140 < y < 200 and 20 < h < 30:
number_loc.append((x, y, w, h))
#按照X的值从小到大对得到的坐标值元组进行排序
number_loc = sorted(number_loc, key=lambda x: x[0])
#遍历轮廓坐标值构成的列表,循环从处理完成的图像中提取想要的单独数字图像并设置其大小和模板数字图像一致
for (i, (x, y, w, h)) in enumerate(number_loc):
roi_i = close[y:y + h, x:x + w]
roi_i = cv.resize(roi_i, (57, 88))
# 模板匹配
#初始化匹配得分的列表,用了存储与各个数字模板匹配得到的分数,按照TM_SQDIFF规定,去最小值
scores = []
for (j, d) in digits.items():
# 特别注意是roi_i
result = cv.matchTemplate(roi_i, d, cv.TM_SQDIFF)
minVal, maxVal, minLoc, maxLoc = cv.minMaxLoc(result)
scores.append(minVal)
#np.argmin()获得scores中最小值的索引值,并将其转换为字符串,此处的索引值正好为对应的目标数字
match_number = str(np.argmin(scores))
#在原图像中目标数字上画矩形框住数字
cv.rectangle(img_i, (x - 5, y - 5), (x + w + 5, y + h + 5), (0, 0, 255), 1)
#在原图像中目标数字上方将匹配到的数字写到图像上面
cv.putText(img_i, match_number, (x, y - 15), cv.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
#将得到的匹配结果存储到列表中
match_numbers.append(match_number)
#输出信用卡上数字,join方法将使用之前的字符串穿插在之后的迭代对象每个元素直接构成新的字符串输出
print('卡号为:# {}'.format(" ".join(match_numbers)))
#显示写有数字的图像
cv_show('credit card number', img_i)