13- 信用卡匹配 (OpenCV基础) (项目十三) *

news/2024/4/26 14:58:32/文章来源:https://blog.csdn.net/March_A/article/details/129179412

项目要点

  • _, ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)   二值化处理图片, 黑白化图片
  • ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY灰度化处理
  • ref_contours, _ = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  计算轮廓
  • cv2.drawContours(img, ref_contours, -1, (0, 0, 255), 2)  画出外轮廓
  • image = cv2.resize(image, (300, int(h * r)))   等比例缩放原图   # r = width / w
  • 顶帽运算: tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rect_kernel)  顶帽 = 原图 - 开运算  开运算的效果是去除图像外的噪点, 原图 - 开运算就得到了去掉的噪点.
  • 索贝尔算子: grad_x = cv2.Sobel(tophat, ddepth = cv2.CV_32F, dx = 1, dy = 0, ksize = -1)  边缘检测, 一般先分别求x, y 方向上的检测结果, 再进行合并 .
  • 闭操作: grad_x = cv2.morphologyEx(grad_x, cv2.MORPH_CLOSE, rect_kernel)  闭运算 = 膨胀 + 腐蚀  填充图像内部的噪点 .
  • _, thresh = cv2.threshold(grad_x, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU大津算法找到合适的阈值进行全局二值化  主要是通过阈值进行前后背景分割 .
  • 模板匹配: result = cv2.matchTemplate(roi, digit_roi, cv2.TM_CCOEFF)


信用卡识别项目

基本思路:

总体思路就是取出信用卡中每一个数字作为去和模板中的10个数字进行模板匹配操作.

  1. 先对模板处理, 获取每个数字的模板及其对应的数字标识.

  2. 再对信用卡处理, 通过一系列预处理操作, 取出信用卡数字区域.

  3. 然后再取出每一个数字去和模板中的10个数字进行匹配.

1  先对模板处理

1.1 显示原始图片

# 先处理数字模板的图片
import cv2
import numpy as np# 模板图片
img = cv2.imread('./ocr_a_reference.png')
def cv_show(name, img):    # 封装显示的函数cv2.imshow(name, img)cv2.waitKey(0)cv2.destroyAllWindows()print(img.shape)    # (126, 800, 3)
cv_show('img', img)

1.2 灰度化处理

ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)   # 灰度化处理
cv_show('ref', ref)
print(ref.shape)   # (126, 800)

 1.3 二值化图片

# 二值化处理
_, ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)
cv_show('ref', ref)

 1.4 描绘轮廓

# 计算轮廓
ref_contours,_=cv2.findContours(ref.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# 画出外轮廓
cv2.drawContours(img, ref_contours, -1, (0, 0, 255), 2)
cv_show('img', img)
print(np.array(ref_contours).shape)   # (10,)

1.5 外接矩形

# 对轮廓进行排序, 按照数字大小进行排序, 方便使用
# 排序思路:根据每个数字的最大外接矩形的X轴坐标进行排序
# 计算每个轮廓的外接矩形
bounding_boxes = [cv2.boundingRect(c) for c in ref_contours]
print(len(bounding_boxes))   # 10
sorted(bounding_boxes, key = lambda b : b[0])
print('---------\n', bounding_boxes[0][:2])   # (15, 20)
cv2.rectangle(img, bounding_boxes[0][:2], (bounding_boxes[0][0]+bounding_boxes[0][2],bounding_boxes[0][1]+bounding_boxes[0][3]), (255, 0, 0), 2)
# * 去中括号                                            
(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 成合适的图像roi = cv2.resize(roi, (57, 88))digits[i] = roiprint(len(digits))   # 10
cv_show('img', img)
  • digits 是一个数字和特征对应的字典 .

2 信用卡处理

2.1 信用卡图片

# 对信用卡图片进行处理
image = cv2.imread('./credit_card_01.png')
print(image.shape)   # (368, 583, 3)
cv_show('img',image)

 2.2 调整图片尺寸

# 对信用卡图片resize
# 保证原图不拉伸, 计算原图的长宽比
h, w = image.shape[:2]
width = 300
r = width / w
image = cv2.resize(image, (300, int(h * r)))  # 等比例缩放原图
print(image.shape)  # (189, 300, 3)
cv_show('image', image)

 2.3 灰度化处理图片

# 灰度化处理
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
print(gray.shape)    # (189, 300)
cv_show('image', gray)

 2.4 顶帽操作

# 形态学操作
# 顶帽操作,突出明亮区,初始化卷积核, 去除图像外的噪点
rect_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rect_kernel)
print(tophat.shape)   # (189, 300)
cv_show('tophat', tophat)

 2.5 索贝尔算子

# 索贝尔算子, 计算图像轮廓
grad_x = cv2.Sobel(tophat, ddepth = cv2.CV_32F, dx = 1, dy = 0, ksize = -1)
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)  # 只用X方向的值

