揭秘!OpenCV 如何快速准确地识别银行卡号?

机器学习算法大数据

picture.image

向AI转型的程序员都关注公众号 机器学习AI算法工程

项目任务及要求

任务书:要为某家银行设计一套智能卡号识别的系统。

要求:传入一张图片,就自动输出信用卡图片中的数字。

项目实现思路

要实现此项目,首先要知道我们的目标是什么,我们的目标是对银行卡号识别,银行卡号都是数字,因此我们要找到一个模版且模版中有0~9的数字。然后对模版图像中的数字进行定位处理,每一个数字对应一个模版,这样有助于后期进行模版的对照。对模版处理好后,就应对银行卡的图像进行处理,通过对银行卡的一系列处理得到银行卡图像中数字的模版。在进行模版匹配,计算匹配得分,匹配得分最高的就是那个数字,再对数字进行组合、输出得到银行卡号。

模版图像

picture.image

银行卡图像

picture.image

项目实现及代码

导入模块


          
import numpy as np
          
import argparse  # python内置库  不太熟,自行学习
          
import cv2
          
import myutils
          

      

其中myutils包需要自己创建,再项目目录创建一个名叫myutils.py文件就行了,其中两个函数分别用来进行排序和改变图像大小,内容为:


          
import cv2
          

          

          
def sort_contours(cnts, method='left-to-right'):
          
    # 初始化 reverse 为 False,表示默认不使用逆序排序
          
    reverse = False
          
    # 初始化 i 为 0,用于后续选择排序依据的维度
          
    i = 0
          

          
    # 如果排序方法是 right-to-left 或 bottom-to-top,则设置为逆序排序
          
    if method == 'right-to-left' or method == 'bottom-to-top':
          
        reverse = True
          
    # 如果排序方法是 top-to-bottom 或 bottom-to-top,则选择 i 为 1,表示按垂直维度排序
          
    if method == 'top-to-bottom' or method == 'bottom-to-top':
          
        i = 1
          
    # 计算每个轮廓的外接矩形,并存储在 boundingBoxes 列表中
          
    boundingBoxes = [cv2.boundingRect(c) for c in cnts]
          
    # 将轮廓和其对应的外接矩形打包在一起,然后根据 lambda 函数指定的规则进行排序
          
    # b 是 (cnt, boundingBox) 元组,b[1] 是外接矩形,b[1][i] 表示根据 i 所指定的维度(i = 0 为水平方向,i = 1 为垂直方向)
          
    # 根据 reverse 决定是否逆序排序
          
    (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,用于存储调整后的图像尺寸
          
    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))
          
    # 使用 cv2.resize 函数根据 dim 和指定的插值方法对图像进行缩放
          
    resized = cv2.resize(image, dim, interpolation=inter)
          
    # 返回缩放后的图像
          
    return resized
          

      

设置参数

  • 通过导入argparse模块
  • 创建 ArgumentParser 对象。
  • 添加参数。
  • 解析命令行参数。
  • 并指定银行卡类型,便于后期从字典中查找。
  • 创建一个函数cv_show来展示图像。
  • 导入参数

picture.image

picture.image


          
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())  # vars()是Python中的一个内置函数,用于返回对象的属性和值的字典。
          
# 指定信用卡类型
          
FIRST_NUMBER = {"3": "American Express",
          
                "4": "Visa",
          
                "5": "MasterCard",
          
                "6": "Discover Card"}
          
def cv_show(name, img):  # 绘图展示
          
    cv2.imshow(name, img)
          
    cv2.waitKey(0)
          

      

对模版图像中数字的定位处理

  • 导入图片
  • 将图片转化为灰度图
  • 再将灰度图再转化为二值图
  • 计算图像轮廓得到图像外轮廓和终点坐标,并再图像中画出
  • 将得到的轮廓,按从左到到右,从上到下排序
  • 通过遍历得到每一个数字对应的像素值picture.image

          
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)
          
# 计算轮廓:cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图),
          
#   cv2.RETR_EXTERNAL只检测外轮廓,cv2.CHAIN_APPROX_SIMPLE只保留终点坐标
          
_, refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
          
cv2.drawContours(img, refCnts, -1, (0, 0, 255), 3)
          
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)  # 计算外接矩形并且resize成合适大小
          
    roi = ref[y:y + h, x:x + w]
          
    roi = cv2.resize(roi, (57, 88))  # 缩放到指定的大小
          
    digits[i] = roi  # 每一个数字对应每一个模板
          

      

银行卡的图像处理

读取输入图像,预处理

  • 输入图像

  • 重新设置图像大小

  • 转化为灰度图

  • 初始化卷积核

  • 进行顶帽和开运算
    picture.image


          
