OpenCv(7.0)——银行卡号识别

文章目录


前言

本代码实现基于传统图像处理技术,通过模板匹配识别银行卡号。核心流程包括:模板数字提取图像预处理卡号区域定位数字分割与识别结果可视化
适用场景:金融业务自动化、移动支付绑卡等需要快速卡号识别的场景。

1.1 模板处理模块

读取模板图像并预处理

python 复制代码
img = cv2.imread(args['template'])                # 读取模板图像
cv_show('img',img)
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)       # 灰度化处理
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]  # 反向二值化
cv_show('ref',ref)


轮廓检测与处理

python 复制代码
refCnts = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
refCnts = myutils.sort_contours(refCnts, method='left-to-right')[0]  # 关键排序

构建数字模板库

python 复制代码
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                                # 存储数字模板
    

关键技术点:

反向二值化 (THRESH_BINARY_INV):将白色数字转为前景,适配银行卡号特征

轮廓排序:确保模板按0-9顺序存储,需自定义sort_contours函数实现

尺寸统一:57x88像素经过实验验证为最佳识别尺寸

1.2 银行卡图像预处理

图像尺寸标准化

python 复制代码
image = myutils.resize(img, width=300)             # 固定宽度为300px
cv_show('image',img)
image=myutils.resize(img,width=300)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)     # 灰度化
cv_show('gray',gray)

形态学操作

python 复制代码
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,3)) 
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)  # 顶帽运算
cv_show('open',open)
cv_show('tophat',tophat)


二值化与闭操作

python 复制代码
thresh = cv2.threshold(tophat, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)   # 闭合小孔洞
cv_show('thresh1',thresh)

形态学操作解析:

操作类型 核尺寸 作用 效果图示

顶帽运算 9×3 增强横向亮色区域 顶帽效果

闭运算 5×5 连接数字断裂区域 闭运算效果

1.3 卡号区域定位

轮廓筛选逻辑

python 复制代码
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
locs = []
for c in 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))  # 符合条件区域

locs = sorted(locs, key=lambda x:x[0])  # 按X坐标排序

筛选条件验证:

长宽比2.5-4.0:银行卡号通常呈扁平矩形

宽度40-55px:适配300px宽图像中的卡号尺寸

高度10-20px:排除过大干扰区域

1.4 数字识别与结果展示

python 复制代码
for (i, (gX,gY,gW,gH)) in enumerate(locs):
    group = gray[gY-5:gY+gH+5, gX-5:gX+gW+5]        # 扩展边界
    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)[0]
    digitCnts = myutils.sort_contours(digitCnts, method='left-to-right')[0]
    
    # 模板匹配
    groupOutput = []
    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)
        scores = [cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF)[1] for digitROI in digits.values()]
        groupOutput.append(str(np.argmax(scores)))  # 取最高分
        
    # 绘制结果
    cv2.rectangle(image, (gX-5,gY-5), (gX+gW,gY+gH+2), (0,255,0), 2)
    cv2.putText(image, ''.join(groupOutput), (gX,gY-15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0,0,255), 2)




匹配算法对比:

方法 优点 缺点 适用场景

TM_CCOEFF 光照鲁棒性强 计算量较大 标准字体

TM_SQDIFF 计算速度快 对亮度敏感 高对比度图像

深度学习 准确率高 需要训练数据 复杂场景

完整代码展示

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

import myutils



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())
FIRST_NUMBER={
    "3":"American Express",
    "4":"Visa",
    "5":"MasterCard",
    "6":"Discover Card"
}

def cv_show(name,img):
    cv2.imshow(name,img)
    cv2.waitKey(0)


"""------------------------模板图像中数字的定位步骤-----------------------"""

img=cv2.imread(args['template'])
cv_show('img',img)

ref=cv2.cvtColor(img,cv2.COLOR_BGRA2GRAY)
cv_show('ref',ref)

ref=cv2.threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1]
cv_show('ref',ref)

# 计算轮廓,cv2。findcountours

_,refCnts,hierarchy=cv2.findContours(ref,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,refCnts,-1,(0,0,255),2)
cv_show('img',img)
refCnts=myutils.sort_contours(refCnts,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))
    cv_show('roi',roi)
    digits[i]=roi

# print(digits)


#读取银行卡图片,处理
img=cv2.imread(args["image"])
cv_show('image',img)
image=myutils.resize(img,width=300)
gray=cv2.cvtColor(image,cv2.COLOR_BGRA2GRAY)
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)
open=cv2.morphologyEx(gray,cv2.MORPH_OPEN,rectkernel)
cv_show('open',open)
show = cv_show('tophat', tophat)

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('thresh1',thresh)

#计算轮廓

_,threshcnts,h=cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

cnts=threshcnts
cur_img=image.copy()
cv2.drawContours(cur_img,cnts,-1,(0,0,255),2)
cv_show('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):
    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,hierarchy=cv2.findContours(group.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

    digitCnts=myutils.sort_contours(digitCnts,method='left-to-right')[0]

    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)


        "使用模板匹配,计算匹配得分"

        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,gY+gH+2),(0,100,255),2)
    cv2.putText(image,''.join(groupOutput),(gX,gY-10),cv2.FONT_HERSHEY_SIMPLEX,0.65,(0,255,255),2)
    output.extend(groupOutput)

# print('Credit Card Type:{}'.format(FIRST_NUMBER[output[0]]))
print('Credit Card #:{}'.format("".join(output)))
cv_show("image",image)


# -i ../data/card2.png -t ../data/kahao.png

代码运行

myutils.py代码,需要和项目在同一目录下创建一个.py代码

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]
    (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)
    return resized

总结

该项目展现了传统视觉技术在特定场景下的实用价值,为金融科技自动化提供了基础能力支撑。未来通过与深度学习、边缘计算等技术的深度融合,有望在跨境支付、智能终端等领域形成更成熟的解决方案,成为金融基础设施的重要组成部分。

相关推荐
NAGNIP4 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab5 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab5 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP9 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年9 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼9 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS9 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区10 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈11 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang11 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx