OpenCV模板匹配实现银行卡数字识别

news/2024/7/21 5:02:33 标签: opencv, 计算机视觉, 人工智能, python, 图像处理

目录

1,项目流程

2,代码流程解读

2.1 导入工具包

2.2  设置参数

2.3    指定信用卡类型

2.4  展示图像

​编辑

2.5  读取一个模板图像

2.6  转化为灰度图--------->再转化为二值图像

2.7  计算轮廓

​编辑

2.8  导入我们要识别的图像,并预处理

3,全部代码


1,项目流程

  1. 导入所需的工具包。

  2. 设置命令行参数,包括输入图像和模板图像的路径。

  3. 定义信用卡类型的字典。

  4. 读取并预处理模板图像。首先将图像转换为灰度图像,然后进行二值化处理。

  5. 计算模板图像中每个数字的轮廓,并保存在字典中。

  6. 初始化卷积核,用于后续图像处理操作。

  7. 读取输入图像并进行预处理。首先调整图像大小为指定宽度,然后将图像转换为灰度图像。

  8. 对灰度图像进行礼帽操作,突出更明亮的区域。

  9. 计算图像的梯度,突出边缘信息。

  10. 通过闭操作将图像中的数字连在一起。

  11. 对图像进行二值化处理。

  12. 再次进行闭操作。

  13. 找到图像中的轮廓。

  14. 根据轮廓的宽高比和面积进行筛选,得到符合条件的轮廓。

  15. 对符合条件的轮廓进行排序,按照从左到右的顺序。

  16. 遍历每个轮廓中的数字。

  17. 提取每个数字所在的组,并进行预处理。

  18. 计算每个组中每个数字的轮廓。

  19. 对每个数字的轮廓与模板进行匹配,得到最合适的数字。

  20. 绘制矩形框和添加识别结果文本。

  21. 输出信用卡类型和完整的信用卡号码。

  22. 展示识别结果图像。

2,代码流程解读

2.1 导入工具包

from imutils import contours
import numpy as np
import argparse
import cv2
import myutils
 

2.2  设置参数

设置了程序需要读入的图像和模板路径

# 设置参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
	help="path to input image")
ap.add_argument("-t", "--template", required=True,
	help="path to template OCR-A image")
args = vars(ap.parse_args())

2.3    指定信用卡类型

在这里,我们指定各种信用卡的开头数字对应的信用卡类型。

# 指定信用卡类型
FIRST_NUMBER = {
	"3": "American Express",
	"4": "Visa",
	"5": "MasterCard",
	"6": "Discover Card"
}

2.4  展示图像

自定义函数,用于可视化图像。

# 绘图展示
def cv_show(name,img):
	cv2.imshow(name, img)
	cv2.waitKey(0)
	cv2.destroyAllWindows()

2.5  读取一个模板图像

# 读取一个模板图像
img = cv2.imread(args["template"])
cv_show('img',img)

2.6  转化为灰度图--------->再转化为二值图像

将模板图像转化为灰度图像,并展示。

# 灰度图
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv_show('ref',ref)
 

将灰度图像进行二值化处理,并展示。

# 二值图像
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]
cv_show('ref',ref)
 

2.7  计算轮廓

计算模板图片的轮廓,并对其进行排序和提取数字,以便后面和输入图像匹配。

# 计算轮廓
ref_, refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

cv2.drawContours(img,refCnts,-1,(0,0,255),3) 
cv_show('img',img)
print (np.array(refCnts).shape)
refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0] #排序,从左到右,从上到下
digits = {}

# 遍历每一个轮廓
for (i, c) in enumerate(refCnts):
	# 计算外接矩形并且resize成合适大小
	(x, y, w, h) = cv2.boundingRect(c)
	roi = ref[y:y + h, x:x + w]
	roi = cv2.resize(roi, (57, 88))

	# 每一个数字对应每一个模板
	digits[i] = roi

2.8  导入我们要识别的图像,并预处理

初始化卷积核,后面操作会用到

# 初始化卷积核
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
 

