基于机器视觉的银行卡识别系统 - opencv python 计算机竞赛

news2024/11/20 9:41:06

1 前言

🔥 优质竞赛项目系列,今天要分享的是

基于深度学习的银行卡识别算法设计

该项目较为新颖,适合作为竞赛课题方向,学长非常推荐!

🧿 更多资料, 项目分享:

https://gitee.com/dancheng-senior/postgraduate

2 算法设计流程

银行卡卡号识别技术原理是先对银行卡图像定位,保障获取图像绝对位置后,对图像进行字符分割,然后将分割完成的信息与模型进行比较,从而匹配出与其最相似的数字。主要流程图如图

在这里插入图片描述

1.银行卡号图像
由于银行卡卡号信息涉及个人隐私,作者很难在短时间内获取大量的银行卡进行测试和试验,本文即采用作者个人及模拟银行卡进行卡号识别测试。

2.图像预处理
图像预处理是在获取图像后必须优先进行的技术性处理工作,先对银行卡卡号图像进行色彩处理,具体做法与流程是先将图像灰度化,去掉图像识别上无用的信息,然后利用归一化只保留有效的卡号信息区域。

3.字符分割
字符分割是在对图像进行预处理后,在获取有效图像后对有效区域进行进一步细化处理,将图像分割为最小识别字符单元。

4.字符识别
字符识别是在对银行卡卡号进行字符分割后,利用图像识别技术来对字符进行分析和匹配,本文作者利用的模板匹配方法。

2.1 颜色空间转换

由于银行卡卡号识别与颜色无关,所以银行卡颜色是一个无用因素,我们在图像预处理环节要先将其过滤掉。另外,图像处理中还含有颜色信息,不仅会造成空间浪费,增加运算量,降低系统的整体效率,还会给以后的图像分析和处理带来干扰。因此,有必要利用灰度处理来滤除颜色信息。

灰度处理的实质是将颜色信息转化为亮度信息,即将原始的三维颜色信息还原为一维亮度信息。灰度化的思想是用灰度值g来表示原始彩色图像的R(绿色)、g(红色)和B(蓝色)分量的值,具体的流程设计如图

在这里插入图片描述

2.2 边缘切割

对于采集到的银行卡号图像,由于背景图案的多样性和卡号字体的不同,无法直接对卡号图像进行分割。分割前要准确定位卡号,才能得到有效区域。数字字符所在的区域有许多像素。根据该特征,通过设置阈值来确定原始图像中卡号图像的区域。银行卡图像的切边处理设计如图

在这里插入图片描述

2.3 模板匹配

模板匹配是一种将需要识别的字符与已有固定模板进行匹配的算法技术,该技术是将已经切割好的字符图像逐个与模板数字图像进行对比分析,其原理就是通过数字相似度来衡量两个字符元素,将目标字符元素逐个与模板数字图像进行匹配,找到最接近的数字元素即可。匹配计算量随特征级别的增加而减少。根据第一步得到的特征,选择第二种相关计算方法来解决图像匹配问题。银行卡模板匹配流程设计如图

在这里插入图片描述

2.4 卡号识别

银行卡卡号识别有其独有的特性,因为目前市面上大多数银行卡卡号是凹凸不平的数字形式,如果使用传统的计算机字符识别技术已显然不适用,本文针对银行卡此类特点,研究了解决此类问题的识别方案。从银行卡待识别的凸凹字符进行预处理,然后根据滑块算法逐个窗口对银行卡字符进行匹配识别,卡号识别一般从切割后的图像最左端开始,设定截图选定框大小为64*48像素,因为银行卡所需要识别的字符一般为45像素左右。故而以此方式循环对卡片上所有数字进行匹配、识别,如果最小值大于设置的阈值,我们将认为这里没有字符,这是一个空白区域,并且不输出字符。同时,窗口位置J向下滑动,输出f<19&&j;+20<图像总长度并判断,最后循环得到字符数f、j。

在这里插入图片描述

3 银行卡字符定位 - 算法实现