# 计算y轴梯度
grad_y = cv2.Sobel(tophat, ddepth = cv2.CV_32F, dx = 0, dy = 1, ksize = -1)
grad_y = np.absolute(grad_y)
# 再把grad_y 变成0 到255 之间的整数
min_val, max_val = np.min(grad_y), np.max(grad_y)
grad_y = ((grad_y - min_val)/ (max_val - min_val)) * 255
# 修改 数据类型
grad_y = grad_y.astype('uint8')cv_show('grad_y', grad_y)
cv_show('gray', grad_x + grad_y)

. y轴的索贝尔算子梯度                                     . 索贝尔算子合并后效果

 2.6 闭操作

# 闭操作 ,先膨胀,再腐蚀, 填充内部噪点
grad_x = cv2.morphologyEx(grad_x, cv2.MORPH_CLOSE, rect_kernel)
cv_show('grad_x', grad_x)

 2.7 大津算法

# 大津算法找到合适的阈值进行全局二值化, 区分前景背景
_, thresh = cv2.threshold(grad_x, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
cv_show('thresh', thresh)

 2.8 继续闭运算

# 再来闭操作, 中间还有空洞  5 * 5 的卷积核放入, 减少内部噪点
sq_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sq_kernel)
cv_show('thresh', thresh)

 2.9 寻找轮廓

# 找轮廓
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('image', image_copy)

2.10 查找信用卡数字区

  • 根据信用卡数字区的长宽特征, 找出数字区域 .

# 定义每一组匹配到的值的列表
# 遍历轮廓, 计算外接矩形,然后根据信用卡的长宽比,找到数字区
locs = []
for c in thresh_contours:# 计算外接矩形(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))print(locs)  
# [(224, 102, 52, 14), (157, 102, 51, 14), (90, 102, 51, 14), (24, 102, 50, 14)]

3  模板匹配

# 对符合的轮廓进行排序
# 遍历每个外接矩形,通过外接矩形可以把原图中的数字抠出来
sorted(locs, key = lambda x : x[0])
out_put = []
for (i , (gx, gy, gw, gh)) in enumerate(locs):# 扣除数字,外轮廓留预值# 根据坐标调出在原图的位置截图group = gray[gy - 5 : gy + gh + 5, gx - 5: gx+ gw + 5]   # gray 是灰度化处理后的图片# 对取出的灰色group做全局二值化处理, 黑白图group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]# 计算轮廓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]))# print(len(digit_contours))   # 4 每次是四个数字, 每个数字的轮廓group_output = []# 遍历排好序的轮廓, 每个digit_contours 为每个数字的元组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)   # 显示每个数字# 进行模板匹配, 定义保存匹配得分的列表scores = []for (digit, digit_roi) in digits.items():result = cv2.matchTemplate(roi, digit_roi, cv2.TM_CCOEFF)# 只要最大值分数(_, score, _, _) = cv2.minMaxLoc(result)scores.append(score)# 找到分数最高的数字, 即匹配到的数字group_output.append(str(np.argmax(scores)))# print(scores)    # 第一列在 9位置的值: -5058846.5# print(group_output)  # 第一个值是 ['9']out_put = group_output + out_put# 画出轮廓显示数字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)
cv_show('image', image)
print(out_put)    # ['4', '0', '0', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '0']
car_num = ''
for i in out_put:car_num += str(i)
print('信用卡账号:',car_num)    # 信用卡账号: 4000123456789010

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

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

