当前位置: 首页 > news >正文

OpenCV篇——项目(一)OCR识别读取银行卡号码

目录

信用卡数字识别系统:前言与代码解析

前言

项目代码

​​​​​​结果演示

代码模块解析

1. 参数解析模块

2. 轮廓排序函数

3. 图像预处理模块

4. 输入图像处理流程

5. 卡号区域定位

6. 数字识别与输出

系统优势


信用卡数字识别系统:前言与代码解析

前言

信用卡数字识别是金融自动化处理的核心技术之一,通过计算机视觉技术自动提取卡面信息,可应用于支付验证、身份认证等场景。本系统基于模板匹配和图像处理技术,实现对信用卡卡号的自动识别。系统通过预处理、轮廓检测和特征匹配三个关键阶段,准确识别信用卡上的16位数字,并自动判断发卡机构(Visa/MasterCard等)。以下将详细解析代码各模块功能。


项目代码

import argparse
import imutils
import numpy as np
import myutils
from imutils import contours
import cv2# 设置参数结果
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,help="path to input image containing credit card")
ap.add_argument("-t", "--template", required=True,help="path to input template image")
args = vars(ap.parse_args())# 指定信用卡类型
FIRST_NUMBER = {"3": "American Express","4": "Visa","5": "MasterCard","6": "Discover Card"
}def sort_contours(cnts, method="left-to-right"):reverse = Falsei = 0if method == "right-to-left" or method == "bottom-to-top":reverse = Trueif method == "top-to-bottom" or method == "bottom-to-top":i = 1boundingBoxes = [cv2.boundingRect(c) for c in cnts]  # 用一个最小的矩形,把找到的形状包起来x,y,h,w(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(h, w) = image.shape[:2]if width is None and height is None:return imageif width is None:r = height / float(h)dim = (int(w * r), height)else:r = width / float(w)dim = (width, int(h * r))resized = cv2.resize(image, dim, interpolation=inter)return resized# 绘图展示
def cv_show(name,img):cv2.imshow(name,img)cv2.waitKey(0)cv2.destroyAllWindows()return None# 读取一个模板
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)# 计算轮廓果
ref_,refCnts = cv2.findContours(ref.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,ref_,-1,(0,0,255),3)
cv_show("img",img)
print(np.array(refCnts).shape)
refCnts =sort_contours(ref_, method="left-to-right")[0]
digits = {}# 遍历模板轮廓
for (i,c) in enumerate(refCnts):(x,y,w,h) = cv2.boundingRect(c)roi = ref[y:y+h,x:x+w]roi = cv2.resize(roi,(57,88))digits[i] = roi# 初始化卷积核
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))
squareKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))# 读取输入图像、预处理
image = cv2.imread(args["image"])
cv_show("image",image)
image = resize(image,width=300)
gary= cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv_show("gary",gary)# 礼帽操作,突出更明亮的区域
tophat = cv2.morphologyEx(gary,cv2.MORPH_TOPHAT,rectKernel)
cv_show("tophat",tophat)
gradx=cv2.Sobel(tophat,ddepth=cv2.CV_32F,dx=1,dy=0,ksize=-1)
gradx = np.absolute(gradx)
(minVal,maxVal) = (np.min(gradx),np.max(gradx))
gradx = (255 * ((gradx - minVal) / (maxVal - minVal)))
gradx = gradx.astype("uint8")
print(np.array(gradx).shape)
cv_show("gradx",gradx)# 通过闭操作(先膨胀,再腐蚀)将数字连在一起
gradx = cv2.morphologyEx(gradx,cv2.MORPH_CLOSE,rectKernel)
cv_show("gradx",gradx)
# THRESH_OTSU会自动寻找全局阈值,适合双峰,需要把阈值参数设置为0
thresh = cv2.threshold(gradx,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show("thresh",thresh)# 再来一个闭操作
thresh = cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,squareKernel)
cv_show("thresh",thresh)# 计算轮廓
thresh_,threshCnts = cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = thresh_
cur_img=image.copy()
cv2.drawContours(cur_img,cnts,-1,(0,0,255),3)
cv_show("cur_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):# initialzie the list of group digitsgroupOutput = []# 根据坐标提取数字的区域group = gary[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_,groupCnts = cv2.findContours(group.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)group_ = contours.sort_contours(group_,method="left-to-right")[0]# 计算每一个组中的数字for c in group_:(x,y,w,h) = cv2.boundingRect(c)digit = group[y:y+h,x:x+w]digit = cv2.resize(digit,(57,88))cv_show("digit",digit)# 计算匹配得分scores = []# 在模板中计算每一个得分for (digitt,digitTempl) in digits.items():result = cv2.matchTemplate(digit,digitTempl,cv2.TM_CCOEFF)(_,score,_,_) = cv2.minMaxLoc(result)scores.append(score)# 获得匹配得分最适合的数字groupOutput.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(groupOutput),(gx,gy-15),cv2.FONT_HERSHEY_SIMPLEX,0.65,(0,0,255),2)# 得到结果output.extend(groupOutput)
# 打印结果
print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Image", image)
cv2.waitKey(0)

注意事项:

由于作者用于较新的PyCharm版本是2024版本,它与传统的PyCharm有区分,而且作者在学习过程中同样发现一些问题,这个代码前面可以不用写排序,而最新版本里面不知什么原因调用接口时候报错说没有这个模块函数,于是作者只能将排序等需要的模块分开出来,写入项目中。

​​​​​​结果演示

代码模块解析

1. 参数解析模块
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="输入信用卡图像路径")
ap.add_argument("-t", "--template", required=True, help="模板图像路径")
args = vars(ap.parse_args())
  • 功能:通过命令行参数接收输入图像和模板图像路径
  • 参数说明
    • -i/--image:待识别的信用卡图像
    • -t/--template:数字模板图像(包含0-9标准数字)

