基于OpenCV的银行卡号识别项目实战

在计算机视觉领域,模板匹配与轮廓检测是基础且实用的技术组合,广泛应用于字符识别、目标定位等场景。本文将带大家从零搭建一个银行卡号识别项目,通过OpenCV实现模板创建、图像预处理、轮廓提取、模板匹配等核心步骤,精准识别银行卡上的数字信息。

一、项目概述

本项目核心目标是自动识别银行卡表面的卡号,整体流程分为两大模块:一是创建数字模板库,通过对标准OCR-A(一种便于机器识别的字体)数字模板图像的处理,提取0-9每个数字的轮廓特征并保存;二是对银行卡图像进行预处理、轮廓检测定位卡号区域,再通过模板匹配识别每个数字,最终输出卡号及银行卡类型。

技术栈:Python + OpenCV + NumPy,无需复杂的深度学习模型,仅通过传统计算机视觉技术即可实现,适合入门者学习和拓展。

实现结果:

二、核心技术原理

1. 模板匹配

模板匹配是通过滑动模板图像在目标图像上的每个位置,计算两者的相似度,找到最匹配区域的技术。本项目使用OpenCV的cv2.matchTemplate()函数,采用cv2.TM_CCOEFF方法计算相关系数,系数越高表示匹配度越好,以此确定目标数字对应的模板。

2. 轮廓检测

轮廓是图像中连续的像素集合,代表物体的边界特征。通过cv2.findContours()函数可提取图像轮廓,结合轮廓的外接矩形(cv2.boundingRect())可定位目标区域的位置和尺寸,实现卡号区域及单个数字的分割。

3. 形态学操作

形态学操作(顶帽、闭运算)用于图像预处理,消除背景干扰、连接断裂的字符。顶帽操作(cv2.MORPH_TOPHAT)可突出图像中的亮细节,抑制暗背景;闭运算(cv2.MORPH_CLOSE)先膨胀后腐蚀,能填充字符间的空隙,将分散的数字连接成完整区域。

4. 图像缩放与阈值处理

图像缩放(cv2.resize())用于统一模板与目标数字的尺寸,确保匹配的准确性;阈值处理(cv2.threshold())结合cv2.THRESH_OTSU自动阈值法,将灰度图转化为二值图,突出字符与背景的对比,为轮廓检测奠定基础。需要注意的是,我们需要使得二值化后的图片背景为黑色,图中的主体为白色,因为背景为白色,主体为黑色在轮廓检测的时候可能会把整个图片的边框作为轮廓,这算是opencv库的一个小bug。

三、完整代码解析

1. 工具函数定义

首先定义两个核心工具函数:轮廓排序和图像缩放,为后续处理提供支撑。

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

