文章目录
- 前言
- 一、项目整体介绍与环境准备
- 二、代码详解与介绍
- 代码整体执行流程
- 导入库
- 命令行参数
- 业务字典
- 自定义图像显示函数 cv_show
- 图像预处理
- 轮廓检测
- 轮廓排序、单个数字区域提取
- 总结
前言
本文基于 OpenCV + Python ,实现银行卡卡号数字提取、模板数字匹配功能,面向编程、计算机视觉零基础学习者。
一、项目整体介绍与环境准备
日常生活中银行卡卡号由固定字体、固定大小的数字组成,我们先准备一张标准数字模板图(0~9 十个数字依次排列),通过 OpenCV 图像处理技术:
读取数字模板图片;
对图片做灰度、二值化预处理,让数字和背景区分更明显;
检测图片中每一个数字的轮廓;
对轮廓从左到右排序,保证数字顺序不乱;
逐个截取 0~9 单个数字,保存为模板,供后续识别真实银行卡图片使用。
银行卡图片:
卡号图片:
首先安装环境:
# 安装OpenCV、数值计算库
pip install opencv–python numpy
然后再准备两个文件: 图片文件:card1.png(待识别银行卡)、kahao.png(数字模板图,0-9 横向排列) myutils.py(自定义工具脚本,用作轮廓排序)。 myutils.py:
import cv2
def sort_contours(cnts, method="left-to-right"):
reverse = False
i = 0
# 判断排序方式
if method == "right-to-left" or method == "bottom-to-top":
reverse = True
if method == "top-to-bottom" or method == "bottom-to-top":
i = 1
# 计算外接矩形并排序
boundingBoxes = [cv2.boundingRect(c) for c in cnts]
(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
key=lambda b: b[1][i], reverse=reverse))
return cnts, boundingBoxes
二、代码详解与介绍
代码整体执行流程
导入所有依赖库;
创建命令行参数,接收银行卡图片和数字模板图片路径;
定义银行卡卡号首位数字与卡种的映射字典;
定义自定义窗口显示函数;
读取数字模板图片;
图像预处理:彩色图 → 灰度图 → 二值黑白图;
检测图像中所有数字轮廓,并绘制轮廓;
对轮廓从左到右排序,保证数字顺序为 0~9;
逐个截取单个数字区域,统一尺寸,存入字典;
打印最终提取的数字模板字典,程序结束。
导入库
import numpy as np
import argparse
import cv2
import myutils
import argparse是Python 内置命令行参数解析模块。
import myutils会导入自定义工具脚本 myutils.py。 把重复使用的工具函数(轮廓排序、图像缩放、旋转等)单独封装,简化主代码。
命令行参数
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())
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')
-i:短参数名(命令行简写,使用方便);–image:长参数名(代码内部使用的变量名,语义清晰);required=True:必传参数。运行代码时必须传入该参数,否则程序直接报错,强制用户输入图片路径;help:参数说明,输入 –help 时会展示提示文字,方便使用者理解参数作用。
args = vars(ap.parse_args())
vars():将解析后的参数对象,转为 Python 字典; 我们进入程序后右击鼠标,进去设置脚本形参:
设置结束点击‘Apply’应用即可,否则程序将会报错。
业务字典
FIRST_NUMBER={'3':'American Express',
'4':"Visa",
"5": "MasterCard",
"6":"Discover Card"}
卡号以 3 开头 → American Express 卡号以 4 开头 → Visa 卡号以 5 开头 → MasterCard 卡号以 6 开头 → Discover Card 根据银行卡第一位卡号数字判断银行卡类型。
自定义图像显示函数 cv_show
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
封装为工具函数,一行调用。 使用格式:
cv_show('xxx', img)
图像预处理
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)
读取图像
img = cv2.imread(args['template'])
从命令行参数字典中,取出数字模板图 kahao.png 的路径。
彩色图转灰度图
ref = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
即使图片是黑白色的,我们依旧要使用转灰度图,这是因为彩图有3个颜色通道,灰度图只有一个通道,计算快捷,简化运算。
图像二值化
ref = cv2.threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1]
将灰度图只保留两种像素值:纯黑 (0)、纯白 (255)。 但是代码使用的是反向二值化,这是为了边缘检测时不会检测到边缘的那一圈轮廓
使用反向二值化就可以避免这个问题。 
轮廓检测
refCnts = cv2.findContours(ref,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[–2]
cv2.drawContours(img,refCnts,–1,(0,0,255),3)
只检测最外层轮廓,忽略内部嵌套轮廓,数字内部也是有轮廓的。 refCnts是上一步检测到的轮廓列表,-1绘制所有轮廓,(0,0,255):绘制颜色,OpenCV BGR 格式 → (0,0,255) = 红色,3:轮廓线条宽度,数值越大线条越粗。
轮廓排序、单个数字区域提取
refCnts = myutils.sort_contours(refCnts,method='lert=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))
cv_show('roi',roi)
digits[i] = roi
print(digits)
调用自定义工具 myutils.sort_contours,对轮廓排序,排序规则 从左到右。
检测到10个最小外接矩阵,对x排序进行排序,x越小,数字越小。
截取感兴趣区域 ROI 对图片进行切片,截取单个数字。
roi = ref[y:y+h,x:x+w]
统一数字尺寸 resize
roi = cv2.resize(roi,(57,88))
将所有数字统一缩放为 宽 57、高 88

总结
读者朋友们可以通过学习前半段代码,熟练掌控模块匹配算法,后续会有完整项目放出,请耐心等待,谢谢。


