《OpenCV计算机视觉实战项目》——银行卡号识别

文章目录

  • 项目任务及要求
  • 项目实现思路
  • 项目实现及代码
    • 导入模块
    • 设置参数
    • 对模版图像中数字的定位处理
    • 银行卡的图像处理
      • 读取输入图像,预处理
      • 找到数字边框
      • 使用模版匹配,计算匹配得分
    • 画出并打印结果

项目任务及要求

  • 任务书: 要为某家银行设计一套智能卡号识别的系统。
  • 要求:传入一张图片,就自动输出信用卡图片中的数字。

项目实现思路

  • 要实现此项目,首先要知道我们的目标是什么,我们的目标是对银行卡号识别,银行卡号都是数字,因此我们要找到一个模版且模版中有0~9的数字。然后对模版图像中的数字进行定位处理,每一个数字对应一个模版,这样有助于后期进行模版的对照。对模版处理好后,就应对银行卡的图像进行处理,通过对银行卡的一系列处理得到银行卡图像中数字的模版。在进行模版匹配,计算匹配得分,匹配得分最高的就是那个数字,再对数字进行组合、输出得到银行卡号。、

模版图像
在这里插入图片描述

银行卡图像
在这里插入图片描述

项目实现及代码

导入模块

import numpy as np
import argparse  # python内置库  不太熟,自行学习
import cv2
import myutils

其中myutils包需要自己创建,再项目目录创建一个名叫myutils.py文件就行了,其中两个函数分别用来进行排序和改变图像大小,内容为:

import cv2def sort_contours(cnts, method='left-to-right'):# 初始化 reverse 为 False,表示默认不使用逆序排序reverse = False# 初始化 i 为 0,用于后续选择排序依据的维度i = 0# 如果排序方法是 right-to-left 或 bottom-to-top,则设置为逆序排序if method == 'right-to-left' or method == 'bottom-to-top':reverse = True# 如果排序方法是 top-to-bottom 或 bottom-to-top,则选择 i 为 1,表示按垂直维度排序if method == 'top-to-bottom' or method == 'bottom-to-top':i = 1# 计算每个轮廓的外接矩形,并存储在 boundingBoxes 列表中boundingBoxes = [cv2.boundingRect(c) for c in cnts]# 将轮廓和其对应的外接矩形打包在一起,然后根据 lambda 函数指定的规则进行排序# b 是 (cnt, boundingBox) 元组,b[1] 是外接矩形,b[1][i] 表示根据 i 所指定的维度(i = 0 为水平方向,i = 1 为垂直方向)# 根据 reverse 决定是否逆序排序(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),key=lambda b: b[1][i], reverse=reverse))# 返回排序好的轮廓和外接矩形return cnts, boundingBoxesdef resize(image, width=None, height=None, inter=cv2.INTER_AREA):# 初始化 dim 为 None,用于存储调整后的图像尺寸dim = None# 获取图像的高度和宽度(h, w) = image.shape[:2]# 如果宽度和高度都未指定,直接返回原图像if width is None and height is None:return image# 如果仅指定了高度,计算宽度的缩放比例if width is None:r = height / float(h)dim = (int(w * r), height)# 如果仅指定了宽度,计算高度的缩放比例else:r = width / float(w)dim = (width, int(h * r))# 使用 cv2.resize 函数根据 dim 和指定的插值方法对图像进行缩放resized = cv2.resize(image, dim, interpolation=inter)# 返回缩放后的图像return resized

设置参数

  • 通过导入argparse模块
    • 创建 ArgumentParser 对象。
    • 添加参数。
    • 解析命令行参数。
  • 并指定银行卡类型,便于后期从字典中查找。
  • 创建一个函数cv_show来展示图像。
  • 导入参数
    • 在这里插入图片描述
    • 在这里插入图片描述

代码:

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())  # vars()是Python中的一个内置函数,用于返回对象的属性和值的字典。
# 指定信用卡类型
FIRST_NUMBER = {"3": "American Express","4": "Visa","5": "MasterCard","6": "Discover Card"}
def cv_show(name, img):  # 绘图展示cv2.imshow(name, img)cv2.waitKey(0)

对模版图像中数字的定位处理

  • 导入图片
  • 将图片转化为灰度图
  • 再将灰度图再转化为二值图
  • 计算图像轮廓得到图像外轮廓和终点坐标,并再图像中画出
  • 将得到的轮廓,按从左到到右,从上到下排序
  • 通过遍历得到每一个数字对应的像素值
    在这里插入图片描述

