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

news2025/7/21 23:12:05

目录

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.coloradmin.cn/o/1102274.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设计海报都有哪些好用的软件推荐

在新媒体时代&#xff0c;设计在各个方面都是不可分割的。它最初是设计师的工作&#xff0c;并逐渐成为新媒体编辑的必要技能。 网页内容需要图片和文字&#xff0c;应用程序需要独特的风格基调&#xff0c;人们更喜欢分享视频和图片&#xff0c;而不是简单的文本。因此&#…

HTX 与 Zebec Protocol 深度合作,并将以质押者的身份参与 ZBC Staking

自 2023 年下半年以来&#xff0c;加密市场始终处于低迷的状态&#xff0c;在刚刚结束的 9 月&#xff0c;加密行业总融资额创下 2021 年以来的新低&#xff0c;同时在 DeFi 领域 DEX 交易额为 318.9 亿美元&#xff0c;同样创下 2021 年 1 月以来的新低。 对于投资者而言&…

jmeter接口测试断言

一、响应断言&#xff1a;对服务器的响应接口进行断言校验&#xff0c;来判断接口测试得到的接口返回值是否正确。 二、添加断言&#xff1a; 1、apply to&#xff1a;通常发出一个请求只触发一个请求&#xff0c;所以勾选“main sampie only”就可以&#xff1b;若发一个请求可…

基于Mediapipe的对象分类任务,CPU平台毫秒级别延迟

计算机视觉任务一直是GPU的天下,由于GPU超强的算力,也把计算机视觉任务提高了很多水平。但是在移动终端平台,如何来运行大型的模型,一直是大家关注的话题。Mediapipe是Google开源的可以直接运行在移动终端设备上的多任务模型,不仅在计算机视觉任务上,还是NLP自然语言处理…

面向对象是一种艺术

目录 文章导读面向对象与面向过程是什么&#xff1f;两者的比较 面向对象的三大特性封装怎么理解优点&#xff1a;代码示例 继承怎么理解优点代码示例 多态怎么理解优点示例代码 总结 文章导读 本文不纠结语言的选择&#xff0c;仅仅介绍面向对象这一个编程思想的运用&#xf…

MySQL高级02-MySQL的数据目录

1.MySQL8的主要目录结构 查看MySQL的目录结构 find / -name mysql1.1 数据库文件的存放路径 MySQL数据库文件的存放路径&#xff1a;/var/lib/mysql/ mysql> show variables like datadir; -------------------------------- | Variable_name | Value | ---------------…

MAT查找类(岔路口)-技巧

文章目录 前言一、现状二、使用步骤1.导出 hprof2.用MAT打开3.细节操作找大对象的线程名称查看线程的详情查找类的GC Roots柳暗花明检验真理 总结 前言 又是java 内存溢出 OOM JAVA MAT 分析工具大大的好。 高效查找问题根源&#xff0c;才是硬道理。 一、现状 mat 打开hprof…

app.json: [“usingComponents“][“van-icon“]: “@vant/weapp/icon/index“ 未找到

维护一个微信小程序的项目&#xff0c;运行报错如下&#xff1a; app.json: ["usingComponents"]["van-icon"]: "vant/weapp/icon/index" 未找到解决办法 我只说我用到的&#xff0c;如果解决不了你的问题&#xff0c;详细的可以参照官方文档&…

git的介绍和安装、常用命令、忽略文件

git介绍和安装 首页功能写完了 ⇢ \dashrightarrow ⇢ 正常应该提交到版本仓库 ⇢ \dashrightarrow ⇢ 大家都能看到这个 ⇢ \dashrightarrow ⇢ 运维应该把现在这个项目部署到测试环境中 ⇢ \dashrightarrow ⇢ 测试开始测试 ⇢ \dashrightarrow ⇢ 客户可以看到目前做的…

使用 Python 和蒙特卡罗计算未来股价走势以及历史波动率和隐含波动率

一、简介 预测金融市场是定量精度和全球经济细微差别的复杂融合。在这一探索中,蒙特卡罗模拟脱颖而出,成为首要的统计工具,指导我们对未来股票价格的理解。 这种方法以摩纳哥著名的蒙特卡洛赌场命名,并不依靠运气,而是植根于严格的概率模型。想象一下在受控环境中精心策划…

Jmeter执行接口自动化测试-如何初始化清空旧数据

需求分析&#xff1a; 每次执行完自动化测试&#xff0c;我们不会执行删除接口把数据删除&#xff0c;而需要留着手工测试&#xff0c;此时会导致下次执行测试有旧数据我们手工可能也会新增数据&#xff0c;导致下次执行自动化测试有旧数据 下面介绍两种清空数据的方法 一、通过…

DIN模型和SIM模型原理与实践

文章目录 1.DIN模型原理缺点 2.SIM模型原理算法步骤1.查找2.注意力机制 参考文献 1.DIN模型 原理 计算用户Last N向量的加权平均权重是候选物品与Last N物品的相似度 缺点 注意力层的计算量正比于n&#xff08;用户行为序列的长度&#xff09;只能记录最近的几百个物品&…

HANA:计算视图-图形化Aggregation组件-踩坑小记(注意事项)

今天遇到在做HANA视图开发的时候&#xff0c;遇到一个事&#xff0c;一直以为是个BUG&#xff0c;可把我气坏了&#xff0c;具体逻辑是这样的&#xff0c;是勇图形化处理的&#xff0c;ACDOCA innerjoin 一个时间维度表&#xff0c;就这么简单&#xff0c;完全按照ACDOCA的主键…

​换电站:一个「利用户、利蔚来、利电力改革」的能源产品

换电的本质是什么&#xff1f; 我相信连蔚来车主自己也不是很清楚&#xff0c;不就是目前体验最接近油车的补能方式吗&#xff0c;还有什么&#xff1f; 「换电站是分布在城市中的采集和存储能源的触点&#xff0c;除了可为电动车用户加电&#xff0c;将来结合储能还有很多想象…

PAM从入门到精通(四)

接前一篇文章&#xff1a;PAM从入门到精通&#xff08;三&#xff09; 本文参考&#xff1a; 《The Linux-PAM Application Developers Guide》 先再来重温一下PAM系统架构&#xff1a; 更加形象的形式&#xff1a; 五、主要函数详解 1. pam_start 概述&#xff1a; PAM事务…

添加Mybatis框架支持

配置环境 1.老项目 在pom.xml中使用generate ->edit starts->添加下面俩 2.新项目 然后运行发现会有报错 2.1这是因为没有配置数据库连接信息 spring:datasource:url: jdbc:mysql://localhost:3306/java2023?characterEncodingutf8&useSSLfalse # MySQL数…

2024泰晤士世界大学排名公布(附Top200榜单)

近日&#xff0c;泰晤士高等教育世界大学排名&#xff08;THE&#xff09;发布了2024世界大学榜单&#xff0c;这表明全球高等教育领域的四大主流排名&#xff08;QS、U.S. News、ARWU、THE&#xff09;都已经尘埃落定。知识人网小编整理了泰晤士TOP前200的榜单&#xff0c;供访…

数据挖掘十大算法--Apriori算法

一、Apriori 算法概述 Apriori 算法是一种用于关联规则挖掘的经典算法。它用于在大规模数据集中发现频繁项集&#xff0c;进而生成关联规则。关联规则揭示了数据集中项之间的关联关系&#xff0c;常被用于市场篮分析、推荐系统等应用。 以下是 Apriori 算法的基本概述&#x…

发面试题:(四)synchronized和lock区别

synchronized 关键字 synchronized关键字解决的是多个线程之间访问资源的同步性&#xff0c;synchronized关键字可以保证被它 修饰的方法或者代码块在任意时刻只能有一个线程执行。 另外&#xff0c;在 Java 早期版本中&#xff0c; synchronized属于重量级锁&#xff0c;效率…

20-数据结构-内部排序-插入排序

简介&#xff1a;插入排序基本有两步&#xff0c;先是通过比较&#xff0c;得到插入位置&#xff0c;随后移动给需要插入的位置处腾空&#xff0c;最后进行值的插入。 目录 一、直接插入排序 1.1简介&#xff1a; 1.2代码 二、折半插入排序 2.1简介&#xff1a; 2.2代码…