相关文章

xselect 实现已用数据禁止选择

function queryExamtemplate() {layui.$.ajax({url: 接口地址,type: "get",data: {id: id, name: name},//传参数方式 无参[]dataType: "json",contentType: "application/json",success: function (res) {if (res.code 0) {let data [];res.…

滤波算法:经典卡尔曼滤波

卡尔曼滤波实质上就是基于观测值以及估计值二者的数据对真实值进行估计的过程。预测步骤如图1所示&#xff1a; ​图1 卡尔曼滤波原理流程图 假设我们能够得到被测物体的位置和速度的测量值 ​&#xff0c;在已知上一时刻的最优估计值 ​以及它的协方差矩阵 的条件下&#xff…

【数据结构】时间复杂度

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a;初阶数据结构 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是对…

Nordic nRF芯片FDS模块学习

FDS系统学习 文章目录FDS系统学习一、ROM&#xff0c;RAM&#xff0c;FLASH作用二、ROM,RAM和FLASH在单片中的运作原理三、Flash访问模块FDS用法1. FDS在sdk_config.h中的配置2. fds_register()注册3. fds_record_write()写记录4. fds_record_find()查找5. fds_record_open()读…

常见的排序算法 | 直接插入排序 | 希尔排序 | 选择排序 | 堆排序 | 冒泡排序 | 快速排序 | 归并排序 |(详解,附动图,代码)

思维导图&#xff1a; 一.插入排序 1.直接插入排序&#xff08;InsertSort&#xff09; ①手机通讯录时时刻刻都是有序的&#xff0c;新增一个电话号码时&#xff0c;就是使用插入排序的方法将其插入原有的有序序列。 ②打扑克 步骤&#xff1a; ①如果一个序列只有一个数&am…

报表开发难上手?这里有一份 Fastreport 最新中文用户指南,请查收

Fast Reports,Inc.成立于1998年&#xff0c;多年来一直致力于开发快速报表软件&#xff0c;包括应用程序、库和插件。FastReport的报表生成器&#xff08;VCL平台和.NET平台&#xff09;、跨平台的多语言脚本引擎FastScript、桌面OLAP FastCube&#xff0c;如今都受到世界各地开…

工赋开发者社区 | 工业数字孪生:西门子工业网络与设备虚拟调试案例(TIA+MCD+SINETPLAN)

PART1案例背景及基本情况新生产系统的设计和实施通常是耗时且高成本的过程&#xff0c;完成设计、采购、安装后&#xff0c;在移交生产运行之前还需要一个阶段&#xff0c;即调试阶段。如果在开发过程中的任何地方出现了错误而没有被发现&#xff0c;那么每个开发阶段的错误成本…

建议收藏!数据可视化大屏设计必备步骤

相信对于从事大数据相关的人员来说&#xff0c;数据的可视化大屏是最能展现自己工作价值的一个途径。可视化大屏就是数据分析的最后成果的展示&#xff0c;而如果能设计出更直观、更酷炫、更具有科技感的大屏&#xff0c;更能获得客户的青睐。 那么客户喜欢的究竟是怎样的可视化…

嵌入式开发:在嵌入式应用程序中混合C和C++

许多嵌入式应用程序仍使用c语言编写&#xff0c;但越来越多的嵌入式开发人员现在使用C语言编写程序。某些应用程序甚至共享这两种语言。这有意义吗?C是嵌入式应用中最常用的编程语言。多年来&#xff0c;人们一直期待着向C过渡&#xff0c;但过渡速度相当缓慢。但是&#xff0…

Appium自动化测试 Inspector定位Webview/H5页面元素

目录操作步骤Python操作该混合App代码Appium在操作混合App或Android App的H5页面时, 常常需要定位H5页面中的元素, 传统方式是 FQ 使用Chrome://inspect来定位元素, 环境准备相当繁琐, 不仅需要想办法FQ, 而且还需要Android设备安装Google框架以及手机版Chrome浏览器以及相应的…

问题记录-网卡丢失导致Temporary failure in name resolution

没网了&#xff0c;ifconfig查看一下 发现是网卡丢失 使用如下命令&#xff1a; sudo ifconfig eth0 up sudo dhclient解决

postgresql 数据库 主从切换 测试

postgresql 数据库 主从切换 测试 文章目录postgresql 数据库 主从切换 测试前言环境&#xff1a;主从切换1. 查看数据库状态&#xff1a;2. 备库切换主库3. 旧主库切换成备库&#xff1b;4 查看状态后记前言 因数据库等保需要&#xff0c;需要对老系统的数据库进行主从切换来…

考出PMP证书到底有没有用?

我们将从三方面分享&#xff1a; 1. PMP 证书在国内的含金量怎么样&#xff1f; 2. HR 如何看待 PMP 证书&#xff1f; 3. 拿到 PMP 证书后&#xff0c;有哪些变化&#xff1f; 一&#xff0c;PMP证书的含金量 说到 PMP 证书的含金量&#xff0c;相信这个问题是所有学员都…

JS语法让人困惑的点 “==与===”

在JS中有很多神奇的语法&#xff0c;非常让人困惑&#xff0c;我们就先一一道来&#xff0c;相信你在开发中或多或少都踩过这些坑&#xff0c;或者让人无法理解。 今天我们就来说下【】和【】 这题对于很多没有系统学过前端开发的技术人员来说&#xff0c;算个重点&#xff0c…

Spring+MVC+MYbatis注解开发

Spring常见注解 注解一&#xff1a;Configuration 用在类上面&#xff0c;加上这个注解的类可以成为一个spring的xml配置文件&#xff0c;使用的是java代码的配置 注解二&#xff1a;ComponentScan 用在类上&#xff0c;加上注解可以指定扫描路径 注解三&#xff1a;创建对…

面试 | 递归乘法【细节决定成败】

不用[ * ]如何使两数相乘❓一、题目明细二、思路罗列 & 代码解析1、野蛮A * B【不符合题意】2、sizeof【可借鉴】解析3、简易递归【推荐】① 解析&#xff08;递归展开图&#xff09;② 时间复杂度分析4、移位<<运算【有挑战性&#x1f4aa;】① 思路顺理② 算法图解…

啥是原神?女友说想要全角色语音+表情包,顺手用python把高清图也整下来了

原神全角色中日语音表情包高清图人生苦短 我用python表情包部分&#xff1a;1. 素材来自&#xff1a;2. 准备模块3. 调用浏览器驱动4. 页面滚动5. 保存数据5. 效果全角色语音高清彩图部分1.准备工具2. 准备模块3. 请求链接4. 本次目标5. 分析数据来源6. 开始代码7. 执行结果8. …

Revit操作 | 门和窗的载入与放置。

想要系统性掌握BIM技能&#xff0c;不能只停留在理论知识上&#xff0c;实操也是关键一步。 拒绝纸上谈兵&#xff0c;提升操作技巧&#xff0c;从点滴开始积累。今天我们来学习Revit实操小技巧之门和窗的载入与放置。 门和窗的载入与放置 门和窗是建筑中最常用的构件。在 Re…

单通道说话人语音分离——Conv-TasNet(Convolutional Time-domain audio separation Network)

单通道说话人语音分离——Conv-TasNet模型(Convolutional Time-domain audio separation Network) 参考文献&#xff1a;《Conv-TasNet: Surpassing Ideal Time-FrequencyMagnitude Masking for Speech Separation》 1.背景 在真实的声学环境中&#xff0c;鲁棒的语音处理通常…

Unity(二)--通过简单例子了解UGUI几个常用对象操作(Text,Image,Button)

目录 文本框等UI对象的添加Canvas 画布给Canvas添加脚本,绑定要操作的对象文本框Text的使用图像Image的使用更换图片Type:显示图片相关按钮Button的使用过渡导航事件绑定文本框等UI对象的添加 Canvas 画布 当创建一个UI元素的时候,如果没有Canvas 画布,就会自动创建一个画布…