读入输入图像并进行预处理,包括将图像大小调整为宽度为300像素、转化为灰度图像、应用礼帽操作和 Sobel 滤波器等操作

#读取输入图像,预处理
image = cv2.imread(args["image"])
cv_show('image',image)
image = myutils.resize(image, width=300)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv_show('gray',gray)

#礼帽操作,突出更明亮的区域
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) 
cv_show('tophat',tophat) 
# 
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, #ksize=-1相当于用3*3的
	ksize=-1)


gradX = np.absolute(gradX)
(minVal, maxVal) = (np.min(gradX), np.max(gradX))
gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
gradX = gradX.astype("uint8")

print (np.array(gradX).shape)
cv_show('gradX',gradX)

通过闭操作将数字连在一起

#通过闭操作(先膨胀,再腐蚀)将数字连在一起
gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel) 
cv_show('gradX',gradX)
计算处理后的图像中的轮廓
# 计算轮廓
thresh_, threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
	cv2.CHAIN_APPROX_SIMPLE)

cnts = threshCnts
cur_img = image.copy()
cv2.drawContours(cur_img,cnts,-1,(0,0,255),3) 
cv_show('img',cur_img)
locs = []

对于每个轮廓,检测并保留包含四个数字的矩形

# 遍历轮廓
for (i, c) in enumerate(cnts):
	# 计算矩形
	(x, y, w, h) = cv2.boundingRect(c)
	ar = w / float(h)

	# 选择合适的区域,根据实际任务来,这里的基本都是四个数字一组
	if ar > 2.5 and ar < 4.0:

		if (w > 40 and w < 55) and (h > 10 and h < 20):
			#符合的留下来
			locs.append((x, y, w, h))

将保留的矩形按照从左到右的顺序排列

# 将符合的轮廓从左到右排序
locs = sorted(locs, key=lambda x:x[0])
output = []

遍历每一个轮廓中的数字,对于每个轮廓中的数字,使用模板匹配的方法计算匹配得分,并选择得分最高的数字作为结果。然后将该数字显示在输入图像上,并将结果保存在output列表中。

# 遍历每一个轮廓中的数字
for (i, (gX, gY, gW, gH)) in enumerate(locs):
	# initialize the list of group digits
	groupOutput = []

	# 根据坐标提取每一个组
	group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5]
	cv_show('group',group)
	# 预处理
	group = cv2.threshold(group, 0, 255,
		cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
	cv_show('group',group)
	# 计算每一组的轮廓
	group_,digitCnts,hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL,
		cv2.CHAIN_APPROX_SIMPLE)
	digitCnts = contours.sort_contours(digitCnts,
		method="left-to-right")[0]

	# 计算每一组中的每一个数值
	for c in digitCnts:
		# 找到当前数值的轮廓,resize成合适的的大小
		(x, y, w, h) = cv2.boundingRect(c)
		roi = group[y:y + h, x:x + w]
		roi = cv2.resize(roi, (57, 88))
		cv_show('roi',roi)

		# 计算匹配得分
		scores = []

		# 在模板中计算每一个得分
		for (digit, digitROI) in digits.items():
        # 计算匹配得分
		scores = []

		# 在模板中计算每一个得分
		for (digit, digitROI) in digits.items():
			# 使用相似性度量(差异越小,得分越高)
			result = cv2.matchTemplate(roi, digitROI,
				cv2.TM_CCOEFF)
			(_, score, _, _) = cv2.minMaxLoc(result)
			scores.append(score)

		# 得到最合适的数字
		groupOutput.append(str(np.argmax(scores)))

	# 获得这一组的数值并显示在图像上
	cv2.rectangle(image, (gX - 5, gY - 5),
		(gX + gW + 5, gY + gH + 5), (0, 255, 0), 2)
	cv2.putText(image, "".join(groupOutput), (gX, gY - 15),
		cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 255, 0), 2)

	# 将这一组的数值连接起来
	output.extend(groupOutput)

,输出结果,包括信用卡类型和信用卡号码,并在输入图像上展示结果

# 输出结果
print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Image", image)
cv2.waitKey(0)

3,全部代码