代码:

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只保留终点坐标
_, 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)
refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0]  # 排序,从左到右,从上到下
digits = {}  # 保存模板中每个数字对应的像素值
for (i, c) in enumerate(refCnts):  # 遍历每一个轮廓(x, y, w, h) = cv2.boundingRect(c)  # 计算外接矩形并且resize成合适大小roi = ref[y:y + h, x:x + w]roi = cv2.resize(roi, (57, 88))  # 缩放到指定的大小digits[i] = roi  # 每一个数字对应每一个模板

银行卡的图像处理

读取输入图像,预处理

  • 输入图像
  • 重新设置图像大小
  • 转化为灰度图
  • 初始化卷积核
  • 进行顶帽和开运算
    在这里插入图片描述

代码:

# 读取输入图像,预处理
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)
# 顶帽操作,突出图像中的亮细节,清除背景图,原因是背景颜色变化小,不被腐蚀掉。
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))  # 初始化卷积核
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)  # 顶帽 = 原始图像 - 开运算结果(先腐蚀后膨胀)
open = cv2.morphologyEx(gray, cv2.MORPH_OPEN, rectKernel)  # 顶帽 = 原始图像 - 开运算结果(先腐蚀后膨胀)
cv_show('open', open)
cv_show('tophat', tophat)

找到数字边框

  • 通过闭操作(先膨胀,再腐蚀)将数字连在一起并进行二值化处理。
  • 再重复一次上述操作。
  • 计算轮廓并画出
  • 遍历轮廓,根据条件找到数字部分像素区域
  • 遍历每一个轮廓中的数字,得到每一个数字模版
    在这里插入图片描述

代码:

# 1、通过闭操作(先膨胀,再腐蚀)将数字连在一起
closeX = cv2.morphologyEx(tophat, cv2.MORPH_CLOSE, rectKernel)
cv_show('gradX', closeX)
# THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
thresh = cv2.threshold(closeX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show('thresh', thresh)
# 再来一个闭操作
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)  # 再来一个闭操作
cv_show('thresh1', thresh)
# 计算轮廓
_, threshCnts, h = 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):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 = myutils.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)))

画出并打印结果

在这里插入图片描述

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

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

相关文章

【LeetCode: 560. 和为 K 的子数组 + 前缀和 + 哈希表】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Tableau数据可视化与仪表盘搭建-可视化原则及BI仪表盘搭建

目录 可视化原则 BI仪表盘搭建 仪表盘搭建原则 明确仪表盘主题 仪表盘主题拆解 开发设计工作表 经营情况总览&#xff1a;突出显示的文字 经营数据详情&#xff1a;表格 每日营收数据&#xff1a;多轴折线图 每日流量数据&#xff1a;双轴组合图 新老客占比&#xf…

vue2日历组件

这个代码可以直接运行&#xff0c;未防止有组件库没安装&#xff0c;将组件库的代码&#xff0c;转成文字了 vue页面 <template><div class"about"><div style"height: 450px; width: 400px"><div style"height: 100%; overflo…

交响曲-24-3-单细胞CNV分析及聚类

CNV概述 小于1kb是常见的插入、移位、缺失等的变异 人体内包含<10% 的正常CNV&#xff0c;我们的染色体数是两倍体&#xff0c;正常情况下&#xff0c;只有一条染色体表达&#xff0c;另一条沉默&#xff0c;当表达的那条染色体发生CNV之后&#xff0c;表达数量就会成倍增加…

UDP -- 简易聊天室

目录 gitee&#xff08;内有详细代码&#xff09; 图解 MessageRoute.hpp UdpClient.hpp UdpServer.hpp Main.hpp 运行结果&#xff08;本地通信&#xff09; 如何分开对话显示&#xff1f; gitee&#xff08;内有详细代码&#xff09; chat_room zihuixie/Linux_Lear…

电影动画shader解析与实现

着色器代码解析 大家好&#xff01;我是 [数擎AI]&#xff0c;一位热爱探索新技术的前端开发者&#xff0c;在这里分享前端和Web3D、AI技术的干货与实战经验。如果你对技术有热情&#xff0c;欢迎关注我的文章&#xff0c;我们一起成长、进步&#xff01; 开发领域&#xff1a;…

【FlutterDart】 拖动边界线改变列宽类似 vscode 那种拖动改变编辑框窗口大小(11 /100)

【Flutter&Dart】 拖动改变 widget 的窗口尺寸大小GestureDetector&#xff5e;简单实现&#xff08;10 /100&#xff09; 【Flutter&Dart】 拖动边界线改变列宽并且有边界高亮和鼠标效果&#xff08;12 /100&#xff09; 上效果&#xff1a; 这个在知乎里找到的效果&…

【Rust自学】11.1. 编写和运行测试

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 11.1.1. 什么是测试 在Rust里一个测试就是一个函数&#xff0c;它被用于验证非测试代码的功能是否和预期一致。 在一个测试的函数体里通…

