【计算机视觉】OpenCV 模板匹配银行卡数字识别---下

文章目录


前言

在上篇教程中,我们已经完成了项目环境搭建、工具脚本封装、数字模板图预处理、0-9数字模板提取存储的全部核心操作,成功将标准模板图中的十个数字拆解为独立、统一尺寸的数字样本字典,为后续真实银行卡数字识别搭建了核心模板库。

本篇作为项目下篇核心实战内容,将重点讲解真实银行卡图像的全套图像处理流程、数字区域精准定位、单数字拆分、模板匹配识别、结果可视化输出全链路逻辑。全程延续零基础友好的讲解风格,逐行拆解代码原理、图像处理底层逻辑、参数设置依据,让大家不仅能跑通代码,更能理解每一步图像变换的意义,彻底掌握OpenCV模板匹配数字识别的核心思路。


信用卡图像处理

不同于干净的标准模板图,真实银行卡图片存在背景复杂、光线不均、底色干扰、纹理杂乱等问题,直接轮廓检测无法精准提取数字,因此需要通过缩放、灰度转换、形态学运算、二值化层层优化图像质量,凸显数字特征,弱化背景干扰。

完整代码:

c 复制代码
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)
cv_show('tophat',tophat)

首先读取图片,从命令行参数中读取我们传入的待识别银行卡图片,此时读取的图像包含完整的色彩、背景、文字、图案所有信息,是原始未处理的图像。

c 复制代码
image = cv2.imread(args['image'])
cv_show('image',image)

然后进行图像预处理,真实银行卡图片像素尺寸大小不一,有的原图分辨率过高、像素冗余,有的尺寸过小特征模糊,统一将图像宽度缩放至300像素,高度按比例自适应缩放。

然后再进行灰度图展示

c 复制代码
image = myutils.resize(image,width=300)
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv_show('gray',gray)

我们调用的是先前封装好的脚本myutils.py中的resize函数,它可以统一输入图像尺寸,避免因图像大小差异导致轮廓检测阈值失效;压缩冗余像素,减少代码运算量,提升程序运行速度;标准化数字像素大小,匹配我们提前设定的数字模板尺寸,保证后续模板匹配精度。

myutils.py中的resize函数:

c 复制代码
def 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 image
    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

运行结果图:

矩形内核

形态学运算是OpenCV处理二值图像、提取目标特征、降噪的核心手段,而结构核(Kernel)是形态学运算的核心载体,所有的膨胀、腐蚀、开运算、闭运算、顶帽运算都需要依靠自定义结构核完成。

c 复制代码
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))

rectKernel = (9,3)矩形核,长宽比接近3:1的横向矩形结构核,专门适配银行卡数字的形态------银行卡数字为横向排列、细长矩形形态。

sqKernel = (5,5)正方形核,标准正方形结构核,尺寸更小、更规整,主要用于后期精细化降噪、闭合微小轮廓缺口,填补数字边缘的细微断裂,同时不会破坏数字整体形态。

顶帽运算(TopHat)提取数字特征

顶帽运算是形态学高级运算。公式:顶帽图像 = 原图 - 开运算图像。

c 复制代码
tophat = cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel)
cv_show('tophat',tophat)

顶帽运算可以剔除大面积均匀背景,只保留数字、文字、细小纹理等高亮细节特征。

闭运算降噪与轮廓闭合处理

我们要知道原理:

膨胀操作:填补数字边缘的细微缺口、断裂,让数字轮廓完整连续;

腐蚀操作:还原数字整体尺寸,避免膨胀导致数字轮廓过度放大、粘连;

闭运算(MORPH_CLOSE)的运算逻辑:先膨胀、后腐蚀。

c 复制代码
closeX = cv2.morphologyEx(tophat,cv2.MORPH_CLOSE,rectKernel)
cv_show('closeX',closeX)
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('close2',thresh)

先对顶帽运算的结果图进行闭运算,然后对结果图进行二值化处理,在对二值化处理后的图片在进行一次闭运算,将图片中的空缺补齐。

银行卡数字区域轮廓筛选与定位

经过全套预处理后,图像中仅保留卡号、少量文字轮廓,接下来我们需要通过轮廓检测+特征筛选,精准定位出银行卡四组卡号数字区域,过滤掉有效期、银行名称、logo等无效轮廓。

c 复制代码
cnts = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]
cnts_img = img.copy()
cv2.drawContours(cnts_img,cnts,-1,(0,0,255),3)
cv_show('cnts_img',cnts_img)

cv2.RETR_EXTERNAL只检测图像最外层轮廓,忽略所有嵌套子轮廓。银行卡数字是实心区域,内部无嵌套轮廓,该参数可以过滤掉无效内层轮廓,减少运算量;同时cv2.CHAIN_APPROX_SIMPLE压缩轮廓点,只保留轮廓拐点坐标,去除冗余像素点,精简轮廓数据,提升运算速度;

基于长宽比、尺寸阈值筛选数字区域

c 复制代码
locs = []
for c in cnts:
    (x,y,w,h) = cv2.boundingRect(c)
    ar = w/float(h)
    if 2.5<ar<4.0:
        if (40<w<55) and (10<h<20):
            locs.append((x,y,w,h))
locs = sorted(locs,key=lambda x:x[0])
print(locs)

为每一个轮廓绘制最小外接矩形,返回矩形左上角坐标(x,y)、宽度w、高度h,通过这四个参数定义轮廓的几何尺寸。