首先就是将整张银行卡号里面的银行卡号部分进行识别,且分出来,这一个环节学长用的技术就是faster-rcnn的方法

将目标识别部分的银行卡号部门且分出来,进行保存

主程序的代码如下(非完整代码):



    #!/usr/bin/env python
    
    from __future__ import absolute_import
    from __future__ import division
    from __future__ import print_function
    import argparse
    import os
    import cv2
    import matplotlib.pyplot as plt
    import numpy as np
    import tensorflow as tf
    from lib.config import config as cfg
    from lib.utils.nms_wrapper import nms
    from lib.utils.test import im_detect
    from lib.nets.vgg16 import vgg16
    from lib.utils.timer import Timer
    
    os.environ["CUDA_VISIBLE_DEVICES"] = '0'   #指定第一块GPU可用
    config = tf.ConfigProto()
    config.gpu_options.per_process_gpu_memory_fraction = 0.8  # 程序最多只能占用指定gpu50%的显存
    config.gpu_options.allow_growth = True      #程序按需申请内存
    sess = tf.Session(config = config)
    
    CLASSES = ('__background__','lb')
    NETS = {'vgg16': ('vgg16_faster_rcnn_iter_70000.ckpt',), 'res101': ('res101_faster_rcnn_iter_110000.ckpt',)}
    DATASETS = {'pascal_voc': ('voc_2007_trainval',), 'pascal_voc_0712': ('voc_2007_trainval+voc_2012_trainval',)}
    
    def vis_detections(im, class_name, dets, thresh=0.5):
        """Draw detected bounding boxes."""
        inds = np.where(dets[:, -1] >= thresh)[0]
        if len(inds) == 0:
            return
    
        im = im[:, :, (2, 1, 0)]
        fig, ax = plt.subplots(figsize=(12, 12))
        ax.imshow(im, aspect='equal')
        sco=[]
        for i in inds:
            score = dets[i, -1]
            sco.append(score)
        maxscore=max(sco)
        # print(maxscore)成绩最大值
        for i in inds:
            # print(i)
            score = dets[i, -1]
            if score==maxscore:
                bbox = dets[i, :4]
                # print(bbox)#目标框的4个坐标
                img = cv2.imread("data/demo/"+filename)
                # img = cv2.imread('data/demo/000002.jpg')
                sp=img.shape
                width = sp[1]
                if bbox[0]>20 and bbox[2]+20<width:
                    cropped = img[int(bbox[1]):int(bbox[3]), int(bbox[0]-20):int(bbox[2])+20] # 裁剪坐标为[y0:y1, x0:x1]
                if bbox[0]<20 and bbox[2]+20<width:
                    cropped = img[int(bbox[1]):int(bbox[3]), int(bbox[0]):int(bbox[2])+20] # 裁剪坐标为[y0:y1, x0:x1]
                if bbox[0] > 20 and bbox[2] + 20 > width:
                    cropped = img[int(bbox[1]):int(bbox[3]), int(bbox[0] - 20):int(bbox[2])]  # 裁剪坐标为[y0:y1, x0:x1]
                path = 'cut1/'
                # 重定义图片的大小
                res = cv2.resize(cropped, (1000, 100), interpolation=cv2.INTER_CUBIC)  # dsize=(2*width,2*height)
                cv2.imwrite(path+str(i)+filename, res)
                ax.add_patch(plt.Rectangle((bbox[0], bbox[1]),
                                  bbox[2] - bbox[0],
                                  bbox[3] - bbox[1], fill=False,
                                  edgecolor='red', linewidth=3.5)
                )
                ax.text(bbox[0], bbox[1] - 2,
                        '{:s} {:.3f}'.format(class_name, score),
                        bbox=dict(facecolor='blue', alpha=0.5),
                        fontsize=14, color='white')
    
                ax.set_title(('{} detections with '
                              'p({} | box) >= {:.1f}').format(class_name, class_name,thresh),
                             fontsize=14)
        plt.axis('off')
        plt.tight_layout()
        plt.draw()


    def demo(sess, net, image_name):
        """Detect object classes in an image using pre-computed object proposals."""
    
        # Load the demo image
        im_file = os.path.join(cfg.FLAGS2["data_dir"], 'demo', image_name)
        im = cv2.imread(im_file)
        # Detect all object classes and regress object bounds
        timer = Timer()
        timer.tic()
        scores, boxes = im_detect(sess, net, im)
        timer.toc()
        print('Detection took {:.3f}s for {:d} object proposals'.format(timer.total_time, boxes.shape[0]))
    
        # Visualize detections for each class
        CONF_THRESH = 0.1
        NMS_THRESH = 0.1
        for cls_ind, cls in enumerate(CLASSES[1:]):
            cls_ind += 1  # because we skipped background
            cls_boxes = boxes[:, 4 * cls_ind:4 * (cls_ind + 1)]
            cls_scores = scores[:, cls_ind]
            # print(cls_scores)#一个300个数的数组
            #np.newaxis增加维度  np.hstack将数组拼接在一起
            dets = np.hstack((cls_boxes,cls_scores[:, np.newaxis])).astype(np.float32)
            keep = nms(dets, NMS_THRESH)
            dets = dets[keep, :]
    
            vis_detections(im, cls, dets, thresh=CONF_THRESH)
    
    def parse_args():
        """Parse input arguments."""
        parser = argparse.ArgumentParser(description='Tensorflow Faster R-CNN demo')
        parser.add_argument('--net', dest='demo_net', help='Network to use [vgg16 res101]',
                            choices=NETS.keys(), default='vgg16')
        parser.add_argument('--dataset', dest='dataset', help='Trained dataset [pascal_voc pascal_voc_0712]',
                            choices=DATASETS.keys(), default='pascal_voc')
        args = parser.parse_args()
    
        return args



    if __name__ == '__main__':
        args = parse_args()
    
        # model path
        demonet = args.demo_net
        dataset = args.dataset
    
        #tfmodel = os.path.join('output', demonet, DATASETS[dataset][0], 'default', NETS[demonet][0])
        tfmodel = r'./default/voc_2007_trainval/cut1/vgg16_faster_rcnn_iter_8000.ckpt'
        # 路径异常提醒
        if not os.path.isfile(tfmodel + '.meta'):
            print(tfmodel)
            raise IOError(('{:s} not found.\nDid you download the proper networks from '
                           'our server and place them properly?').format(tfmodel + '.meta'))
    
        # set config
        tfconfig = tf.ConfigProto(allow_soft_placement=True)
        tfconfig.gpu_options.allow_growth = True
    
        # init session
        sess = tf.Session(config=tfconfig)
        # load network
        if demonet == 'vgg16':
            net = vgg16(batch_size=1)
        # elif demonet == 'res101':
            # net = resnetv1(batch_size=1, num_layers=101)
        else:
            raise NotImplementedError
        net.create_architecture(sess, "TEST", 2,
                            tag='default', anchor_scales=[8, 16, 32])
        saver = tf.train.Saver()
        saver.restore(sess, tfmodel)
    
        print('Loaded network {:s}'.format(tfmodel))
        # # 文件夹下所有图片进行识别
        # for filename in os.listdir(r'data/demo/'):
        #     im_names = [filename]
        #     for im_name in im_names:
        #         print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
        #         print('Demo for data/demo/{}'.format(im_name))
        #         demo(sess, net, im_name)
        #
        #     plt.show()
        # 单一图片进行识别
        filename = '0001.jpg'
        im_names = [filename]
        for im_name in im_names:
            print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
            print('Demo for data/demo/{}'.format(im_name))
            demo(sess, net, im_name)
        plt.show()



