《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.rhkb.cn/news/303.html

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

相关文章

PyCharm文档管理

背景&#xff1a;使用PyCharmgit做文档管理 需求&#xff1a;需要PyCharm自动识别docx/xslx/vsdx等文件类型&#xff0c;并在PyCharm内点击文档时唤起系统内关联应用(如word、excel、visio) 设置步骤&#xff1a; 1、file -》 settings -》file types 2、在Files opened i…

景联文科技提供高质量多模态数据处理服务,驱动AI新时代

在当今快速发展的AI时代&#xff0c;多模态数据标注成为推动人工智能技术进步的关键环节。景联文科技作为行业领先的AI数据服务提供商&#xff0c;专注于为客户提供高质量、高精度的多模态数据标注服务&#xff0c;涵盖图像、语音、文本、视频及3D点云等多种类型的数据。通过专…

django基于Python的智能停车管理系统

1.系统概述 1.定义&#xff1a;Django 基于 Python 的智能停车管理系统是一个利用 Django 框架构建的软件系统&#xff0c;用于高效地管理停车场的各种事务&#xff0c;包括车辆进出记录、车位预订、收费管理等诸多功能。 2.目的&#xff1a;它的主要目的是提高停车场的运营效…

【Rust】切片类型

目录 思维导图 1. 切片类型概述 2. 函数示例&#xff1a;获取字符串中的第一个单词 2.1 问题描述 2.2 初步实现 2.3 代码实现 3. 切片的引入 3.1 切片的定义 3.2 切片的优势 3.3 改进后的函数 4. 函数参数的通用性 4.1 改进函数签名 4.2 示例代码 5. 其他切片类型…

微信小程序-Docker+Nginx环境配置业务域名验证文件

在实际开发或运维工作中&#xff0c;我们时常需要在 Nginx 部署的服务器上提供一个特定的静态文件&#xff0c;用于域名验证或第三方平台验证。若此时使用 Docker 容器部署了 Nginx&#xff0c;就需要将该验证文件正确地映射&#xff08;挂载&#xff09;到容器中&#xff0c;并…

鸿蒙UI(ArkUI-方舟UI框架)

参考&#xff1a;https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/arkts-layout-development-overview-V13 ArkUI简介 ArkUI&#xff08;方舟UI框架&#xff09;为应用的UI开发提供了完整的基础设施&#xff0c;包括简洁的UI语法、丰富的UI功能&#xff…

图片和短信验证码(头条项目-06)

1 图形验证码接口设计 将后端⽣成的图⽚验证码存储在redis数据库2号库。 结构&#xff1a; {img_uuid:0594} 1.1 创建验证码⼦应⽤ $ cd apps $ python ../../manage.py startapp verifications # 注册新应⽤ INSTALLED_APPS [django.contrib.admin,django.contrib.auth,…

SpringCloud系列教程:微服务的未来(十二)OpenFeign连接池、最佳实践、日志、微服务拆分

本篇博客将讨论如何优化 OpenFeign 的连接池配置&#xff0c;如何使用最佳实践提升服务间通信的效率和可维护性&#xff0c;并探讨如何通过拆分服务来提升微服务架构的灵活性和可扩展性&#xff0c;具体涵盖了用户服务和交易服务的拆分。 目录 前言 OpenFeign 连接池 最佳实…

进阶——十六届蓝桥杯嵌入式熟练度练习(LED的全开,全闭,点亮指定灯,交替闪烁,PWM控制LED呼吸灯)

点亮灯的函数 void led_show(unsigned char upled) { HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOC,upled<<8,GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RE…

动态规划【打家劫舍】

今天和大家分享一下动态规划当中的打家劫舍题目&#xff0c;希望在大家刷题的时候提供一些思路 打家劫舍1&#xff1a; 题目链接&#xff1a; 198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋…

Jaeger UI使用、采集应用API排除特定路径

Jaeger使用 注&#xff1a; Jaeger服务端版本为&#xff1a;jaegertracing/all-in-one-1.6.0 OpenTracing版本为&#xff1a;0.33.0&#xff0c;最后一个版本&#xff0c;停留在May 06, 2019。最好升级到OpenTelemetry。 Jaeger客户端版本为&#xff1a;jaeger-client-1.3.2。…

Vue-Cli

一.vue.js 1.优点 1)体积小,压缩后33K 2)更高的运行效率 3)双向数据绑定 4)生态丰富,学习成本低 2.Vue指令 指令带有前缀 v- 开头&#xff0c;以表示它们是 Vue 提供的特殊属性。 1)v-text (1)作用 &#xff1a;设置标签的文本内容 默认写法会替换全部内容&#xff0c…

【源码解析】Java NIO 包中的 ByteBuffer

文章目录 1. 前言2. ByteBuffer 概述3. 属性4. 构造器5. 方法5.1 allocate 分配 Buffer5.2 wrap 映射数组5.3 slice 获取子 ByteBuffer5.4 duplicate 复刻 ByteBuffer5.5 asReadOnlyBuffer 创建只读的 ByteBuffer5.6 get 方法获取字节5.7 put 方法往 ByteBuffer 里面加入字节5.…

HTML5实现好看的中秋节网页源码

HTML5实现好看的中秋节网页源码 前言一、设计来源1.1 网站首页界面1.2 登录注册界面1.3 节日由来界面1.4 节日习俗界面1.5 节日文化界面1.6 节日美食界面1.7 节日故事界面1.8 节日民谣界面1.9 联系我们界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载结束语 HTML5实现好看…

OA项目登录

导入依赖,下面的依赖是在这次OA登录中用到的 <!--web依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.sprin…

YangQG 面试题汇总

一、交叉链表 问题&#xff1a; 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 解题思想&#xff1a; 双指针 备注&#xff1a;不是快慢指针&#xff0c;如果两个长度相…

【面试题】技术场景 4、负责项目时遇到的棘手问题及解决方法

工作经验一年以上程序员必问问题 面试题概述 问题为在负责项目时遇到的棘手问题及解决方法&#xff0c;主要考察开发经验与技术水平&#xff0c;回答不佳会影响面试印象。提供四个回答方向&#xff0c;准备其中一个方向即可。 1、设计模式应用方向 以登录为例&#xff0c;未…

WEB前端-3.2

目录 css 【例】飙升榜 【源码】 css 【例】飙升榜 【源码】 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"&g…

qml SpringAnimation详解

1. 概述 SpringAnimation 是 Qt Quick 中用于模拟弹簧效果的动画类。它通过模拟物体在弹簧力作用下的反应&#xff0c;产生一种振荡的动画效果&#xff0c;常用于模拟具有自然回弹、弹性和振动的动态行为。这种动画效果在 UI 中广泛应用&#xff0c;特别是在拖动、拉伸、回弹等…

新活动平台建设历程与架构演进

01 前言 历时近两年的重新设计和迭代重构&#xff0c;用户技术中心的新活动平台建设bilibili活动中台终于落地完成&#xff01;并迎来了里程碑时刻 —— 接过新老迭代的历史交接棒&#xff0c;从内到外、从开发到搭建实现全面升级&#xff0c;开启了活动生产工业化新时代&#…