OpenCV图像处理——(实战)信用卡识别

news2024/11/23 7:37:07

总目录

图像处理总目录←点击这里

十四、信用卡数字识别

  • 识别的图片
  • 模板图片

14.1、模板图片处理

在这里插入图片描述

读入图片->灰度图->二值图->计算轮廓->存储每一个模板

如果是所需模板匹配只有一个,课直接读入灰度图像即可
这里有10个模板(0-9),所以需要切割存储为多个模板进行循环匹配

# 预处理
	#  读入图片->灰度图->二值图->计算轮廓->存储每一个模板

# 遍历模板的每一个轮廓并且存储
for (i, c) in enumerate(refCnts):
    # 计算外接矩形并且resize成合适大小
    (x, y, w, h) = cv2.boundingRect(c)
    roi = ref[y:y + h, x:x + w]
    roi = cv2.resize(roi, (57, 88))

    # 每一个数字对应每一个模板
    digits[i] = roi

14.2、识别图片处理

读取图像->灰度->礼帽(去除背景)->梯度(边界)->闭操作(先膨胀,再腐蚀)-找阈值->闭操作->计算轮廓->遍历筛选(宽高比)存储->再次计算轮廓(每个小部分,如4000)

灰度

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

灰度图
礼帽

rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)

tophat
梯度

sobel算子梯度

gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)  # ksize=-1相当于用3*3的

gradX = np.absolute(gradX)
(minVal, maxVal) = (np.min(gradX), np.max(gradX))
gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
gradX = gradX.astype("uint8")

grad
闭操作

gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)

闭操作
找阈值

这里采用双峰自动找合适的阈值,以下为固定写法

thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

阈值
闭操作
让目前轮廓白色填充满

thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)

闭操作
计算轮廓
计算轮廓在原图像中展示

threshCnts, hierarchy =  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)

计算轮廓

筛选

按符合条件的比例进行筛选,筛选出符合条件的

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))

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
再次计算轮廓并处理

遍历上面每一个轮廓中的数字并且和模板匹配

for (i, (gX, gY, gW, gH)) in enumerate(locs):
    # initialize the list of group digits
    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 = contours.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(image, "".join(groupOutput), (gX, gY - 15),
                cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)

    # 得到结果
    output.extend(groupOutput)

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

最终效果

在这里插入图片描述

在这里插入图片描述

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

原图

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

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

源码

主程序

注意初始化参数的输入
在这里插入图片描述
--image ./images/credit_card_01.png --template ocr_a_reference.png

# 导入工具包
from imutils import contours
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)
    cv2.destroyAllWindows()


# 读取一个模板图像
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只保留终点坐标
# 返回的list中每个元素都是图像中的一个轮廓

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)
print(np.array(refCnts).shape)
refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0]  # 排序,从左到右,从上到下
digits = {}

# 遍历每一个轮廓
for (i, c) in enumerate(refCnts):
    # 计算外接矩形并且resize成合适大小
    (x, y, w, h) = cv2.boundingRect(c)
    roi = ref[y:y + h, x:x + w]
    roi = cv2.resize(roi, (57, 88))

    # 每一个数字对应每一个模板
    digits[i] = roi

# 初始化卷积核
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))

# 读取输入图像,预处理
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)

# 礼帽操作,突出更明亮的区域
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
# cv_show('tophat', tophat)
# 
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)  # ksize=-1相当于用3*3的

gradX = np.absolute(gradX)
(minVal, maxVal) = (np.min(gradX), np.max(gradX))
gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
gradX = gradX.astype("uint8")

print(np.array(gradX).shape)
# cv_show('gradX', gradX)

# 通过闭操作(先膨胀,再腐蚀)将数字连在一起
gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
# cv_show('gradX', gradX)
# THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
thresh = cv2.threshold(gradX, 0, 255,
                       cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# cv_show('thresh', thresh)

# 再来一个闭操作

thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)  # 再来一个闭操作
# cv_show('thresh', thresh)