PyCharm2024通过argparse模块操作命令行设置方法如下,接下来的步骤是中文展示,作者PyCharm已经汉化,若未汉化也可记作者选择按钮的位置点击:

第一步:将需要的图片存放在本人知道的文件路径

原图片如下:

模板图片如下:

我存放的路径如下:

第二步:右击项目代码,选择“更多运行/调试”后,在选择下拉列表中的“修改运行配置”

第三步:在新打开的对话框选择“运行”栏下最后一行点击“展开”

第四步:输入之前存放模板和原图片的路径

注:由于作者存放图片和模板与项目同一个文件夹目录下,因此可以省略具体的路径,若用户存放不在同一个文件夹目录下,那么需要你加上如下格式:

--image

盘符:\文件夹1名称\文件夹2名称\.....\图片名称和后缀名(.png,.jpg等等)

--template

盘符:\文件夹1名称\文件夹2名称\.....\图片名称和后缀名(.png,.jpg等等)

2. 轮廓排序函数
def sort_contours(cnts, method="left-to-right"):reverse = Falsei = 0if method == "right-to-left" or method == "bottom-to-top":reverse = Trueif method == "top-to-bottom" or method == "bottom-to-top":i = 1boundingBoxes = [cv2.boundingRect(c) for c in cnts]  # 用一个最小的矩形,把找到的形状包起来x,y,h,w(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),key=lambda b: b[1][i], reverse=reverse))return cnts, boundingBoxes

  • 功能:对检测到的轮廓按空间位置排序
  • 排序逻辑
    • 计算每个轮廓的边界框 (x,y,w,h)
    • method参数选择排序基准(X轴或Y轴坐标)
    • 支持四种排序方向:左→右、右→左、上→下、下→上
3. 图像预处理模块
# 模板预处理流程
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 灰度化
ref = cv2.threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1]  # 二值化反转

  • 关键操作
    • 灰度转换:将RGB图像转为单通道灰度图
    • 二值化:通过阈值处理突出数字区域
    • 轮廓提取findContours定位每个数字的独立轮廓
    • 模板存储:将0-9数字按索引存入字典digits
