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

embedded/2025/1/13 3:07:28/

文章目录

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

项目任务及要求

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

项目实现思路

  • 要实现此项目,首先要知道我们的目标是什么,我们的目标是对银行卡号识别,银行卡号都是数字,因此我们要找到一个模版且模版中有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.ppmy.cn/embedded/153443.html

相关文章

嵌入式C语言:二维数组

目录 一、二维数组的定义 二、内存布局 2.1. 内存布局特点 2.2. 内存布局示例 2.2.1. 数组元素地址 2.2.2. 内存布局图&#xff08;简化表示&#xff09; 2.3. 初始化对内存布局的影响 三、访问二维数组元素 3.1. 常规下标访问方式 3.2. 通过指针访问 3.2.1. 指向数…

C# SQL ASP.NET Web

留学生的课程答疑 按照要求完成程序设计、数据库设计、用户手册等相关技术文档&#xff1b; 要求 1. 计算机相关专业&#xff0c;本科以上学历&#xff0c;至少有1年以上工作经验或实习经历。 2. 熟练掌握WinForm程序开发&#xff0c;或ASP.NET Web编程。 3. 熟悉C#中网络…

vue3模板引用ref

1.访问模板引用 要在组合式 API 中获取引用&#xff0c;我们可以使用辅助函数 useTemplateRef() 只可以在组件挂载后才能访问模板引用 <script setup> import { useTemplateRef, onMounted } from vue// 第一个参数必须与模板中的 ref 值匹配 const input useTempla…

Unity3d 基于Barracuda推理库和YOLO算法实现对象检测功能

前言 近年来&#xff0c;随着AI技术的发展&#xff0c;在游戏引擎中实现和运行机器学习模型的需求也逐渐显现。Unity3d引擎官方推出深度学习推理框架–Barracuda &#xff0c;旨在帮助开发者在Unity3d中轻松地实现和运行机器学习模型&#xff0c;它的主要功能是支持在 Unity 中…

如何训练大型语言模型?

训练大型语言模型&#xff08;LLMs&#xff09;是一个复杂且资源密集的过程&#xff0c;它通常包括以下几个关键步骤&#xff1a; 1. 数据收集 数据收集是训练大型语言模型的第一步。这个过程需要获取大量、高质量的文本数据。数据来源可以是公开可用的网页、新闻文章、社交媒…

79 Openssl3.0 RSA公钥加密数据

1 引言 最近不小心用到了openssl3.0&#xff0c;项目中需要使用rsa非对称加解密算法&#xff0c;所以把openssl3.0使用公钥加密数据的函数调用摸了一遍。 之所以记录此篇文章&#xff0c;是因为网络上大多数是openssl3.0以前的版本的函数接口&#xff0c;而openssl3.0之后已经丢…

如何在应用或系统中正确解析和渲染淘宝商品详情API接口返回的HTML内容?

选择合适的编程语言和工具 后端语言&#xff1a; 如果是在后端处理&#xff0c;像 Python 有许多库可以帮助解析 HTML。例如&#xff0c;BeautifulSoup是一个功能强大且易于使用的 HTML 解析库。首先需要安装BeautifulSoup&#xff0c;可以使用pip install beautifulsoup4命令进…

服务器登陆后有java变量

需求&#xff1a;在ssh服务器后&#xff0c;用户root 使用java会报错&#xff0c;没有这个变量&#xff0c;其实环境变量中已经有配置了&#xff0c;在/etc/profile 中有写变量及地址&#xff0c;通过source /etc/profile 命令也可以使环境变量加载上&#xff0c;但是ssh后不会…