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

OpenCV 银行卡号识别

目录

一、项目原理与核心技术

二、环境准备与工具包导入

1. 环境依赖

2. 工具包导入

三、自定义工具类 myutils.py 实现

四、主程序核心流程(银行卡识别.py)

1. 命令行参数设置

2. 银行卡类型映射

3. 辅助函数:图像展示

五、步骤 1:模板图像预处理与数字提取

1. 读取模板并预处理

2. 检测模板数字轮廓并排序

六、步骤 2:银行卡图像预处理与卡号识别

1. 读取银行卡图像并预处理

2. 数字区域定位(闭运算 + 二值化 + 轮廓检测)

3. 筛选有效数字区域

4. 模板匹配识别卡号

七、结果展示与输出

1. 打印识别结果

2. 显示最终结果图

八、运行示例与效果验证

1. 准备测试文件

2. 运行命令

3. 预期结果

九、常见问题与优化建议

1. 识别准确率低的原因与解决方法

2. 功能扩展建议


在金融场景中,自动识别银行卡号能极大提升业务处理效率,避免人工录入的误差。本文将详细介绍如何使用 Python+OpenCV 构建一套智能卡号识别系统,通过模板匹配的方式,从银行卡图片中精准提取卡号并判断卡类型,适合 OpenCV 初学者和计算机视觉入门学习者参考。


一、项目原理与核心技术

本系统核心采用模板匹配思想,通过预先制作的数字模板(OCR-A 字体,银行卡常用字体),与待识别银行卡图片中的数字区域进行匹配,计算相似度后确定对应数字。整体流程分为两大模块:模板图像预处理与数字提取、银行卡图像预处理与卡号识别。

涉及的关键技术如下:

  • 图像预处理:灰度化、二值化、形态学操作(顶帽、闭运算),用于突出数字区域、抑制背景干扰。
  • 轮廓检测与排序:通过cv2.findContours检测数字轮廓,并按 “从左到右” 顺序排序,确保数字识别顺序正确。
  • 模板匹配:使用cv2.matchTemplate计算待识别数字与模板的相似度,选择得分最高的模板作为识别结果。

二、环境准备与工具包导入