效果如下:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

4 字符分割

将切分出来的图片进行保存,然后就是将其进行切分:

主程序的代码和上面第一步的步骤原理是相同的,不同的就是训练集的不同设置

效果图如下:

在这里插入图片描述

5 银行卡数字识别

仅部分代码:


    import os
    import tensorflow as tf
    from PIL import Image
    from nets2 import nets_factory
    import numpy as np
    import matplotlib.pyplot as plt
    # 不同字符数量
    CHAR_SET_LEN = 10
    # 图片高度
    IMAGE_HEIGHT = 60
    # 图片宽度
    IMAGE_WIDTH = 160
    # 批次
    BATCH_SIZE = 1
    # tfrecord文件存放路径
    TFRECORD_FILE = r"C:\workspace\Python\Bank_Card_OCR\demo\test_result\tfrecords/1.tfrecords"
    
    # placeholder
    x = tf.placeholder(tf.float32, [None, 224, 224])
    
    os.environ["CUDA_VISIBLE_DEVICES"] = '0'   #指定第一块GPU可用
    config = tf.ConfigProto()
    config.gpu_options.per_process_gpu_memory_fraction = 0.5  # 程序最多只能占用指定gpu50%的显存
    config.gpu_options.allow_growth = True      #程序按需申请内存
    sess = tf.Session(config = config)
    
    # 从tfrecord读出数据
    def read_and_decode(filename):
        # 根据文件名生成一个队列
        filename_queue = tf.train.string_input_producer([filename])
        reader = tf.TFRecordReader()
        # 返回文件名和文件
        _, serialized_example = reader.read(filename_queue)
        features = tf.parse_single_example(serialized_example,
                                           features={
                                               'image' : tf.FixedLenFeature([], tf.string),
                                               'label0': tf.FixedLenFeature([], tf.int64),
    
                                           })
        # 获取图片数据
        image = tf.decode_raw(features['image'], tf.uint8)
        # 没有经过预处理的灰度图
        image_raw = tf.reshape(image, [224, 224])
        # tf.train.shuffle_batch必须确定shape
        image = tf.reshape(image, [224, 224])
        # 图片预处理
        image = tf.cast(image, tf.float32) / 255.0
        image = tf.subtract(image, 0.5)
        image = tf.multiply(image, 2.0)
        # 获取label
        label0 = tf.cast(features['label0'], tf.int32)


        return image, image_raw, label0

    # 获取图片数据和标签
    image, image_raw, label0 = read_and_decode(TFRECORD_FILE)
    # 使用shuffle_batch可以随机打乱
    image_batch, image_raw_batch, label_batch0 = tf.train.shuffle_batch(
        [image, image_raw, label0], batch_size=BATCH_SIZE,
        capacity=50000, min_after_dequeue=10000, num_threads=1)

    # 定义网络结构
    train_network_fn = nets_factory.get_network_fn(
        'alexnet_v2',
        num_classes=CHAR_SET_LEN * 1,
        weight_decay=0.0005,
        is_training=False)
    
    with tf.Session() as sess:
        # inputs: a tensor of size [batch_size, height, width, channels]
        X = tf.reshape(x, [BATCH_SIZE, 224, 224, 1])
        # 数据输入网络得到输出值
        logits, end_points = train_network_fn(X)
        # 预测值
        logits0 = tf.slice(logits, [0, 0], [-1, 10])


        predict0 = tf.argmax(logits0, 1)


        # 初始化
        sess.run(tf.global_variables_initializer())
        # 载入训练好的模型
        saver = tf.train.Saver()
        saver.restore(sess, '../Cmodels/model/crack_captcha1.model-6000')
        # saver.restore(sess, '../1/crack_captcha1.model-2500')
    
        # 创建一个协调器,管理线程
        coord = tf.train.Coordinator()
        # 启动QueueRunner, 此时文件名队列已经进队
        threads = tf.train.start_queue_runners(sess=sess, coord=coord)
    
        for i in range(6):
            # 获取一个批次的数据和标签
            b_image, b_image_raw, b_label0 = sess.run([image_batch,image_raw_batch,label_batch0])
            # 显示图片
            img = Image.fromarray(b_image_raw[0], 'L')
            plt.imshow(img)
            plt.axis('off')
            plt.show()
            # 打印标签
            print('label:', b_label0)
            # 预测
            label0 = sess.run([predict0], feed_dict={x: b_image})
            # 打印预测值
    
            print('predict:', label0[0])
            # 通知其他线程关闭
        coord.request_stop()
        # 其他所有线程关闭之后,这一函数才能返回
        coord.join(threads)