# 导入工具包
from imutils import contours
import numpy as np
import argparse
import cv2
import myutils

# 设置参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
	help="path to input image")
ap.add_argument("-t", "--template", required=True,
	help="path to template OCR-A image")
args = vars(ap.parse_args())

# 指定信用卡类型
FIRST_NUMBER = {
	"3": "American Express",
	"4": "Visa",
	"5": "MasterCard",
	"6": "Discover Card"
}
# 绘图展示
def cv_show(name,img):
	cv2.imshow(name, img)
	cv2.waitKey(0)
	cv2.destroyAllWindows()
# 读取一个模板图像
img = cv2.imread(args["template"])
cv_show('img',img)
# 灰度图
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv_show('ref',ref)
# 二值图像
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]
cv_show('ref',ref)

# 计算轮廓
#cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图),cv2.RETR_EXTERNAL只检测外轮廓,cv2.CHAIN_APPROX_SIMPLE只保留终点坐标
#返回的list中每个元素都是图像中的一个轮廓

# ref_, refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
ref_, refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

cv2.drawContours(img,refCnts,-1,(0,0,255),3) 
cv_show('img',img)
print (np.array(refCnts).shape)
refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0] #排序,从左到右,从上到下
digits = {}

# 遍历每一个轮廓
for (i, c) in enumerate(refCnts):
	# 计算外接矩形并且resize成合适大小
	(x, y, w, h) = cv2.boundingRect(c)
	roi = ref[y:y + h, x:x + w]
	roi = cv2.resize(roi, (57, 88))

	# 每一个数字对应每一个模板
	digits[i] = roi

# 初始化卷积核
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))

#读取输入图像,预处理
image = cv2.imread(args["image"])
cv_show('image',image)
image = myutils.resize(image, width=300)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv_show('gray',gray)

#礼帽操作,突出更明亮的区域
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) 
cv_show('tophat',tophat) 
# 
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, #ksize=-1相当于用3*3的
	ksize=-1)


gradX = np.absolute(gradX)
(minVal, maxVal) = (np.min(gradX), np.max(gradX))
gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
gradX = gradX.astype("uint8")

print (np.array(gradX).shape)
cv_show('gradX',gradX)

#通过闭操作(先膨胀,再腐蚀)将数字连在一起
gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel) 
cv_show('gradX',gradX)
#THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
thresh = cv2.threshold(gradX, 0, 255,
	cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] 
cv_show('thresh',thresh)

#再来一个闭操作

thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel) #再来一个闭操作
cv_show('thresh',thresh)

# 计算轮廓

thresh_, threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
	cv2.CHAIN_APPROX_SIMPLE)

cnts = threshCnts
cur_img = image.copy()
cv2.drawContours(cur_img,cnts,-1,(0,0,255),3) 
cv_show('img',cur_img)
locs = []

# 遍历轮廓
for (i, c) in enumerate(cnts):
	# 计算矩形
	(x, y, w, h) = cv2.boundingRect(c)
	ar = w / float(h)

	# 选择合适的区域,根据实际任务来,这里的基本都是四个数字一组
	if ar > 2.5 and ar < 4.0:

		if (w > 40 and w < 55) and (h > 10 and h < 20):
			#符合的留下来
			locs.append((x, y, w, h))

# 将符合的轮廓从左到右排序
locs = sorted(locs, key=lambda x:x[0])
output = []