数据分析思维(八):分析方法——RFM分析方法

数据分析并非只是简单的数据分析工具三板斧——Excel、SQL、Python&#xff0c;更重要的是数据分析思维。没有数据分析思维和业务知识&#xff0c;就算拿到一堆数据&#xff0c;也不知道如何下手。 推荐书本《数据分析思维——分析方法和业务知识》&#xff0c;本文内容就是提取…

57. Three.js案例-创建一个带有聚光灯和旋转立方体的3D场景

57. Three.js案例-创建一个带有聚光灯和旋转立方体的3D场景 实现效果 该案例实现了使用Three.js创建一个带有聚光灯和旋转立方体的3D场景。 知识点 WebGLRenderer&#xff08;WebGL渲染器&#xff09; THREE.WebGLRenderer 是 Three.js 中用于将场景渲染为 WebGL 内容的核…

Idea-离线安装SonarLint插件地址

地址&#xff1a; SonarQube for IDE - IntelliJ IDEs Plugin | Marketplace 选择Install Plugin from Disk..&#xff0c;选中下载好的插件&#xff0c;然后重启idea

Unity:删除注册表内的项目记录

然后WinR按键输入regedit 打开注册表 在注册表 HKEY CURRENT USER—>SOFTWARE—>Unity—>UnityEditor—>DefaultCompany —>language_Test 中&#xff0c;删除我们的之前存储的语言环境数据。在 “ 三、文本调用和替换 ” 测试时已经将语言环境存储到注册表中了…

JAVA学习记录3

文章为个人学习记录&#xff0c;仅供参考&#xff0c;如有错误请指出。 上期说到使用记事本编写Java程序太过繁琐&#xff0c;所以我们后面都将使用IDEA进行代码的编写、编译和运行。 如何下载安装IDEA&#xff1f; 这个的下载途径也很多&#xff0c;我还是推荐去官网下载(h…

CSS——22.静态伪类(伪类是选择不同元素状态)

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>静态伪类</title> </head><body><a href"#">我爱学习</a></body> </html>单击链接前的样式 左键单击&#xff08;且…

IDEA中Maven依赖包导入失败报红的潜在原因

在上网试了别人的八个问题总结之后依然没有解决&#xff1a; IDEA中Maven依赖包导入失败报红问题总结最有效8种解决方案_idea导入依赖还是报红-CSDN博客https://blog.csdn.net/qq_43705131/article/details/106165960 江郎才尽之后突然想到一个原因&#xff1a;<dep…

GMDH自组织网络模型时间序列预测,可预测未来

GMDH自组织网络模型时间序列预测&#xff0c;可预测未来 目录 GMDH自组织网络模型时间序列预测&#xff0c;可预测未来效果一览基本介绍模型构建程序设计学习总结参考资料 效果一览 基本介绍 GMDH自组织网络模型是自组织数据挖掘中的一种模型方法&#xff0c;是基于计算机科学和…

【docker系列】可视化Docker 管理工具——Portainer

1. 介绍 Portainer是一个可视化的Docker操作界面&#xff0c;提供状态显示面板、应用模板快速部署、容器镜像网络数据卷的基本操作&#xff08;包括上传下载镜像&#xff0c;创建容器等操作&#xff09;、事件日志显示、容器控制台操作、Swarm集群和服务等集中管理和操作、登录…

Linux/Ubuntu/银河麒麟 arm64 飞腾FT2000 下使用 arm64版本 linuxdeployqt 打包Qt程序

文章目录 一、前言二、环境三、准备1、下载Linuxdeployqt源码2、下载Appimagetool-aarch64.AppImage四、编译linuxdeployqt1.配置环境变量2.编译linuxdeployqt五、安装patchelf六、配置Appimagetool七、打包Qt程序重要提示:测试启动应用八、其他九、最后一、前言 因为项目需要…

pg数据库运维经验2024

这篇文章主要是讲pg运维常见问题&#xff0c;两三年见一次的疑难杂症就不说了。 主要是技术性运维总结&#xff0c;主打通俗易懂和快速上手&#xff0c;尽量避免源码层面等深入分析。 SQL性能与执行计划 执行计划突变 pg官方不支持hint功能&#xff0c;并且计划永远不支持&…

Hadoop 实战笔记(一) -- Windows 安装 Hadoop 3.x

环境准备 安装 JAVA 1.8 Java环境搭建之JDK下载及安装下载 Hadoop 3.3.5 安装包 Hadoop 下载&#xff1a;https://archive.apache.org/dist/hadoop/common/ 一、JAVA JDK 环境检查 二、Hadoop(HDFS)环境搭建 1. 解压安装文件 hadoop-3.3.5.tar 2. 配置环境变量 HADOOP_HO…