银行卡的卡号是4位数字为一组,整体呈现细长矩形形态。经过大量实测,合法卡号组的长宽比固定在2.5~4.0之间。

结合缩放后图像的像素标准,限定宽度40-55像素、高度10-20像素,精准匹配四位数字组的像素尺寸,彻底过滤所有不符合尺寸的无效轮廓。

最后进行排序,关键字为x坐标,我们筛选出来的四个数字区域可以观察到,他的y,w,h变化都不是很大,只有x是有变化趋势的,所以选取x作为关键字。

单组数字拆分与逐字模板匹配识别

我们已经定位到四组卡号区域,每组包含4个数字,接下来需要拆分每组数字、提取单个数字、与模板库数字匹配、判定最优结果,完成最终的数字识别。

c 复制代码
output = []
for (gX,gY,gW,gH) in 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)
    digitCnts = cv2.findContours(group.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]
    digitCnts = myutils.sort_contours(digitCnts,method='left-to-right')[0]

通过坐标裁剪提取每组数字区域时,我们对坐标进行了上下左右5像素扩边。原因是:轮廓检测的外接矩形会紧贴数字边缘,容易裁剪丢失数字边缘像素,扩边5像素可以完整保留数字全貌,避免因裁剪缺失导致识别失败,同时不会引入多余干扰。

c 复制代码
    digitCnts = cv2.findContours(group.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]
    digitCnts = myutils.sort_contours(digitCnts,method='left-to-right')[0]

对每组4位数字的局部图像,再次进行外层轮廓检测,提取单个数字的轮廓。此时检测到的轮廓即为单个0-9数字的独立轮廓。

再次调用我们封装的sort_contours排序函数,对单数字轮廓从左到右排序,严格保证每组内4个数字的顺序与银行卡显示顺序一致。

数字尺寸统一归一化处理

c 复制代码
    for c in digitCnts:
        (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)

上篇中我们将所有标准模板数字统一缩放为57*88像素,因此这里必须将检测到的真实银行卡单个数字,同样resize为宽57、高88的统一尺寸。

尺寸归一化是模板匹配识别成功的关键,如果尺寸不一致,匹配分数会完全失效,识别准确率直接归零。缩放后每个单数字roi图像,与模板库数字尺寸、维度完全对齐,可直接用于匹配计算。

c 复制代码
        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)))

我们遍历上篇构建的digits模板字典,依次取出0-9的标准数字模板;执行模板匹配,得到匹配结果矩阵;然后提取匹配结果中的最小值、最大值、最小值坐标、最大值坐标,我们只需要最大匹配分数score。

找出分数列表中最大值对应的索引,该索引即为匹配度最高的数字(0-9);将识别出的数字转为字符串,存入当前组的输出列表groupOutput,完成单个数字识别。

识别结果绘制

在原始银行卡图像上,为每一组识别完成的数字区域绘制红色矩形框,标记识别的目标位置,实现结果可视化。扩边绘制与前期图像裁剪扩边逻辑一致,完整包裹数字区域,标注清晰美观。线宽设置为1,不遮挡原始数字,同时标记明显。

同时将每组识别完成的4位数字拼接为字符串,绘制在对应数字区域的上方位置,方便直观查看识别结果。

c 复制代码
    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)

绘制坐标:区域上方15像素,字体大小0.65、颜色红色、线宽2。

卡号拼接与卡种匹配输出

c 复制代码
print("Credit Card Type:{}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imshow(  "Image", image)
cv2.waitKey(0)

将四组数字的识别结果全部存入output列表,拼接为完整的16位银行卡卡号;根据开篇定义的FIRST_NUMBER字典,通过卡号第一位数字匹配银行卡类型,支持Visa、MasterCard、American Express、Discover Card四种主流卡种识别。

输出结果:

c 复制代码
Credit Card Type:Visa
Credit Card #: 4000123456789010
相关推荐
罗政1 小时前
AI工作流实现Excel全自动化(支持SQL)-案例:医院门诊排班表
人工智能·自动化·excel
遇见小修修1 小时前
选择正规上门修电脑服务,有哪些通用标准和判断方法?
python
黎阳之光1 小时前
黎阳之光透明大楼:实景孪生重构智慧建筑全新范式
人工智能·物联网·算法·安全·数字孪生
步步为营DotNet1 小时前
Blazor 与 Microsoft.Extensions.AI 在客户端性能优化中的协同应用
人工智能·microsoft·性能优化
ayqy贾杰1 小时前
SpaceX 收购 Cursor,马斯克花600亿美元买了个代码编辑器
前端·人工智能·机器学习
JAMSAN09301 小时前
机器人轴承:被低估的“物理关节”,正在打开300倍增长空间
数据库·人工智能·机器人·智能硬件
财经资讯数据_灵砚智能1 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年6月16日
人工智能·python·ai·信息可视化·自然语言处理·ai编程·灵砚智能
“码”力全开2 小时前
解耦与重塑:基于 Docker 容器化与 GB28181/RTSP 统一接入的 AI 视频管理平台架构解析(支持源码交付与边缘计算)
人工智能·docker·边缘计算
小宋10212 小时前
4 万 Star 的开源 ChatGPT 桌面端:用 Jan 把电脑变成离线 AI 工作站
人工智能·chatgpt·开源·jan