1. 环境依赖

  • Python 3.6+
  • OpenCV-Python(pip install opencv-python
  • NumPy(pip install numpy

2. 工具包导入

代码中需导入 OpenCV(核心图像处理)、NumPy(数值计算)、argparse(命令行参数解析),以及自定义工具类myutils(封装轮廓排序和图像缩放功能)。

import numpy as np
import argparse
import cv2
import myutils  # 自定义工具类,下文会提供完整代码

三、自定义工具类 myutils.py 实现

myutils.py封装了两个核心函数:轮廓排序图像缩放,是整个系统的基础工具,代码如下:

import cv2def sort_contours(cnts, method='left-to-right'):"""对轮廓进行排序(支持4种排序方式):param cnts: 输入的轮廓列表:param method: 排序方式,默认左到右(left-to-right),可选:right-to-left、top-to-bottom、bottom-to-top:return: 排序后的轮廓列表、对应的边界框列表"""reverse = False  # 是否逆序,默认正序i = 0  # 排序依据:0=x坐标(左右排序),1=y坐标(上下排序)# 若为右到左或下到上,设置逆序if method in ['right-to-left', 'bottom-to-top']:reverse = True# 若为上下排序,按y坐标排序if method in ['top-to-bottom', 'bottom-to-top']:i = 1# 计算每个轮廓的外接矩形,按指定维度排序bounding_boxes = [cv2.boundingRect(c) for c in cnts]# 同时对轮廓和边界框排序(确保一一对应)cnts, bounding_boxes = zip(*sorted(zip(cnts, bounding_boxes),key=lambda item: item[1][i], reverse=reverse))return list(cnts), list(bounding_boxes)  # 返回列表格式,兼容后续操作def resize(image, width=None, height=None, inter=cv2.INTER_AREA):"""图像缩放(保持宽高比,避免拉伸变形):param image: 输入图像:param width: 目标宽度(若为None,按高度缩放):param height: 目标高度(若为None,按宽度缩放):param inter: 插值方式,默认INTER_AREA(适合缩小图像,画质更清晰):return: 缩放后的图像"""dim = None  # 目标尺寸(宽,高)h, w = image.shape[:2]  # 获取原始图像高度和宽度# 若宽高均为None,返回原始图像if width is None and height is None:return image# 按高度缩放(宽度为None时)if 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

四、主程序核心流程(银行卡识别.py)

主程序分为模板处理银行卡处理两大步骤,下面逐模块详细讲解。

1. 命令行参数设置

通过argparse设置两个必填参数:待识别银行卡图片路径(-i/--image)和数字模板图片路径(-t/--template),方便运行时灵活指定输入文件。

# 创建参数解析器
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())  # 解析参数,转为字典格式

2. 银行卡类型映射

根据银行卡号第一位数字,映射对应的卡类型(符合国际标准):

FIRST_NUMBER = {"3": "American Express(美国运通卡)","4": "Visa(维萨卡)","5": "MasterCard(万事达卡)","6": "Discover Card(发现卡)"
}

3. 辅助函数:图像展示

封装cv_show函数,方便在处理过程中查看每一步的图像结果:

def cv_show(name, img):"""显示图像(按任意键关闭窗口):param name: 窗口名称:param img: 待显示图像"""cv2.imshow(name, img)cv2.waitKey(0)  # 等待按键(0表示无限等待)cv2.destroyWindow(name)  # 关闭窗口

五、步骤 1:模板图像预处理与数字提取

模板图片(如kahao.png)包含 0-9 的 OCR-A 字体数字,需先提取每个数字的轮廓,作为后续匹配的 “模板库”。

1. 读取模板并预处理

  • 读取模板图像 → 转为灰度图 → 二值化(THRESH_BINARY_INV:黑底白字,突出数字)。
# 读取模板图像
template_img = cv2.imread(args["template"])
cv_show("模板原始图", template_img)# 1. 转为灰度图
template_gray = cv2.cvtColor(template_img, cv2.COLOR_BGR2GRAY)
cv_show("模板灰度图", template_gray)# 2. 二值化(黑底白字,便于轮廓检测)
# THRESH_BINARY_INV:反转二值化(原白色区域变黑色,原黑色变白色)
template_binary = cv2.threshold(template_gray, 10, 255, cv2.THRESH_BINARY_INV)[1]
cv_show("模板二值化图", template_binary)

2. 检测模板数字轮廓并排序

  • cv2.findContours检测数字外轮廓 → 按 “从左到右” 排序(确保数字顺序为 0-9)→ 提取每个数字的 ROI(感兴趣区域)并缩放为统一尺寸(57×88),存入digits字典(键:0-9,值:对应数字的 ROI 模板)。
# 检测模板中的轮廓(只检测外轮廓,简化轮廓坐标)
_, template_cnts, _ = cv2.findContours(template_binary.copy(), cv2.RETR_EXTERNAL,  # 只检测外轮廓cv2.CHAIN_APPROX_SIMPLE  # 简化轮廓(只保留顶点)
)# 在模板图上绘制轮廓(红色,线宽3),验证轮廓检测结果
cv2.drawContours(template_img, template_cnts, -1, (0, 0, 255), 3)
cv_show("模板轮廓图", template_img)# 按“从左到右”排序轮廓(确保数字顺序为0-9)
sorted_cnts, _ = myutils.sort_contours(template_cnts, method="left-to-right")# 提取每个数字的ROI,构建模板库
digits = {}
for i, cnt in enumerate(sorted_cnts):# 计算轮廓的外接矩形(x:左上角x坐标,y:左上角y坐标,w:宽度,h:高度)x, y, w, h = cv2.boundingRect(cnt)# 提取ROI(数字区域)digit_roi = template_binary[y:y+h, x:x+w]# 缩放ROI为统一尺寸(57×88),便于后续匹配digit_roi = cv2.resize(digit_roi, (57, 88))cv_show(f"数字{i}模板", digit_roi)# 存入字典(键:数字0-9,值:对应ROI模板)digits[i] = digit_roiprint("模板库构建完成,包含数字0-9的ROI模板")

六、步骤 2:银行卡图像预处理与卡号识别

银行卡图片(如card1.pngcard2.png)需经过多步预处理,突出数字区域,再通过模板匹配识别卡号。

1. 读取银行卡图像并预处理

  • 读取图像 → 缩放(统一宽度为 300,避免尺寸差异影响后续处理)→ 转为灰度图 → 顶帽操作(突出亮细节,抑制暗背景,增强数字与背景的对比度)。
# 读取银行卡图像
card_img = cv2.imread(args["image"])
cv_show("银行卡原始图", card_img)# 缩放图像(宽度300,保持宽高比)
card_img_resized = myutils.resize(card_img, width=300)
cv_show("银行卡缩放图", card_img_resized)# 转为灰度图
card_gray = cv2.cvtColor(card_img_resized, cv2.COLOR_BGR2GRAY)
cv_show("银行卡灰度图", card_gray)# 顶帽操作(突出亮细节,消除亮背景干扰)
# 定义矩形卷积核(9×3,适合横向数字区域)
rect_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
# 顶帽 = 原始图像 - 开运算结果(开运算:先腐蚀后膨胀,用于去除小亮点)
card_tophat = cv2.morphologyEx(card_gray, cv2.MORPH_TOPHAT, rect_kernel)
cv_show("银行卡顶帽图(突出数字)", card_tophat)

2. 数字区域定位(闭运算 + 二值化 + 轮廓检测)

  • 闭运算(先膨胀后腐蚀):将数字区域连接成一个整体,便于定位 → 二值化(THRESH_OTSU自动找阈值,适合双峰图像)→ 再次闭运算(填补数字内部的小空隙)→ 轮廓检测(找到所有可能的数字区域)。
# 1. 第一次闭运算:连接数字区域(横向)
card_close1 = cv2.morphologyEx(card_tophat, cv2.MORPH_CLOSE, rect_kernel)
cv_show("第一次闭运算(连接数字)", card_close1)# 2. 二值化(OTSU自动阈值,适合数字与背景对比明显的图像)
_, card_thresh = cv2.threshold(card_close1, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU  # 二值化+自动阈值
)
cv_show("银行卡二值化图", card_thresh)# 3. 第二次闭运算:填补数字内部小空隙(使用5×5正方形核)
sq_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
card_close2 = cv2.morphologyEx(card_thresh, cv2.MORPH_CLOSE, sq_kernel)
cv_show("第二次闭运算(填补空隙)", card_close2)# 4. 检测数字区域轮廓
_, card_cnts, _ = cv2.findContours(card_close2.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
)# 在银行卡图上绘制所有轮廓(红色,线宽3),验证轮廓检测结果
card_cnts_img = card_img_resized.copy()
cv2.drawContours(card_cnts_img, card_cnts, -1, (0, 0, 255), 3)
cv_show("银行卡轮廓检测图", card_cnts_img)

3. 筛选有效数字区域

通过宽高比(AR) 和尺寸范围筛选出真正的数字区域(排除无关轮廓,如卡面 logo、文字等):

# 存储有效数字区域的坐标(x, y, w, h)
valid_locs = []for cnt in card_cnts:x, y, w, h = cv2.boundingRect(cnt)ar = w / float(h)  # 宽高比(数字区域通常为横向,AR>2.5)# 筛选条件:宽高比2.5~4.0,宽度40~55,高度10~20(需根据实际图片调整)if 2.5 < ar < 4.0 and 40 < w < 55 and 10 < h < 20:valid_locs.append((x, y, w, h))# 按“从左到右”排序有效区域(确保卡号顺序正确)
valid_locs = sorted(valid_locs, key=lambda x: x[0])
print(f"找到{len(valid_locs)}个数字区域")

4. 模板匹配识别卡号

遍历每个有效数字区域,提取 ROI → 预处理(二值化)→ 检测单个数字轮廓 → 模板匹配(计算与每个模板的相似度)→ 选择得分最高的模板作为识别结果,最终拼接成完整卡号。

# 存储最终识别结果
card_number = []# 遍历每个有效数字区域
for i, (gx, gy, gw, gh) in enumerate(valid_locs):# 提取数字区域ROI(适当扩大边界5个像素,避免裁剪到数字)group_roi = card_gray[gy-5:gy+gh+5, gx-5:gx+gw+5]cv_show(f"数字区域{i}", group_roi)# 预处理:二值化(突出单个数字)_, group_binary = cv2.threshold(group_roi, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)cv_show(f"数字区域{i}二值化", group_binary)# 检测单个数字轮廓(每个数字区域包含4个数字,如“4000”)_, digit_cnts, _ = cv2.findContours(group_binary.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 按“从左到右”排序单个数字轮廓sorted_digit_cnts, _ = myutils.sort_contours(digit_cnts, method="left-to-right")# 识别当前区域的4个数字group_result = []for cnt in sorted_digit_cnts:# 提取单个数字的ROI并缩放为模板尺寸(57×88)x, y, w, h = cv2.boundingRect(cnt)digit_roi = group_binary[y:y+h, x:x+w]digit_roi = cv2.resize(digit_roi, (57, 88))cv_show("单个数字ROI", digit_roi)# 模板匹配:计算与每个模板的相似度(使用TM_CCOEFF方法,值越大相似度越高)match_scores = []for digit, digit_template in digits.items():# 模板匹配result = cv2.matchTemplate(digit_roi, digit_template, cv2.TM_CCOEFF)_, score, _, _ = cv2.minMaxLoc(result)  # 获取最大相似度得分match_scores.append(score)# 选择得分最高的模板对应的数字(np.argmax返回最大得分的索引,即数字0-9)best_digit = str(np.argmax(match_scores))group_result.append(best_digit)# 在银行卡图上绘制矩形框和识别结果(红色框,红色文字)cv2.rectangle(card_img_resized, (gx-5, gy-5),  # 左上角坐标(gx+gw+5, gy+gh+5),  # 右下角坐标(0, 0, 255), 1  # 红色,线宽1)# 绘制文字(位置在矩形框上方,字体大小0.65,红色,线宽2)cv2.putText(card_img_resized, "".join(group_result),  # 识别的数字(如“4000”)(gx, gy-15),  # 文字左上角坐标cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)# 将当前区域的识别结果添加到总卡号中card_number.extend(group_result)

七、结果展示与输出

1. 打印识别结果

输出银行卡类型(通过卡号第一位数字映射)和完整卡号:

# 打印银行卡类型和卡号
card_type = FIRST_NUMBER.get(card_number[0], "Unknown Card Type(未知卡类型)")
print(f"\n银行卡类型:{card_type}")
print(f"银行卡号:{' '.join(card_number[i:i+4] for i in range(0, len(card_number), 4))}")

2. 显示最终结果图

cv_show("银行卡号识别结果", card_img_resized)
cv2.destroyAllWindows()  # 关闭所有窗口


八、运行示例与效果验证

1. 准备测试文件

  • 模板图片:kahao.png(包含 0-9 的 OCR-A 字体数字)。

  • 银行卡图片:card1.png(卡号 4000 1234 5678 9010,)、card2.png(万事达卡,卡号 5412 7512 3456 7890)等。

2. 运行命令

在终端中执行以下命令(需替换为实际文件路径):

python 银行卡识别.py -i card1.png -t kahao.png

3. 预期结果

  • 依次弹出每一步的图像处理窗口(模板轮廓、银行卡顶帽图、数字区域 ROI 等)。
  • 最终输出:
    银行卡类型:Visa(维萨卡)
    银行卡号:4000 1234 5678 9010
    
  • 显示标注了卡号的银行卡图片,红色矩形框包围数字区域,上方显示识别的数字。

九、常见问题与优化建议

1. 识别准确率低的原因与解决方法

  • 问题 1:数字区域未被正确检测(轮廓筛选条件不合适)。
    解决:调整valid_locs筛选时的宽高比(AR)和尺寸范围(如宽度 40~60,高度 8~22),根据实际银行卡图片尺寸优化。
  • 问题 2:模板匹配得分低(数字 ROI 与模板尺寸不匹配)。
    解决:确保数字 ROI 缩放后的尺寸与模板尺寸一致(本文为 57×88),若模板字体不同,需重新制作 OCR-A 字体模板。
  • 问题 3:图像噪声干扰(如卡面反光、污渍)。
    解决:增加高斯模糊(cv2.GaussianBlur)预处理步骤,减少噪声影响,例如:
    card_gray = cv2.GaussianBlur(card_gray, (3, 3), 0)  # 在灰度图后添加高斯模糊
    

2. 功能扩展建议

  • 支持更多卡类型:在FIRST_NUMBER字典中添加更多卡类型映射(如 “2” 对应 MasterCard Maestro)。
  • 批量识别:修改代码支持批量读取文件夹中的银行卡图片,自动输出所有识别结果到文档(如 Excel、TXT)。
  • 实时识别:结合摄像头(cv2.VideoCapture),实现实时拍摄银行卡并识别卡号。

文章转载自:

http://F64kiudq.hxgLy.cn
http://0iNgxuIc.hxgLy.cn
http://QLHAGViF.hxgLy.cn
http://Q4mAV3IU.hxgLy.cn
http://wW88P15f.hxgLy.cn
http://LTmaiKIX.hxgLy.cn
http://GxX4nFoY.hxgLy.cn
http://PvIN86mm.hxgLy.cn
http://kfO8kR8G.hxgLy.cn
http://OVZLGUsJ.hxgLy.cn
http://jPmKjkQ8.hxgLy.cn
http://3pOT0VoP.hxgLy.cn
http://es15Thqo.hxgLy.cn
http://TYJe6Z3N.hxgLy.cn
http://GDRwTg8W.hxgLy.cn
http://ZxfMwbhE.hxgLy.cn
http://Eg8tp2qw.hxgLy.cn
http://5iE96KEo.hxgLy.cn
http://tjUalsJg.hxgLy.cn
http://XT9kH1s3.hxgLy.cn
http://kqmlKzaM.hxgLy.cn
http://TnS3Dlq2.hxgLy.cn
http://IbYQiRJh.hxgLy.cn
http://m1JSrrS8.hxgLy.cn
http://5xF6qawC.hxgLy.cn
http://4mopgTYa.hxgLy.cn
http://GdnKZjpM.hxgLy.cn
http://yXzOeALm.hxgLy.cn
http://DQK6du7U.hxgLy.cn
http://1X9JkcPF.hxgLy.cn
http://www.dtcms.com/a/372726.html

相关文章:

  • CentOS 8重启后网卡不见了解决办法
  • Nginx反向代理和负载均衡详解及使用Nginx和tomcat共同实现动静分离配置
  • UDP的使用
  • WGAI项目图像视频语音识别功能
  • 9.3深度循环神经网络
  • 【嵌入式硬件实例】-555定时器实现自动晚灯(220V)
  • Linux Shell | set、env、export 用法区别
  • 浅聊一下微服务的服务保护
  • Nginx 实战系列(五)—— Nginx流量监控:从stub_status到nginx-module-vts的进阶指南
  • 34. 什么是反射
  • YOLO11 改进、魔改|通道自注意力卷积块CSA-ConvBlock,实现 “轻量化特征增强”
  • 优先搜索(DFS)实战
  • 计算机视觉opencv----银行卡号码识别
  • 第六章、从transformer到nlp大模型:编码器-解码器模型 (Encoder-Decoder)
  • pymodbus启动一个简单的modbus tcp server
  • 【NowCoder】牛客周赛 Round 108 EF (背包问题 | SOSDP)
  • 【ARMday02】
  • OFDR设备开机到出图的5个关键操作步骤
  • ArcGIS学习-19 实战-表面分析
  • 【算法】双指针(二)复写零
  • 视频串行解串器(SerDes)介绍
  • PyTorch 动态图的灵活性与实用技巧
  • 【P01_AI测试开发课程-导论】
  • 从社交破冰到学习规划,鸿蒙5开启智慧校园新生活
  • 【Linux操作系统】简学深悟启示录:文件fd
  • Kata Container 部署与应用实践
  • 【CentOS7】docker安装成功后测试,报Unable to find image ‘hello-world:latest‘ locally
  • springboot配置请求日志
  • 2-ATSAMV71Q21-BOOT
  • 【Qt开发】显示类控件(一)-> QLabel