计算机视觉基础 第 9 章|实战:银行卡号识别

前言

前期我们已经学了图像检测,边缘检测,轮廓检测,模板匹配,现在我们通过一个实战来综合运用以上知识点。

一、项目需求

基于OpenCV计算机视觉算法,对银行卡图像进行处理,精准识别并提取图片中的完整银行卡卡号。

项目配套提供0-9标准数字模板图像,无需自行采集数字样本,依托模板匹配算法完成数字识别。

二、项目思路分析

  1. 处理数字模板图像:提取模板中0-9单个数字的有效区域,建立数字与图像的对应关系并保存,构建标准数字模板库。

  2. 处理银行卡原图:通过图像预处理、轮廓检测等操作,精准定位银行卡号区域,分割提取出每一个独立数字图像。

  3. 执行模板匹配:将银行卡提取的单个数字图像,与提前构建的0-9标准模板逐一比对匹配,判定对应的真实数字。

  4. 整合输出结果:按从左至右的正确顺序拼接识别出的数字,最终输出完整的银行卡号。

三、开始实战

1. 导包

python 复制代码
python
import numpy as np
import argparse
import cv2
import myutils

2. 使用 argparse 解析命令行参数

argparse 模块用于解析命令行参数,当需要传递多个参数时,它可以避免固定位置传参的繁琐,使脚本调用更加灵活和规范。

4. 对模板进行处理

4.1 读取模板图片、灰度转换、反二值化

读取图片,将彩色图片处理成灰度图,二值化处理,偏白变成黑色,偏黑变成白色,最后我们会得到一张黑底白字的照片,方便寻找轮廓。

python 复制代码
python
img = cv2.imread(args['template'])
cv_show('template',img)

#处理成灰度图
ref = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

#二值化处理,黑底白字
ref = cv2.threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1]
cv_show('ref',ref)

4.2 查找并绘制数字轮廓

寻找轮廓,通过findContours 返回所有父类轮廓和主要像素点矩阵,该轮廓子轮廓统统不要。findContour有返回值,取倒数第二个。

python 复制代码
#寻找轮廓
refCnts = cv2.findContours(ref,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]
#在原图上画出检测的轮廓
cv2.drawContours(img,refCnts,-1,(0,255,0),3)
cv_show('refCnts',img)

4.3 轮廓排序,遍历保存数字模板字典

通过自定义包的函数,对获取的轮廓矩阵进行排序,遍历所有数字轮廓,裁剪ROI并统一尺寸,存入字典对应0-9数字。

python 复制代码
#轮廓排序
refCnts = myutils.sort_contours(refCnts)[0]
digits = {}

#存储进字典中
for i ,j in enumerate(refCnts):
    (x,y,w,h) = cv2.boundingRect(j)
    #提取对应的像素矩阵
    roi = ref[y:y+h,x:x+w]
    #重置大小
    roi = cv2.resize(roi,(57,88))
    cv_show('roi',roi)
    digits[i] = roi
print(digits)

裁剪示例:

字典:

自定义工具 (myutils.py)

将该文件与主程序放置在同一项目目录下即可直接调用。

自定义工具包 myutils 包含两个核心工具函数,其中 sort_contours 用于对检测得到的无序轮廓矩阵进行规整排序。其原理为:获取所有轮廓的外接矩形,通过矩形左上角横坐标完成排序。数字模板中0-9的排布从左至右有序,对应的轮廓外接矩形横坐标依次递增,因此可通过x轴坐标大小判定轮廓先后顺序。该函数最终返回排序完成的轮廓矩阵与对应外接矩形。由于 OpenCV 检测到的轮廓默认无序、随机排列,必须通过排序规整顺序,才能精准匹配0-9对应的数字轮廓,保证后续模板匹配结果准确。

python 复制代码
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] #返回x,y,w,h
    (cnts,boundingBoxes)=zip(*sorted(zip(cnts,boundingBoxes),
                                     key=lambda b:b[1][i],reverse=reverse))
    return cnts,boundingBoxes


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)    #默认为cv2.INTER_AREA,即面积插值,适用于缩放图像。
    return resized

5. 处理银行卡号

5.1 读取银行卡图片、缩放、灰度转换

读取银行卡原图,对图像进行等比例缩放统一尺寸,再将彩色图像转为灰度图,为后续形态学操作、轮廓检测做预处理。

python 复制代码
image = cv2.imread(args['img'])
cv_show('img',image)
image = myutils.resize(image,width=300)
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv_show('gray',gray)

5.2 顶帽运算,去除背景噪声、突出字符细节

灰度图像存在大量背景干扰噪声,通过顶帽形态学运算,抑制复杂背景,提亮、突出卡号字符细节。

python 复制代码
#对灰度图进行操作,灰度图中有过多的噪声(背景)
#顶帽,可以去除背景,突出细节
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)

5.3 第一次闭运算,聚合字符连通区域