# 读取输入图像,预处理
          
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)  # 顶帽 = 原始图像 - 开运算结果(先腐蚀后膨胀)
          
open = cv2.morphologyEx(gray, cv2.MORPH_OPEN, rectKernel)  # 顶帽 = 原始图像 - 开运算结果(先腐蚀后膨胀)
          
cv_show('open', open)
          
cv_show('tophat', tophat)
          

      

找到数字边框

  • 通过闭操作(先膨胀,再腐蚀)将数字连在一起并进行二值化处理。

  • 再重复一次上述操作。

  • 计算轮廓并画出

  • 遍历轮廓,根据条件找到数字部分像素区域

  • 遍历每一个轮廓中的数字,得到每一个数字模版
    picture.image


          
# 1、通过闭操作(先膨胀,再腐蚀)将数字连在一起
          
closeX = cv2.morphologyEx(tophat, cv2.MORPH_CLOSE, rectKernel)
          
cv_show('gradX', closeX)
          
# THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
          
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), 3)
          
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)
          
    # 计算每一组的轮廓
          
    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:
          
        # 找到当前数值的轮廓,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)))
          

          

      

画出并打印结果

picture.image

机器学习算法AI大数据技术

搜索公众号添加: datanlp

picture.image

长按图片,识别二维码

阅读过本文的人还看了以下文章:

实时语义分割ENet算法,提取书本/票据边缘

整理开源的中文大语言模型,以规模较小、可私有化部署、训练成本较低的模型为主

《大语言模型》PDF下载

动手学深度学习-(李沐)PyTorch版本

YOLOv9电动车头盔佩戴检测,详细讲解模型训练

TensorFlow 2.0深度学习案例实战

基于40万表格数据集TableBank,用MaskRCNN做表格检测

《基于深度学习的自然语言处理》中/英PDF

Deep Learning 中文版初版-周志华团队

【全套视频课】最全的目标检测算法系列讲解,通俗易懂!

《美团机器学习实践》_美团算法团队.pdf

《深度学习入门:基于Python的理论与实现》高清中文PDF+源码

《深度学习:基于Keras的Python实践》PDF和代码

特征提取与图像处理(第二版).pdf

python就业班学习视频,从入门到实战项目

2019最新《PyTorch自然语言处理》英、中文版PDF+源码

《21个项目玩转深度学习:基于TensorFlow的实践详解》完整版PDF+附书代码

《深度学习之pytorch》pdf+附书源码

PyTorch深度学习快速实战入门《pytorch-handbook》

【下载】豆瓣评分8.1,《机器学习实战:基于Scikit-Learn和TensorFlow》

《Python数据分析与挖掘实战》PDF+完整源码

汽车行业完整知识图谱项目实战视频(全23课)

李沐大神开源《动手学深度学习》,加州伯克利深度学习(2019春)教材

笔记、代码清晰易懂!李航《统计学习方法》最新资源全套!

《神经网络与深度学习》最新2018版中英PDF+源码

将机器学习模型部署为REST API

FashionAI服装属性标签图像识别Top1-5方案分享

重要开源!CNN-RNN-CTC 实现手写汉字识别

yolo3 检测出图像中的不规则汉字

同样是机器学习算法工程师,你的面试为什么过不了?

前海征信大数据算法:风险概率预测

【Keras】完整实现‘交通标志’分类、‘票据’分类两个项目,让你掌握深度学习图像分类

VGG16迁移学习,实现医学图像识别分类工程项目

特征工程(一)

特征工程(二) :文本数据的展开、过滤和分块

特征工程(三):特征缩放,从词袋到 TF-IDF

特征工程(四): 类别特征

特征工程(五): PCA 降维

特征工程(六): 非线性特征提取和模型堆叠

特征工程(七):图像特征提取和深度学习

如何利用全新的决策树集成级联结构gcForest做特征工程并打分?

Machine Learning Yearning 中文翻译稿

蚂蚁金服2018秋招-算法工程师(共四面)通过

全球AI挑战-场景分类的比赛源码(多模型融合)

斯坦福CS230官方指南:CNN、RNN及使用技巧速查(打印收藏)

python+flask搭建CNN在线识别手写中文网站

中科院Kaggle全球文本匹配竞赛华人第1名团队-深度学习与特征工程

不断更新资源

深度学习、机器学习、数据分析、python

搜索公众号添加: datayx

picture.image

0
0
0
0
关于作者

文章

0

获赞

0

收藏

0

相关资源
火山引擎大规模机器学习平台架构设计与应用实践
围绕数据加速、模型分布式训练框架建设、大规模异构集群调度、模型开发过程标准化等AI工程化实践,全面分享如何以开发者的极致体验为核心,进行机器学习平台的设计与实现。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论