最终实现效果:

在这里插入图片描述

最后

🧿 更多资料, 项目分享:

https://gitee.com/dancheng-senior/postgraduate

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1149682.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode刷题详解——按摩师

1. 题目链接&#xff1a;面试题 17.16. 按摩师 2. 题目描述&#xff1a; 一个有名的按摩师会收到源源不断的预约请求&#xff0c;每个预约都可以选择接或不接。在每次预约服务之间要有休息时间&#xff0c;因此她不能接受相邻的预约。给定一个预约请求序列&#xff0c;替按摩师…

华泰证券:新奥能源:零售气待恢复,泛能与智家仍是亮点

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;由于新奥能源&#xff08;02688&#xff09;发布三季度经营数据&#xff1a; 1-3Q23&#xff1a;天然气零售量yoy-4.7%&#xff0c;燃气批发量yoy17.6%&#xff0c;综合能源销量yoy34.2%&#xff…

基于SpringBoot的社区医院管理系统设计与实现

目录 前言 一、技术栈 二、系统功能介绍 管理员功能实现 用户信息管理 病例信息管理 家庭医生管理 药品信息管理 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的…

中科驭数亮相2023中国移动全球合作伙伴大会

10月11-13日&#xff0c;2023中国移动全球合作伙伴大会开幕。中科驭数作为移动云COCA生态合作伙伴&#xff0c;受邀出席“算网融百业数智赢未来”政企分论坛&#xff0c;高级副总裁张宇上台参与移动云OpenCOCA开源项目和《OpenCOCA白皮书》的重磅发布仪式&#xff0c;助力构建未…