# 遍历每一个轮廓中的数字
for (i, (gX, gY, gW, gH)) in enumerate(locs):
	# initialize the list of group digits
	groupOutput = []

	# 根据坐标提取每一个组
	group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5]
	cv_show('group',group)
	# 预处理
	group = cv2.threshold(group, 0, 255,
		cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
	cv_show('group',group)
	# 计算每一组的轮廓
	group_,digitCnts,hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL,
		cv2.CHAIN_APPROX_SIMPLE)
	digitCnts = contours.sort_contours(digitCnts,
		method="left-to-right")[0]

	# 计算每一组中的每一个数值
	for c in digitCnts:
		# 找到当前数值的轮廓,resize成合适的的大小
		(x, y, w, h) = cv2.boundingRect(c)
		roi = group[y:y + h, x:x + w]
		roi = cv2.resize(roi, (57, 88))
		cv_show('roi',roi)

		# 计算匹配得分
		scores = []

		# 在模板中计算每一个得分
		for (digit, digitROI) in digits.items():
			# 模板匹配
			result = cv2.matchTemplate(roi, digitROI,
				cv2.TM_CCOEFF)
			(_, score, _, _) = cv2.minMaxLoc(result)
			scores.append(score)

		# 得到最合适的数字
		groupOutput.append(str(np.argmax(scores)))

	# 画出来
	cv2.rectangle(image, (gX - 5, gY - 5),
		(gX + gW + 5, gY + gH + 5), (0, 0, 255), 1)
	cv2.putText(image, "".join(groupOutput), (gX, gY - 15),
		cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)

	# 得到结果
	output.extend(groupOutput)

# 打印结果
print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Image", image)
cv2.waitKey(0)


http://www.niftyadmin.cn/n/5097510.html

相关文章

执行事务合伙人和法人区别是什么

1. 定义不同&#xff1a; 执行事务合伙人指负责经营和管理合伙企业的人&#xff0c;对外代表合伙企业进行业务活动&#xff0c;对内负责合伙企业的日常管理。 法人则是企业的法定代表人&#xff0c;代表企业参与民事活动&#xff0c;是企业的行政领导&#xff0c;对企业经济活动…

win10 hosts文件修改不生效

解决办法可以参考&#xff1a;修改hosts 不生效? 三种方法解决

大模型LLM相关面试题整理-PEFT

5 大模型&#xff08;LLMs&#xff09;参数高效微调(PEFT) 5.1 PEFT基础 5.1.1 微调方法是啥&#xff1f;如何微调&#xff1f; 微调&#xff08;Fine-tuning&#xff09;是一种迁移学习的技术&#xff0c;用于在一个已经预训练好的模型基础上&#xff0c;通过进一步训练来适…

【Shell脚本】根据起止日期获取Alert日志内容

【Shell脚本】根据起止日期获取Alert日志内容 根据输入的起止日期字符串&#xff0c;检索Oracle告警日志&#xff0c;打印中间的日志行内容。 #!/bin/bash # $1 START_TIME_STR, e.g. "Oct 17 07:" # $2 END_TIME_STR, e.g. "Oct 17 08:" source /home/o…

Windows系统创建Python虚拟环境

文章目录 1 创建虚拟环境的方法2 如何激活环境2.1 目录解读2.2 激活环境和安装第三方库 3 py源文件存放位置4 写在最后 1 创建虚拟环境的方法 在Windows上创建Python虚拟环境的常用方法有以下几种: 使用venv模块创建&#xff08;官方推荐&#xff09; Python内置的venv模块可以…

什么是原型链?

原型链&#xff08;Prototype Chain&#xff09;是 JavaScript 中用于实现继承和对象之间关系的一种机制。在 JavaScript 中&#xff0c;每个对象都有一个内部链接&#xff08;prototype&#xff09;&#xff0c;它指向另一个对象。这个被指向的对象就是原型对象&#xff08;pr…

SAP MM学习笔记37 - 请求书照合中的 追加请求/追加Credit 等概念/ 请求书的取消

有关请求书照合&#xff0c;之前学习了一部分&#xff0c;现在再来学其中的一些概念。 其实这些概念也许并不常用&#xff0c;但是你又不能不知道&#xff0c;因为客户会问。 有关请求书&#xff0c;贴一些以前学习的文章&#xff0c;以方便阅读。 SAP MM学习笔记33 - 请求书…

数据库第六章作业

发表本文章意为记录与分享 第6章作业.xls 题量: 25 满分: 100 一. 单选题&#xff08;共25题&#xff09; 1. (单选题)关系模式中&#xff0c;满足2NF的模式&#xff0c;&#xff08; &#xff09;。 A. 可能是1NFB. 必定是3NFC. 必定是1NFD. 以上都不正确 我的答案: C :…