基于模板匹配的信用卡数字识别

文章目录

  • 一、项目介绍
  • 二、模板匹配的原理
  • 三、模板匹配的步骤
    • 模板图片处理
    • 信用卡图片处理
    • 进行模板匹配

一、项目介绍

模板识别(Template Matching)是一种基于图像匹配的技术,用于在较大图像中识别和定位小图像(模板)。相比使用人工智能(AI)算法训练识别信用卡数字,模板识别具有以下优势:

简单性:模板匹配算法相对简单,容易理解和实现。
计算效率:由于算法简单,模板匹配在计算上通常比深度学习等AI算法更高效。
低资源需求:模板匹配不需要大量的计算资源和训练数据,适合资源受限的环境。
快速部署:在一些应用场景中,模板匹配可以快速部署,而AI模型可能需要较长的时间来训练和优化。
可预测性:模板匹配的结果通常容易预测,因为它依赖于固定的模板和匹配算法。
特定场景下的高准确度:在图像质量高、背景简单、目标物体与模板高度相似的情况下,模板匹配可以提供较高的准确度。
抗干扰能力:如果目标图像与模板之间的变化较小(如旋转、缩放),模板匹配可以很好地工作。
无需大量标注数据:与需要大量标注数据训练的AI算法不同,模板匹配不需要大量的训练数据。
易于集成:模板匹配技术易于集成到现有的图像处理流程中,不需要复杂的模型训练和调优。
然而,模板匹配也有其局限性,例如对噪声敏感、难以处理图像中的遮挡和复杂变化等。而AI算法,尤其是基于深度学习的算法,通常在处理复杂场景和变化时表现更好,具有更好的泛化能力和适应性。

项目直接根据提供的数字模板识别信用卡的卡号。模板字体需要提前准备好,最好和信用卡字体保持一致。用到的图像处理框架为:opencv 版本4.9。

用到的模板:
在这里插入图片描述

模板


识别效果:


在这里插入图片描述

原图


在这里插入图片描述

识别出卡号的图

二、模板匹配的原理

模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图像被模板覆盖的地方)的差别程度,这个差别程度的计算方法在 opencv 里有6种,每次计算的结果放入一个矩阵里,作为结果输出。假如原图形是AxB大小,而模板是axb大小,则输出结果的矩阵是(A-a+1)x(B-b+1):

- TM_SQDIFF:计算平方不同,计算出来的值越小,越相关        
- TM_CCORR:计算相关性,计算出来的值越大,越相关
- TM_CCOEFF:计算相关系数,计算出来的值越大,越相关
- TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关
- TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关
- TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关

建议使用归一化的计算方法会相对公平一些,方法:

- matchTemplate(image, templ, method[, result[, mask]]) 进行模板匹配- image是要匹配的图片- templ是模板图片- method是计算方式- result是进行匹配计算后得到的矩阵. - mask是掩膜
- minMaxLoc(src[, mask])  获取最大值和最小值的位置- 返回四个值, 分别是最小值, 最大值, 最小值坐标, 最大值坐标

三、模板匹配的步骤

模板图片处理

1.首先对模板二值化


# 封装显示图片的函数
def cv_show(name, img):cv2.imshow(name, img)cv2.waitKey(0)cv2.destroyAllWindows()# 先处理数字模板图片
import cv2
import numpy as np# 读取模板图片
img = cv2.imread('./ocr_a_reference.png')
# print(img.shape)
# cv_show('img', img)
# 灰度化处理
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# cv_show('ref', ref)
print('ref', ref)
# 二值化处理
_, ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)
# cv_show('ref', ref)
print('ref2', ref)

在这里插入图片描述

逆二值化后的模板


2.找出每个数字的轮廓和外接矩形,将数字从图片中取出来

# 计算轮廓
ref_contours, _ = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 画出外轮廓
cv2.drawContours(img, ref_contours, -1, (0, 0, 255), 3)
# cv_show('img', img)
# 表示数字的轮廓
print(np.array(ref_contours, dtype='object').shape)

在这里插入图片描述

每个数字的轮廓


3.对轮廓进行排序, 按照数字大小进行排序, 方便后面使用
排序思路: 根据每个数字的最大外接矩形的x轴坐标进行排序

# 获取每个轮廓的外接矩形
bounding_boxes = [cv2.boundingRect(c) for c in ref_contours]
# print(bounding_boxes)
# print(sorted(bounding_boxes, key=lambda b: b[0]))
# 要把排序之后的外接矩形和轮廓建立对应关系.
(ref_contours, bounding_boxes) = zip(*sorted(zip(ref_contours, bounding_boxes), key=lambda b: b[1][0]))
digits = {}
for (i, c) in enumerate(ref_contours):# 重新计算外接矩形(x, y, w, h) = cv2.boundingRect(c)# region of interest 感兴趣的区域# 取出每个数字roi = ref[y:y + h, x: x + w]# resize成合适的大小# print('roi0', roi)roi = cv2.resize(roi, (57, 88))# cv_show('roi1', roi)# print('roi', roi)digits[i] = roi
# print(digits)

在这里插入图片描述

取出的数字0


信用卡图片处理

  1. 首先信用卡图片二值化
# 对信用卡图片进行处理
image = cv2.imread('./credit_card_03.png')
# cv_show('image', image)
# 对信用卡图片进行resize
# 为了保证原图不拉伸, 需要计算出原图的长宽比.
h, w = image.shape[:2]
width = 300
r = width / w
image = cv2.resize(image, (300, int(h * r)))
# cv_show('image', image)
# 灰度化处理
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv_show('gray', gray)

在这里插入图片描述

二值化后的图


2.接下来是形态学的各种操作,顶帽操作,突出更明亮的区域
顶帽操作是原图像与图像开运算结果之间的差,它把开运算“去掉”的细节显现出来。

# 初始化卷积核
rect_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rect_kernel)
cv_show('tophat', tophat)

在这里插入图片描述

顶帽操作后的图


3.直接应用闭操作可能就足够了,结合Sobel算子和其他图像处理技术可能会产生更好的效果

# sobel算子
grad_x = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)
# print(grad_x)
# 对grad_x进行处理
# 只用x轴方向的梯度
grad_x = np.absolute(grad_x)
# 再把grad_x变成0到255之间的整数
min_val, max_val = np.min(grad_x), np.max(grad_x)
grad_x = ((grad_x - min_val) / (max_val - min_val)) * 255
# 修改一下数据类型
grad_x = grad_x.astype('uint8')
cv_show('grad_x', grad_x)

在这里插入图片描述

sobel对水平方向梯度处理后的图


4.闭操作, 先膨胀, 再腐蚀, 可以把数字连在一起

grad_x = cv2.morphologyEx(grad_x, cv2.MORPH_CLOSE, rect_kernel)
cv_show('gradx', grad_x)

在这里插入图片描述

闭操作后的图


# 通过大津(OTSU)算法找到合适的阈值, 进行全局二值化操作.
_, thresh = cv2.threshold(grad_x, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
cv_show('thresh', thresh)# 中间还有空洞, 再来一个闭操作
sq_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sq_kernel)
cv_show('thresh', thresh)

在这里插入图片描述

二次闭操作后的图


5.原图上找到对应轮廓

thresh_contours, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 在原图上画轮廓
image_copy = image.copy()
cv2.drawContours(image_copy, thresh_contours, -1, (0, 0, 255), 3)
cv_show('img', image_copy)

在这里插入图片描述

画出轮廓


6.遍历轮廓, 计算外接矩形, 然后根据实际信用卡数字区域的长宽比, 找到真正的数字区域

locs = []
output = []
for c in thresh_contours:# 计算外接矩形(x, y, w, h) = cv2.boundingRect(c)# 计算外接矩形的长宽比例ar = w / float(h)# 选择合适的区域#     print(ar)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))# 对符合要求的轮廓进行从左到右的排序.
sorted(locs, key=lambda x: x[0])# 遍历每一个外接矩形, 通过外接矩形可以把原图中的数字抠出来.
for (i, (gx, gy, gw, gh)) in enumerate(locs):# 抠出数字区域, 并且加点余量group = gray[gy - 5: gy + gh + 5, gx - 5: gx + gw + 5]cv_show('group', group)# 对取出灰色group做全局二值化处理group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]cv_show('group', group)

在这里插入图片描述

找到的一组数字

7.接下来和模板中取出数字区域的逻辑类似

    # 计算轮廓digit_contours, _ = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 对轮廓进行排序bounding_boxes = [cv2.boundingRect(c) for c in digit_contours](digit_contours, _) = zip(*sorted(zip(digit_contours, bounding_boxes), key=lambda b: b[1][0]))# 定义每一组匹配到的数字的存放列表group_output = []print('digit_contours', digit_contours)image_copy = image.copy()cv2.drawContours(image_copy, digit_contours, -1, (0, 0, 255), 3)# 遍历排好序的轮廓for c in digit_contours:# 找到当前数字的轮廓, 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)  