CS224W3.3——整图Embedding

在某些情况下&#xff0c;重要的是不仅要学习节点的嵌入&#xff0c;还要学习整个图。在这篇中&#xff0c;我们介绍了几种可以有效地学习整个图嵌入的方法&#xff0c;包括节点嵌入的聚合&#xff08;aggregation of node embeddings&#xff09;&#xff0c;以及匿名行走嵌入…

Sketch mac 98.3(矢量绘图设计软件)

Sketch是一款专为Mac设计的矢量图形编辑软件&#xff0c;被广泛应用于UI/UX设计、网页设计、移动应用设计等领域。Sketch提供了各种工具和功能&#xff0c;包括绘图、图形设计、排版等&#xff0c;可以帮助设计师轻松地创建高质量的矢量图形和模型。Sketch的主要特点包括&#…

Selenium3-当元素通过@FindBy获取时,返回元素为null

报错: 在获取元素的js属性时一直获取不到&#xff0c;报空指针&#xff0c;定位到元素时&#xff0c;发现是FindBy的元素没有找到 解决方法: 在page类的构造函数中加上了 界面初始化&#xff0c;让元素先隐式加载&#xff0c;这样就不会出现返回元素为空的情况辣 PageFactory…

瑞萨RH850-P1X ECM和英飞凌TC3xx SMU对比

1.1 基本结构 P1X ECM(Error Control Module)收集从不同的错误源和监控电路发来的错误信号&#xff0c;并通过error pin(ERROROUTZ)对外输出、产生中断并发出ECM reset信号。 P1x-C系列根据产品型号不同&#xff0c;ECM个数也不相同&#xff0c;如下&#xff1a; 对应寄存器基地…

高效合并视频剪辑:批量操作,省时省力,提高效率

在视频制作领域&#xff0c;合并视频剪辑是一个必不可少的环节。然而&#xff0c;逐个合并视频文件不仅费时且效率低下&#xff0c;还容易出现错误。通过批量操作的方式&#xff0c;可以一次性处理多个视频文件&#xff0c;大大节省了时间和精力&#xff0c;提高了工作效率。本…