# 计算轮廓

threshCnts, hierarchy = \
    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):
    # initialize the list of group digits
    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 = contours.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(image, "".join(groupOutput), (gX, gY - 15),
                cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)

    # 得到结果
    output.extend(groupOutput)

# 打印结果
print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Image", image)
cv2.waitKey(0)

工具方法

myutils.py

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,h,w
    (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

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

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

相关文章

学校教务管理系统的设计与实现--Word文档可在最后链接处下载,无code

一 设计背景 1.1 课题现状 国家经济水平逐渐的提升&#xff0c;各行各业发展也稳步向前&#xff0c;其业务也繁多起来。慢慢的对其管理需求也要求提高&#xff0c;因此很多行业临着新的困难和机遇&#xff0c;现如今要怎么利用利于对自身发展需要的而且适用的种种技术来提升自身…

【PID优化】基于正余弦算法 (SCA)优化PID实现微型机器人系统位置控制附simulink模型和matlab代码

​✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法…

CMake中math的使用

CMake中的math用于评估数学表达式&#xff0c;其格式如下&#xff1a; math(EXPR <variable> "<expression>" [OUTPUT_FORMAT <format>]) 计算数学<expression>并将<variable>设置为结果值。expression的结果必须可以表示为64位有符号…

用python gdal裁剪栅格数据提取添加xy经纬度和栅格值

用python gdal裁剪栅格数据提取添加xy经纬度和栅格值 问题&#xff1a;把遥感影像转为一张表。 现有一全球经济作物数据alfalfa的产量。 alfalfa是一种亚洲西南部多年生草本植物&#xff0c;是重要的经济作物。在图中也可以看到&#xff0c;主要分布在热带和南美洲。 我们想把…

Android PackageManager 基本使用

Android系统为我们提供了很多服务管理类&#xff0c;PackageManager管理类&#xff0c;它的主要职责是管理应用程序包。 通过PackageManager获取的应用程序信息来自AndroidManifest.xml。 AndroidManifest.xml是Android应用程序中最重要的文件之一&#xff0c;它是Android程序…

java语法复习:注解

目录 一.注解概念 二.常用注解(1) Override: 限定某个函数必须重载其他函数&#xff0c;该注解只能用于函数(2) Deprecated&#xff1a;用于表示某个程序元素&#xff08;类、函数&#xff09;已过时(3) SuppressWarnings&#xff1a;抑制编译器警告 三.元注解 一.注解概念 …

山东大学线性代数-2-行列式

目录 2.2 n阶行列式 2.2.1 代数余子式和余子式 2.2.2 n阶行列式的定义 2.3 特殊行列式的计算 2.3.1 对角行列式 2.3.2 三角行列式 2.3.3 斜三角行列式 2.3.4 其他特殊行列式 2.4 行列式的性质 2.4.1 性质1 2.4.2 性质2 2.4.3 性质3 2.4.4 性质4 2.4.5 性质5 2.5 行列…

【笔试题】【day25】

文章目录第一题&#xff08;缺页中断&#xff09;第二题&#xff08;多线程&#xff09;第三题&#xff08;系统死锁的原因&#xff09;第四题&#xff08;大小端在内存中的存储方式&#xff09;第五题&#xff08;处理器运行时间计算&#xff09;第六题&#xff08;计算机的访…

八.STM32F030C8T6 MCU开发之电源掉电检测案例

八.STM32F030C8T6 MCU开发之电源掉电检测案例 0.总体功能概述 使用STD库–en.stm32f0_stdperiph_lib_v1.6.0。 单片机在正常工作时&#xff0c;因某种原因造成突然掉电&#xff0c;将会丢失数据存储器(RAM)里的数据。在某些应用场合如测量、控制等领域&#xff0c;单片机正常…

RedisMysql同步

1. canal Canal&#xff0c;阿里巴巴 MySQL binlog 增量订阅&消费组件&#xff0c;译意为水道/管道/沟渠&#xff0c;主要用途是基于 MySQL 数据库增量日志解析&#xff0c;提供增量数据订阅和消费。 首先了解一下mysql主备复制原理&#xff1a;   &#xff08;1&#x…

51单片机计算定时器初值

51单片机计算定时器初值前言理论分析工作方式寄存器 TMODGATE 门控位C/T 计数器模式和定时器模式选择位M1 M0 工作方式选择位定时器/计数器控制寄存器 TCONTCON补充&#xff08;中断相关&#xff09;计算过程补充&#xff1a; 方式2运行原理源码前言 芯片使用AT89S51参考书目《…

Vue2.0开发之——Vue基础用法-列表渲染指令(24)

一 概述 列表渲染指令v-forv-for 中的索引使用 key 维护列表的状态key 的注意事项 二 列表渲染指令v-for 2.1 概念 vue 提供了 v-for 列表渲染指令&#xff0c;用来辅助开发者基于一个数组来循环渲染一个列表结构。v-for 指令需要使 用 item in items 形式的特殊语法&#x…

C#程序发布时,一定要好好地保护,不然你会后悔的

上次分享一个C#混淆开源项目《一个对C#程序混淆加密&#xff0c;小巧但够用的小工具》&#xff0c;发现大家都非常感兴趣&#xff0c;但也发现很多人&#xff0c;不了解为什么没有混淆&#xff0c;就会很容易被破解。 所以今天给大家做一个教程&#xff1a;如何通过工具来反编…

[网络工程师]-传输层协议-UDP协议

用户数据协议&#xff08;User Datagram Protocol&#xff0c;UDP&#xff09;是一种不可靠的、无连接的数据报服务。源主机在传送数据前不需要和目标主机建立连接。数据附加了源端口号和目标端口号等UDP报头字段后&#xff0c;直接发往目的主机。这时&#xff0c;每个数据报的…

【数据结构】链表

目录 一、线性表接口 二、单链表 2.1 单链表的结构定义 2.2 头插法 2.3中间位置的插入 2.4尾插法 2.5遍历链表 2.6查询线性表中是否包含指定元素 2.7返回索引为index的元素值 2.8修改索引为index位置的元素为新值&#xff0c;返回修改前的元素值 2.9删除链表中索引为…

公众号免费搜题系统

公众号免费搜题系统 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点击…

嵌入式FreeRTOS学习十,任务调度和任务就绪链表任务调度过程

一.main函数里面的栈是哪里分配的 main函数里面用到的栈&#xff0c;假设为msp,是汇编代码里面设定的&#xff0c;对于STM32F103,在汇编代码里的向量表设置了一个栈_initial_sp,那这个栈是给谁用的呢&#xff1f; 可以看到&#xff0c;这个_initial_sp在内存中分配了一个空间区…

案例驱动,手把手教你学PyTorch(二)

通过案例学PyTorch。 扫码关注《Python学研大本营》&#xff0c;加入读者群&#xff0c;分享更多精彩 Autograd Autograd 是 PyTorch 的自动微分包。多亏了它&#xff0c;我们不需要担心偏导数、链式法则或类似的东西。 那么&#xff0c;我们如何告诉 PyTorch 做它的事情并计…

python在线及离线安装库

目录 一、配置python环境变量&#xff1a; 二、在线安装python库&#xff1a; 三、离线安装python库&#xff1a; 一、配置python环境变量&#xff1a; 1、以windows10为例&#xff0c;右键电脑->>属性: 2、选择高级系统设置&#xff1a; 3、选择环境变量&#xff1a…

八行代码一键照片转漫画

有些小程序可以实现照片转漫画的功能&#xff0c;本文和你一起来探索其背后的原理。用Python实现八行代码一键照片转漫画。    文章目录一、效果展示二、代码详解1 导入库2 照片转漫画一、效果展示 在介绍代码之前&#xff0c;先来看下本文的实现效果。    喜欢的小伙伴也…