4. 输入图像处理流程
# 核心处理链
image = resize(image, width=300)  # 尺寸标准化
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  # 灰度化
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)  # 顶帽运算
gradx = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0)  # Sobel边缘检测
gradx = cv2.morphologyEx(gradx, cv2.MORPH_CLOSE, rectKernel)  # 闭运算

  • 处理阶段
    1. 尺寸归一化:固定宽度为300像素,保持比例
    2. 顶帽运算:突出亮色区域(信用卡数字通常为亮色)
    3. Sobel算子:检测垂直边缘(数字的竖直笔画)
    4. 形态学闭操作:连接数字笔画形成连续区域
5. 卡号区域定位
# 数字区域筛选
for (i,c) in enumerate(cnts):(x,y,w,h) = cv2.boundingRect(c)ar = w / float(h)  # 宽高比if 2.5 < ar < 4.0 and 40<w<55 and 10<h<20:locs.append((x,y,w,h))  # 保存候选区域

  • 筛选条件
    • 宽高比 $2.5 < \frac{w}{h} < 4.0$(信用卡数字的典型比例)
    • 宽度 $40 < w < 55$ 像素
    • 高度 $10 < h < 20$ 像素
  • 结果:获得4组数字区域的坐标 locs
6. 数字识别与输出
# 模板匹配识别
for c in group_:digit = cv2.resize(roi, (57,88))  # 标准化尺寸scores = []for digitTempl in digits.values():score = cv2.matchTemplate(digit, digitTempl, cv2.TM_CCOEFF)scores.append(score)  # 存储匹配得分groupOutput.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(groupOutput), (gx,gy-15), cv2.FONT_HERSHEY_SIMPLEX, ...)
# 得到结果
output.extend(groupOutput)
# 打印结果
print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Image", image)
cv2.waitKey(0)

  • 识别流程
    1. 截取单个数字区域并缩放至模板尺寸
    2. 与0-9模板进行相似度匹配(TM_CCOEFF相关系数法)
    3. 选择最高匹配得分对应的数字
  • 输出形式
    • 红色矩形框标记数字组区域
    • 在区域上方显示识别出的4位数字

系统优势

  1. 形态学操作链:通过顶帽、闭操作等组合优化数字区域提取
  2. 动态模板匹配:适应不同字体和尺寸的数字
  3. 空间约束:利用宽高比和尺寸过滤误检区域
  4. 实时可视化:各阶段结果可实时显示便于调试

该系统实现了从原始图像到卡号识别的完整流程,准确率依赖模板质量和图像清晰度,可通过优化预处理参数进一步提升性能。

http://www.dtcms.com/a/264337.html

相关文章:

  • 在 Docker 容器中使用内网穿透
  • ubuntu 安装QT
  • 【python】三元表达式
  • Jeecg前端经验汇总
  • 扫描电子显微镜(SEM)夏令营面试基础题及答案
  • 五个元素一行均分三个换行且有间距,如何均分布局
  • 设计模式之上下文对象设计模式
  • 机器学习在智能金融风险评估中的应用:信用评分与欺诈检测
  • 电脑键盘不能打字了怎么解决 查看恢复方法
  • 无人机一机多控技术要点难点
  • Redis 概述及安装
  • 设计模式之组合模式
  • RabbitMQ:消息队列的轻量级王者
  • 系统安全之大模型案例分析
  • 用openCV实现基础的人脸检测与情绪识别
  • 磐维数据库panweidb3.1.0单节点多实例安装
  • 【Python】断言(assert)
  • 1.MySQL之如何定位慢查询
  • 隔离网络(JAVA)
  • 【前端】vue工程环境配置
  • linux 用户态|内核态打印函数调用进程的pid
  • OEM怎么掌握软件开发能力
  • Linux CentOS环境下Java连接MySQL数据库指南
  • Golang的代码结构设计原则与实践与模式应用
  • 解码知识整理,使您的研究更高效!
  • Java项目:基于SSM框架实现的中学教学管理系统【ssm+B/S架构+源码+数据库+毕业论文+开题报告】
  • [创业之路-458]:企业经营层 - 蓝海战略 - 重构价值曲线、整合产业要素、创造新需求
  • 软件产品使用说明编写需要注意的内容避坑指南
  • Day 3:Python模块化、异常处理与包管理实战案例
  • 【每天一个知识点】均值偏移(Mean-Shift)