在这里插入图片描述

取出了数字7

进行模板匹配

		# 定义保存匹配得分的列表scores = []for (digit, digit_roi) in digits.items():result = cv2.matchTemplate(roi, digit_roi, cv2.TM_CCOEFF)# 只要最大值, 即分数(_, score, _, _) = cv2.minMaxLoc(result)scores.append(score)# 找到分数最高的数字, 即我们匹配到的数字lgroup_output.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(group_output), (gx, gy - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)output.extend(group_output)
cv2.imwrite('image.jpg', image)
cv_show('image', image)

最后将识别出的数字显示在原图上对应数字区域上方

在这里插入图片描述

识别效果图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/845022.shtml

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

相关文章

YOLO目标检测:框架技术原理和代码实现

Dream推荐 适读人群 &#xff1a;本书适合对YOLO目标检测感兴趣、了解深度学习相关概念的算法工程师、软件工程师等人员阅读。 全面&#xff1a;涵盖6个常用目标检测框架&#xff08;YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOX、YOLOv7&#xff09;的发展状况、技术原理和代码实…

【Spring EL<一>✈️ 】SL 表达式的应用

目录 &#x1f378;前言 &#x1f37b;一、Spring EL 1.1 定义 1.2 常见使用方式 &#x1f37a;二、项目案例 2.1 实现一个简单的案例 2.2 创建注解 2.3 切面类实现 2.4 创建测试接口 2.5 测试 &#x1f379;三、章末 &#x1f378;前言 小伙伴们大家好&#xff0c;前段时间…

32. 【Java教程】集合

在前面的小节中&#xff0c;我们学习了数组&#xff0c;本小节学习的集合同样用于存放一组数据&#xff0c;我们将学习什么是集合、集合的应用场景 &#xff0c;在应用场景部分我们将对比 Java 数组与集合的区别&#xff0c;还将系统介绍 Java 集合的架构&#xff0c;也将结合实…

【观察】数字化生存时代已来临,能源转型如何实现“再升级”?

20多年前&#xff0c;尼古拉斯尼葛洛庞帝在《数字化生存》一书中预言&#xff1a;“数字化生存是现代社会中以新技术为基础的新的生存方式。” 随着数字经济的蓬勃发展&#xff0c;尼葛洛庞帝的预言逐渐被验证。今天&#xff0c;新技术带来的数字化和智能化正全方位影响着经济…

【赠书第27期】向AI提问的艺术:提示工程入门与应用

文章目录 前言 1 问题的构建 1.1 明确性与具体性 1.2 结构化与层次性 1.3 相关性与针对性 2 提问的技巧 2.1 简洁明了 2.2 避免歧义 2.3 使用自然语言 3 与AI的互动策略 3.1 耐心与理解 3.2 逐步引导 3.3 反馈与调整 4 总结与展望 5 推荐图书 6 粉丝福利 前言 …

定时器与PWM的LED控制

目录 一、基础概念定时器定时器类型定时器特性 PWM定义占空比原理 二、实验1.LED周期性亮灭定时器TIM2配置GPIO引脚设置工程相关参数配置Keil编写程序 2.LED呼吸灯(PWM)呼吸灯原理Keil编写程序Keil虚拟示波器&#xff0c;观察 PWM输出波形设置点击setup&#xff0c;并设置观察引…

Sapphire开发日志(三)

任务描述 本周实现并封装了模型的运行&#xff0c;需要在前端安装依赖的库&#xff0c;编写对应的处理函数。同时需要能够展示出抠图效果。 依赖与环境配置 尝试在前端接入并运行onnx。 在前端运行onnx依赖于onnxruntime-web这个包&#xff0c;这个包可以在浏览器进行模型推…

antV+vue3 单选框选中加入逻辑运算,再次点击取消选中,不计入逻辑运算

文章目录 antVvue3 单选框选中加入逻辑运算&#xff0c;再次点击取消选中&#xff0c;不计入逻辑运算需求增加点击事件逻辑处理效果 antVvue3 单选框选中加入逻辑运算&#xff0c;再次点击取消选中&#xff0c;不计入逻辑运算 需求 在做项目时&#xff0c;多次遇到单选框需要再…

【2024新版】银系统源码/超市收银系统/智慧新零售/ERP进销存管理/线上商城/商户助手