闭运算为先膨胀、后腐蚀,可将分散的数字像素点聚合为完整连通块,让卡号区域更加规整集中。

python 复制代码
#先闭运算形成区域,二值化
#闭运算,先膨胀后腐蚀,形成成块的区域
closeX = cv2.morphologyEx(tophat,cv2.MORPH_CLOSE,rectKernel)
cv_show('closeX',closeX)

5.4 OTSU自适应二值化,实现黑白分明

采用OTSU自适应阈值二值化,自动筛选最优阈值,将图像转化为纯黑白效果,彻底分离前景字符与背景。

python 复制代码
#二值化,自动确定阈值,黑白分明
thresh = cv2.threshold(closeX,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]
cv_show('thresh',thresh)

5.5 第二次闭运算,填充孔洞、完善区域

再次执行闭运算,填补字符内部细小孔洞,补齐残缺区域,保证卡号连通块完整连续。

python 复制代码
#再次进行闭运算,补齐区域,保证成块完整
closeX = cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,sqKernel)
cv_show('thresh',closeX)

5.6 查找并绘制所有轮廓

提取图像最外层轮廓,过滤无效子轮廓,在原图绘制轮廓,直观观察所有连通区域范围。

python 复制代码
#寻找轮廓
#计算轮廓,在原图上找到对应的轮廓
cnts = cv2.findContours(closeX,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]
cnts_img = image.copy()
cv2.drawContours(cnts_img,cnts,-1,(0,255,0),3)
cv_show('cnts_img',cnts_img)

5.7 轮廓筛选,精准定位卡号区域

通过限定轮廓长宽比、宽高尺寸范围,过滤无效干扰轮廓,仅保留银行卡号所在的连通区域,并从左到右排序,保证卡号顺序正确。

python 复制代码
#在找到的轮廓当中筛选包含银行卡号的轮廓,通过限定条件
locs = []
for cnt in cnts:
    (x,y,w,h) = cv2.boundingRect(cnt)
    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)

6. 循环匹匹配

检测到的银行卡号轮廓分为多处分散区域,需要逐一对每个区域进行处理,分割提取出区域内的单个数字,再与提前构建的数字模板逐一完成匹配识别。

区域示例: 二值化:

裁剪单个数字:

python 复制代码
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]
    #计算每一组中的每一个数值
    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)))
    #画出来
    cv2.rectangle(image,(gX -5,gY -5),(gX + gW +5,gY + gH +5),(0,0,255),1)
    #cv2.putText()是OpenCV库中的一个函数,用于在图像上添加文本。
    cv2.putText(image,"".join(groupOutput),(gX,gY -15),cv2.FONT_HERSHEY_SIMPLEX,0.65,(0,0,255),2)
    output.extend(groupOutput)
#打印结果
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Image",image)
cv2.waitKey(0)

运行方式:

命令行传参:

python card.py -i 银行卡图片.jpg -t 数字模板图片.png

pycharm脚本形参输入:

四、本章小结

  1. 模板处理:灰度图→反二值化→查找外层轮廓→轮廓从左到右排序→裁剪 ROI,构建 0‑9 数字字典。
  2. 银行卡预处理:灰度、顶帽运算去除背景噪声,两次闭运算把卡号数字合并成连通区域;依靠长宽比筛选卡号分组位置。
  3. 识别逻辑:对每一组卡号分割单个数字,统一尺寸,和模板逐个模板匹配,取最大得分作为识别结果。
  4. 关键点:模板与待识别数字尺寸必须一致;轮廓必须排序,保证数字顺序正确。
相关推荐
CRMEB1 小时前
商城大促活动策划全流程:从定目标到复盘四阶段
java·开发语言·人工智能·ai·开源·php
ClouGence1 小时前
自动化测试实战:手把手教你用 AI Agent 实现版本打包自动化
人工智能·ai编程·测试
vivo互联网技术1 小时前
协同文档下的 Agent 协作闭环:可回滚、可对比的透明化编辑实现
人工智能·笔记·agent
khs185718375271 小时前
AI液冷技术应用:超纯水保障算力芯片运行
人工智能·经验分享
Patrick_Wilson2 小时前
当执行不再稀缺:AI Agent 时代的技术判断力
人工智能·架构·ai编程
Vuji2 小时前
Pi 插件解剖|ssh.ts:只用 221 行,让 Agent 直接在远程机器干活
前端·人工智能·agent
SpaceAIGlobal2 小时前
AI PPT生成工具哪些支持PDF文档导入?
人工智能·ai·pdf·powerpoint·办公
王中阳Go2 小时前
杰富瑞实测 8 款 AI Agent,国产千问 95 分登顶:我连夜把项目的 OpenAI 硬编码全拆了
人工智能·go
贵慜_Derek2 小时前
DeepSeek Harness 多 Agent 解读:subagent、workflow、jobs 各管什么
人工智能·agent·deepseek