def sort_contours(cnts, method='left-to-right'):
    """
    对轮廓进行排序
    :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
    if method in ['top-to-bottom', 'bottom-to-top']:
        i = 1  # 按y轴排序
    # 计算每个轮廓的外接矩形
    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):
    """
    保持比例缩放图像
    :param image: 输入图像
    :param width: 目标宽度(None则按高度比例计算)
    :param height: 目标高度(None则按宽度比例计算)
    :param inter: 插值方法,默认INTER_AREA适合缩放
    :return: 缩放后的图像
    """
    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))
    return cv2.resize(image, dim, interpolation=inter)

def cv_show(name, img):
    """图像显示辅助函数"""
    cv2.imshow(name, img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()  # 避免窗口残留

2. 模板库创建

通过处理标准OCR-A数字模板,提取每个数字的轮廓特征,构建数字模板字典。

python 复制代码
# 解析命令行参数(指定输入图像和模板路径)
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"}

# 1. 处理模板图像,创建数字模板库
template = cv2.imread(args["template"])
cv_show("Template Original", template)

# 模板图像预处理:灰度化 -> 二值化(反色,让数字为白色,背景为黑色)
ref = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
cv_show("Template Gray", ref)
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]
cv_show("Template Binary Inv", ref)

# 提取模板轮廓(仅保留外部轮廓)
_, refCnts, _ = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 绘制轮廓查看效果
cv2.drawContours(template, refCnts, -1, (255, 0, 0), 3)
cv_show("Template Contours", template)

# 按从左到右排序轮廓(对应0-9数字顺序)
refCnts, _ = sort_contours(refCnts, method="left-to-right")
digits = {}  # 存储数字模板:键为数字(0-9),值为对应的轮廓图像

# 遍历轮廓,提取每个数字的ROI(感兴趣区域)并标准化尺寸
for i, c in enumerate(refCnts):
    x, y, w, h = cv2.boundingRect(c)
    roi = ref[y:y+h, x:x+w]
    # 标准化尺寸为57x88,便于后续匹配
    roi = cv2.resize(roi, (57, 88))
    digits[i] = roi  # 数字i对应模板roi
print("数字模板库构建完成,包含数字0-9")

3. 银行卡图像预处理与卡号定位

对银行卡图像进行一系列预处理,定位出卡号所在的区域,为后续数字识别做准备。

python 复制代码
# 2. 银行卡图像处理与卡号区域定位
image = cv2.imread(args["image"])
cv_show("Card Original", image)
# 缩放图像(宽度设为300,保持比例),便于处理
image = resize(image, width=300)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv_show("Card Gray", gray)

# 顶帽操作:突出亮细节(卡号),抑制暗背景
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))  # 矩形结构元素
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
cv_show("Card TopHat", tophat)

# 闭运算1:连接断裂的数字(先膨胀后腐蚀)
closex = cv2.morphologyEx(tophat, cv2.MORPH_CLOSE, rectKernel)
cv_show("Card Close1", closex)

# 二值化:自动阈值(OTSU),适合双峰图像
thresh = cv2.threshold(closex, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show("Card Threshold", thresh)

# 闭运算2:进一步填充空隙,强化卡号区域
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))  # 正方形结构元素
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)
cv_show("Card Close2", thresh)

# 提取轮廓,定位卡号区域
_, cnts, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts_img = image.copy()
cv2.drawContours(cnts_img, cnts, -1, (0, 0, 255), 3)
cv_show("Card All Contours", cnts_img)

# 筛选卡号区域轮廓(根据宽高比和尺寸过滤)
locs = []
for c in cnts:
    x, y, w, h = cv2.boundingRect(c)
    ar = w / float(h)  # 宽高比
    # 卡号区域特征:宽高比2.5-4.0,尺寸在合理范围
    if 2.5 < ar < 4.0 and 40 < w < 55 and 10 < h < 20:
        locs.append((x, y, w, h))

# 按x轴排序卡号区域(从左到右)
locs = sorted(locs, key=lambda x: x[0])
print(f"定位到{len(locs)}个卡号区域")

这里筛选卡号需要根据银行卡的特点来设计代码,如果换成身份证号码的识别,我们代码只需要更改这段从轮廓中筛选出卡号位置的代码。

4. 模板匹配识别数字

对每个卡号区域进行分割,提取单个数字,通过模板匹配识别数字,最终输出结果。

python 复制代码
# 3. 模板匹配识别数字并输出结果
output = []
for (gX, gY, gW, gH) in locs:
    groupOutput = []
    # 提取卡号区域ROI,适当扩大边界(避免裁剪到数字边缘)
    group = gray[gY-5:gY+gH+5, gX-5:gX+gW+5]
    cv_show("Card Number Group", group)
    
    # 对数字组进行二值化
    group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
    cv_show("Group Threshold", group)
    
    # 提取数字组内单个数字的轮廓
    _, digitCnts, _ = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    # 按从左到右排序数字轮廓
    digitCnts, _ = sort_contours(digitCnts, method="left-to-right")
    
    # 遍历每个数字轮廓,进行模板匹配
    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))
        
        # 模板匹配:计算与每个数字模板的相似度
        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(image, "".join(groupOutput), (gX, gY-15), 
                cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
    # 汇总识别结果
    output.extend(groupOutput)

# 输出最终结果
card_type = FIRST_NUMBER.get(output[0], "Unknown Card")
card_number = "".join(output)
print(f"银行卡类型:{card_type}")
print(f"银行卡号:{card_number}")

# 显示最终识别效果
cv2.imshow("Final Recognition Result", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

四、项目运行与效果优化

1. 运行方式与结果

将代码保存为credit_card_recognition.py,准备一张银行卡图像和OCR-A数字模板图像,在命令行执行以下命令:

复制代码
python credit_card_recognition.py -i card.jpg -t template.png

其中card.jpg为银行卡图像路径,template.png为数字模板图像路径。

也可以直接在pycharm中修改运行配置里面来填写形参-i card.jpg -t template.png。

运行结果:

2. 常见问题与优化方案

  • 识别准确率低:可能是模板图像质量差、银行卡图像有倾斜或阴影。优化方案:选用高清无噪点的模板;对银行卡图像进行倾斜校正(霍夫变换)、阴影去除(灰度归一化)。

  • 卡号区域定位失败 :结构元素尺寸不合适。优化方案:根据银行卡图像尺寸调整rectKernelsqKernel的大小,确保能准确提取卡号区域轮廓。

  • 数字分割不完整:二值化阈值不当或轮廓筛选条件过严。优化方案:调整阈值参数,或放宽轮廓筛选的宽高比、尺寸范围,结合实际图像微调。

五、总结与展望

本项目通过传统计算机视觉技术,实现了银行卡号的自动识别,核心在于模板匹配与轮廓检测的结合,以及合理的图像预处理流程。该方案无需训练复杂模型,部署简单、运行高效,适合对实时性要求不高的场景。

未来可进一步优化方向:1. 加入图像倾斜校正和阴影去除模块,提升对复杂场景图像的适配性;2. 结合机器学习算法(如SVM、KNN)替换模板匹配,提高识别准确率和泛化能力;3. 拓展支持多种字体、多种银行卡类型的识别,实现更通用的字符识别系统。

通过本项目的实践,能深入理解OpenCV中形态学操作、轮廓处理、模板匹配的核心用法,为后续更复杂的计算机视觉项目打下基础。

作业:实现身份证好识别。

相关推荐
无代码专家2 小时前
低代码构建数据管理系统:选型逻辑与实践路径
人工智能·低代码
无代码专家2 小时前
低代码搭建项目管理平台:易用性导向的实践方案
人工智能·低代码
KKKlucifer2 小时前
AI赋能与全栈适配:安全运维新范式的演进与实践
人工智能·安全
许泽宇的技术分享2 小时前
当AI学会拍短剧:Huobao Drama全栈AI短剧生成平台深度解析
人工智能
爱喝可乐的老王2 小时前
机器学习监督学习模型--线性回归
人工智能·机器学习·线性回归
金融Tech趋势派2 小时前
2025企业微信私有化部署优秀服务商:微盛·企微管家方案解析
人工智能·企业微信·scrm
Gofarlic_oms12 小时前
跨国企业Cadence许可证全球统一管理方案
java·大数据·网络·人工智能·汽车
AAD555888992 小时前
牛肝菌目标检测:基于YOLOv8-CFPT-P2345模型的创新实现与应用_1
人工智能·yolo·目标检测
幂链iPaaS3 小时前
制造业/零售电商ERP和MES系统集成指南
大数据·人工智能