>>>系统简述&#xff1a;本系统适用于超吃便利店&#xff0c;美妆母婴行业&#xff0c;服装鞋帽行业&#xff0c;食品零售行业&#xff0c;3C数码电子行业&#xff0c;食品生鲜等一切零售行业&#xff0c;产品功能角色介绍如下 合伙人&#xff1a;无限发展代理商和商…

Pod 控制器

前言 Pod 是 Kubernetes 集群中能够被创建和管理的最小部署单元。所以需要有工具去操作和管理它们的生命周期,这里就需要用到控制器了。 Pod 控制器由 master 的 kube-controller-manager 组件提供&#xff0c;常见的此类控制器有 Replication Controller、ReplicaSet、Deploym…

React-生成随机数和日期格式化

生成随机数 uuid文档&#xff1a;https://github.com/uuidjs/uuid npm install uuid import {v4 as uuidV4} from uuid 使用&#xff1a; uuidV4() 日期格式化 dayjs文档&#xff1a;安装 | Day.js中文网 npm install dayjs import dayjs from dayjs

算法-扫描线

目录 什么是扫描线算法&#xff1f; 扫描线简单应用 更多的扫描线 什么是扫描线算法&#xff1f; 在计算几何中&#xff0c;扫描线算法&#xff08;scan line algorithm&#xff09;一般用来解决几何图形的面积交并&#xff0c;周长交并问题&#xff0c;扫描线算法的核心思想…

SAPUI5基础知识3 - 引导过程(Bootstrap)

1. 背景 在上一篇博客中&#xff0c;我们已经建立出了第一个SAPUI5项目&#xff0c;接下来&#xff0c;我们将为这个项目添加引导过程。 在动手练习之前&#xff0c;让我们先解释一下什么引导过程。 1.1 什么是引导过程&#xff1f; 在计算机科学中&#xff0c;引导过程也称…

5、css3 自动动画渐变背景

效果例图&#xff1a;&#xff08;因gif图片太大&#xff0c;而csdn只能上传小于5m图片&#xff0c;所以无法上传&#xff09; 1、首先上传html代码&#xff1a; <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8">&l…

【鸟哥】Linux笔记-硬件搭配

在Linux这个系统当中&#xff0c;几乎所有的硬件设备文件都在/dev这个目录内。打印机与软盘呢&#xff1f;分别是/dev/lp0, /dev/fd0。 几个常见的设备与其在Linux当中的文件名&#xff1a; 如果你的机器使用的是跟网际网络供应商 &#xff08;ISP&#xff09; 申请使用的云端…

二叉树的构建和遍历(oj题)

一、题目链接 https://www.nowcoder.com/practice/4b91205483694f449f94c179883c1fef?tpId60&&tqId29483&rp1&ru/activity/oj&qru/ta/tsing-kaoyan/question-ranking 二、题目思路 利用先序遍历的方法&#xff0c;构建二叉树。为了保证在递归中&#xf…

USB主机模式——Android

理论 摘自&#xff1a;USB 主机和配件概览 | Connectivity | Android Developers (google.cn) Android 通过 USB 配件和 USB 主机两种模式支持各种 USB 外围设备和 Android USB 配件&#xff08;实现 Android 配件协议的硬件&#xff09;。 在 USB 主机模式下&#xff0…

Redis 中的 Zset 数据结构详解

目录 用法 1. 增 2. 删 3. 查 4. 交&#xff0c;并 编码方式 应用场景 Redis 中的 Zset&#xff08;有序集合&#xff09;是一种将元素按照分数进行排序的数据结构。与上篇写的SetRedis 中的 Set 数据结构详解不同&#xff0c;Zset 中的每个元素都关联一个浮点数类型的…

Jmeter的线程组之间传递参数

使用jemter做接口测试&#xff0c;有时候需要会遇到不同线程组之间调用相同变量的情况&#xff0c;最多见的就是token的传递&#xff0c;网上有很多处理方法&#xff0c;这里只记录setProperty的办法&#xff0c;一招鲜走遍天&#xff01; 首先我有两个线程组&#xff1a; 线程…

vm:为虚拟机配置多个虚拟网卡(ubuntu20.04)

前言&#xff1a; 环境&#xff1a;虚拟机 ubuntu 20.04 要求&#xff1a;如标题&#xff0c;但是这里针对的是 ubuntu 20.04&#xff0c;对于其他操作系统&#xff0c;可以找一下其他操作系统对应的配置文件是什么 vm 添加虚拟网卡 首先进入 vm&#xff1a; 点击设置&#xf…