【文件存储服务器】Minio使用

文章目录 2.2 Minio使用2.2.1 Minio介绍2.2.2 Minio安装Windows安装Linux安装 2.2.3 Minio入门 2.3 上传文件接口2.3.1 FileUploadController2.3.2 FileUploadService2.3.3 MinioProperties2.3.4 配置文件内容2.3.5 主启动类加上EnableConfigurationProperties 2.4 前端对接 2.…

Typora 最新激活方法

Markdown是一种可以使用普通文本编辑器编写的标记语言&#xff0c;通过简单的标记语法&#xff0c;它可以使普通文本内容具有一定的格式&#xff0c;其目标是实现易读易写。而Typora则是一个非常不错的Markdown编辑器&#xff0c;它的界面非常的简洁直观&#xff0c;并且功能各…

15. 机器学习 - 支持向量机

Hi, 你好。我是茶桁。 逻辑回归预测心脏病 在本节课开始呢&#xff0c;我给大家一份逻辑回归的练习&#xff0c;利用下面这个数据集做了一次逻辑回归预测心脏病的练习。 本次练习的代码在「茶桁的AI秘籍」在Github上的代码库内&#xff0c;数据集的获取在文末。这样做是因为我…

探究栈帧的奥妙

目录 探究栈帧的奥妙 引言 浅浅说一下栈 问问自己几个问题 什么是栈帧 栈帧的维护 汇编预备知识 小例子 访问栈帧里的数据 例子 栈帧是如何切换的 栈帧是如何处理参数和返回值的 探究栈帧的奥妙 作者申明&#xff1a; 文中有些名词可能不太官方&#xff0c;大部分…

UEditor配置后端上传图片

&#x1f525;博客主页&#xff1a; 破浪前进 &#x1f516;系列专栏&#xff1a; Vue、React、PHP ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 后端框架&#xff1a;Fastadmin 目录结构&#xff1a; 代码&#xff1a; {"imageActionName": "uploadimage&q…

样式迁移 - Style Transfer

所谓风格迁移&#xff0c;其实就是提供一幅画(Reference style image)&#xff0c;将任意一张照片转化成这个风格&#xff0c;并尽量保留原照的内容(Content)。 将样式图片中的样式迁移到内容图片上&#xff0c;得到合成图片。 基于CNN的样式迁移 奠基性工作&#xff1a; 首先…

优先队列PriorityQueue

前言 PriorityQueue这个队列不知道大家使用过吗&#xff0c;反正我用的很少&#xff0c;主要对它不是很了解&#xff0c;今天我带领大家剖析下PriorityQueue这个优先级队列。 PriorityQueue介绍 顾名思义&#xff0c;PriorityQueue是优先队列的意思。优先队列的作用是能保证每…

基于SpringBoot的疫苗发布和接种预约系统

目录 前言 一、技术栈 二、系统功能介绍 管理员功能实现 疫苗信息管理 医院信息管理 医生管理 医生功能实现 预约接种管理 疫苗信息查看 医院信息查看 用户功能实现 在线论坛 疫苗信息 医院信息 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 如…

Java高并发应对策略:探索解决秒杀问题的几种成功方案

01 什么是高并发&#xff1f; 高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一&#xff0c;它通常是指&#xff0c;通过设计保证系统能够同时并行处理很多请求。高并发相关常用的一些指标有响应时间(Response Time)&#xff0c;吞吐量(Throughput)&a…

上下相机对位,上下贴合,上下相机映射对位场景案例

场景描述 适用场景&#xff1a;上下相机映射对位场景&#xff0c;机械手在固定上料位置取料&#xff0c;然后放置到料盘内/贴合 到目标位置&#xff1b;当上料与料盘位置都会出现偏差时可采用上下相机映射对位。 案例场景目标&#xff1a; 位置目标&#xff1a;将图 1 中物料的…

Linux 基本语句_9_C语言_生产者消费者

完整版生产者代码&#xff1a; #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <sys/file.h> #include <string.h>#define MAXLE…