使用opencv的matchTemplate进行银行卡卡号识别

![字体文件](https://img-blog.csdnimg.cn/3a16c87cf4d34aceb0778c4b20ddadb2.png#pic_centere5628b04eb93564c23ed0f58a360a10.png

import cv2
import numpy as np


def show_img(img, name="temp"):
    img = cv2.resize(img, (0, 0), fx=3, fy=3)
    cv2.imshow(name, img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


def show_img_normal(img, name="temp"):
    img = cv2.resize(img, (0, 0), fx=0.5, fy=0.5)
    cv2.imshow(name, img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


# 排序方法
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 == 'bottom-to-top' or method == 'top-to-bottom':
        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)


# 读取图像
number_o = cv2.imread("./source/e5628b04eb93564c23ed0f58a360a10.png")
# 转灰度图
number_gary = cv2.cvtColor(number_o, cv2.COLOR_BGR2GRAY)
# 二值化操作
_, number_threshold = cv2.threshold(number_gary, 100, 255, cv2.THRESH_BINARY_INV)
# show_img(number_threshold, "number_threshold")
# 轮廓检测,只检测外轮廓,绘制到原图上
contours, hierarchy = cv2.findContours(number_threshold, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
number_contours = cv2.drawContours(number_o, contours, -1, (0, 255, 0), 1)
# show_img(number_contours, "number_contours")

# 绘制所有轮廓的方形边界
number_boundingRect = number_o.copy()
number_imgs = []
contours = sort_contours(contours)[0]
# contours.sort_cont(key=cv2.INTER_AREA, reverse=True)
for cnt in contours:
    x, y, w, h = cv2.boundingRect(cnt)
    cv2.rectangle(number_boundingRect, (x, y), (x + w, y + h), (0, 0, 255), 1)

    # 显示每一个模板,作为单独图片
    number_one = number_threshold[y: y + h, x:x + w]
    number_one = cv2.resize(number_one, (35, 50))
    number_imgs.append(number_one)
    # show_img(number_one, "every")

# 显示每一个模板,所有.展示外边框信息数据
show_img(number_boundingRect, "number_boundingRect")

im_o = cv2.rotate(cv2.imread("./source/data/1.jpg"), cv2.ROTATE_90_COUNTERCLOCKWISE)
im_gary = cv2.cvtColor(im_o, cv2.COLOR_BGR2GRAY)
# show_img_normal(im_gary, "im_gary")

# 构建卷积核
kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (8, 2))

# 执行礼帽操作,高亮一下有效信息
im_tophat = cv2.morphologyEx(im_gary, cv2.MORPH_TOPHAT, kernel=kernel2, iterations=1)
# show_img_normal(im_tophat, "im_tophat")

# 闭操作,将临近相似区域连起来
im_close = cv2.morphologyEx(im_tophat, cv2.MORPH_CLOSE, kernel2, iterations=8)
# show_img_normal(im_close, "im_close")

# 二值化,分离信息
_, im_threshold = cv2.threshold(im_close, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# show_img_normal(im_threshold, "im_threshold")

# 中值滤波去除噪音点
im_medianBlur = cv2.medianBlur(im_threshold, 9)
# show_img_normal(im_medianBlur, "im_medianBlur")

# 再执行闭操作,连起来相似区域
im_close2 = cv2.morphologyEx(im_medianBlur, cv2.MORPH_CLOSE, kernel2, iterations=2)
# show_img_normal(im_close2, "im_close2")

# 执行轮廓提取
contours, hierarchy = cv2.findContours(im_close2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

validCnt = []
# 所有的边框边界
im_boundingRect_all = cv2.cvtColor(im_close2.copy(), cv2.COLOR_GRAY2BGR)
im_boundingRect_valid = cv2.cvtColor(im_close2.copy(), cv2.COLOR_GRAY2BGR)

# 记录所有的面积数据
area = np.empty((len(contours),))

# 遍历一下,填充面积,后面算平均值和标准差,来过滤过小数据
for index, cnt in enumerate(contours):
    area[index] = cv2.contourArea(cnt)

mean = np.mean(area)
std = np.std(area)

for cnt in contours:
    x, y, w, h = cv2.boundingRect(cnt)
    # 计算面积
    areaX = cv2.contourArea(cnt)
    # 计算长宽比
    aspectRatio = w / h
    # 绘制长宽比和值
    cv2.rectangle(im_boundingRect_all, (x, y), (x + w, y + h), (0, 255, 0), 2)
    cv2.putText(im_boundingRect_all, f"{aspectRatio:.3f}", (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 0), 2)
    # 只有指定比例和面积不是过小的 > mean + std的,选出来
    if (2.8 < aspectRatio < 3.2 or 4.2 < aspectRatio < 5.8 or 8 < aspectRatio < 10) and (areaX > mean - 0 * std):
        validCnt.append(cnt)
        cv2.rectangle(im_boundingRect_valid, (x, y), (x + w, y + h), (0, 255, 255), 2)
        cv2.putText(im_boundingRect_valid, f"{aspectRatio:.3f}", (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 255), 2)

# show_img_normal(im_boundingRect_all, "im_boundingRect_all")
# show_img_normal(im_boundingRect_valid, "im_boundingRect_valid")

# 将有效区域定义出来ROI区域,进行边缘检测,拆分单个字符
for cnt in validCnt:
    x, y, w, h = cv2.boundingRect(cnt)
    # 显示roi区域
    im_roi = im_gary[y - 15:y + h + 5, x - 15:x + w + 5]
    # show_img(im_roi)

    # 二值化
    _, im_roi_threshold = cv2.threshold(im_roi, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
    # show_img(im_roi_threshold, "im_roi_threshold")

    # 闭操作合并一些边界
    k = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
    im_roi_close = cv2.morphologyEx(im_roi_threshold, cv2.MORPH_CLOSE, k, iterations=2)
    # show_img(im_roi_close, "im_roi_close")

    # 轮廓检测
    im_roi_bound = cv2.cvtColor(im_roi.copy(), cv2.COLOR_GRAY2BGR)
    im_roi_contours, hierarchy = cv2.findContours(im_roi_close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    im_roi_contours = sort_contours(im_roi_contours)[0]

    # im_roi_bound = im_roi_close.copy()
    for cnt_roi in im_roi_contours:
        xr, yr, wr, hr = cv2.boundingRect(cnt_roi)
        cv2.rectangle(im_roi_bound, (xr, yr), (xr + wr, yr + hr), (255, 0, 0), 1)
        im_roi_close_one = cv2.resize(im_roi_close[yr: yr + hr, xr:xr + wr], (35, 50))
        # 显示切分出来的单个数字
        # show_img(im_roi_close_one, "im_roi_close_one")
        # 和10个数字比
        scores = []
        for number_img_one in number_imgs:
            score = cv2.matchTemplate(im_roi_close_one, number_img_one, cv2.TM_CCOEFF_NORMED)
            scores.append(score)
        maxIndex = scores.index(max(scores))
        cv2.putText(im_o, str(maxIndex), (x + xr, y + yr), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 255), 2)
        cv2.putText(im_roi_bound, str(maxIndex), ( xr, yr + 10), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 255), 1)
    # 显示切分的轮廓
    # show_img(im_roi_bound, "im_roi_bound")
show_img_normal(im_o, "im_o")

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

242. 有效的字母异位词

这篇文章会收录到 :算法通关村第十二关-白银挑战字符串经典题目-CSDN博客 242. 有效的字母异位词 描述 : 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 注意&#xff1a;若 s 和 t 中每个字符出现的次数都相同&#xff0c;则称 s 和 t …

VsCode 调试 MySQL 源码

1. 启动 MySQL 2. 查看 MySQL 进程号 [root ~]# ps -ef | grep mysqld root 21479 1 0 Nov01 ? 00:00:00 /bin/sh /usr/local/mysql/bin/mysqld_safe --datadir/usr/local/mysql/data --pid-file/usr/local/mysql/data/mysqld.pid root 26622 21479 0 …

【JDK21】详解虚拟线程

目录 1.概述 2.虚拟线程是为了解决哪些问题 2.1.线程切换的巨大代价 2.2.哪些情况会造成线程的切换 2.3.线程资源是有限的 3.虚拟线程 4.适用场景 1.概述 你发任你发&#xff0c;我用JAVA8&#xff1f;JDK21可能要对这句话say no了。 现在Oracle JDK是每4个版本&#x…

minio分布式存储系统

目录 拉取docker镜像 minio所需要的依赖 文件存放的位置 手动上传文件到minio中 工具类上传 yml配置 config类 service类 启动类 测试类 图片 视频 删除minio服务器的文件 下载minio服务器的文件 拉取docker镜像 拉取稳定版本:docker pull minio/minio:RELEASE.20…

FLASK博客系列6——数据库之谜

我们上一篇已经实现了简易博客界面&#xff0c;你还记得我们的博客数据是自己手动写的吗&#xff1f;但实际应用中&#xff0c;我们是不可能这样做的。大部分程序都需要保存数据&#xff0c;所以不可避免要使用数据库。我们这里为了简单方便快捷&#xff0c;使用了超级经典的SQ…

MySOL常见四种连接查询

1、内联接 &#xff08;典型的联接运算&#xff0c;使用像 或 <> 之类的比较运算符&#xff09;。包括相等联接和自然联接。 内联接使用比较运算符根据每个表共有的列的值匹配两个表中的行。例如&#xff0c;检索 students和courses表中学生标识号相同的所有行。 2、…

linux下的工具---vim

一、了解vim 1、vim是linux的开发工具 2、vi/vim的区别简单点来说&#xff0c;它们都是多模式编辑器&#xff0c;不同的是vim是vi的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0c;而且还有一些新的特性在里面。例如语法加亮&#xff0c;可视化操作不仅可以在终端运行…

前端OFD文件预览(vue案例cafe-ofd)

0、提示 下面只有vue的使用示例demo &#xff0c;官文档参考 cafe-ofd - npm 其他平台可以参考 ofd - npm 官方线上demo: ofd 1、安装包 npm install cafe-ofd --save 2、引入 import cafeOfd from cafe-ofd import cafe-ofd/package/index.css Vue.use(cafeOfd) 3、使…

计算机服务器中了mallox勒索病毒如何处理,mallox勒索病毒解密文件恢复

科技技术的发展推动了企业的生产运营&#xff0c;网络技术的不断应用&#xff0c;极大地方便了企业日常生产生活&#xff0c;但网络毕竟是一把双刃剑&#xff0c;网络安全威胁一直存在&#xff0c;近期&#xff0c;云天数据恢复中心接到很多企业的求助&#xff0c;企业的计算机…

文件夹重命名技巧:如何整理过长且混乱的文件夹名称

当浏览计算机文件夹时&#xff0c;有时候会遇到一些过长且混乱的文件夹名称&#xff0c;给文件夹管理带来不便。倘若手动修改文件夹名称会出现错误的机率过大&#xff0c;且这样操作太耗费时间和精力。有什么方法能够避免手动修改文件夹名称&#xff0c;提升工作效率的方法呢&a…

万字详解,和你用RAG+LangChain实现chatpdf

像chatgpt这样的大语言模型(LLM)可以回答很多类型的问题,但是,如果只依赖LLM,它只知道训练过的内容,不知道你的私有数据:如公司内部没有联网的企业文档,或者在LLM训练完成后新产生的数据。(即使是最新的GPT-4 Turbo,训练的数据集也只更新到2023年4月)所以,如果我们…

leetCode 841. 钥匙和房间 图遍历 深度优先遍历+广度优先遍历 + 图解

841. 钥匙和房间 - 力扣&#xff08;LeetCode&#xff09; 有 n 个房间&#xff0c;房间按从 0 到 n - 1 编号。最初&#xff0c;除 0 号房间外的其余所有房间都被锁住。你的目标是进入所有的房间。然而&#xff0c;你不能在没有获得钥匙的时候进入锁住的房间。当你进入一个房…

Android 12 打开网络ADB并禁用USB连接ADB

平台 RK3588 Android 12 Android 调试桥 (adb) Android 调试桥 (adb) 是一种功能多样的命令行工具&#xff0c;可让您与设备进行通信。adb 命令可用于执行各种设备操作&#xff0c;例如安装和调试应用。adb 提供对 Unix shell&#xff08;可用来在设备上运行各种命令&am…

保护IP地址不被窃取的几种方法

随着互联网的普及和信息技术的不断发展&#xff0c;网络安全问题日益凸显。其中&#xff0c;保护个人IP地址不被窃取成为了一个重要的问题。IP地址是我们在互联网上的身份标识&#xff0c;如果被他人获取&#xff0c;就可能导致个人隐私泄露、计算机受到攻击等一系列问题。因此…

笔记62:注意力汇聚 --- Nadaraya_Watson 核回归

本地笔记地址&#xff1a;D:\work_file\&#xff08;4&#xff09;DeepLearning_Learning\03_个人笔记\3.循环神经网络\第10章&#xff1a;动手学深度学习~注意力机制 a a a a a a a a a a a a a a a a

常见面试题-Netty中ByteBuf类

了解 Netty 中的 ByteBuf 类吗&#xff1f; 答&#xff1a; 在 Java NIO 编程中&#xff0c;Java 提供了 ByteBuffer 作为字节缓冲区类型&#xff08;缓冲区可以理解为一段内存区域&#xff09;&#xff0c;来表示一个连续的字节序列。 Netty 中并没有使用 Java 的 ByteBuff…

SpringBoot详解

一、介绍 Spring Boot 是一个基于 Spring 框架的开源框架&#xff0c;用于构建微服务和 Web 应用程序。它可以帮助开发者轻松创建独立的、基于 Spring 的应用程序&#xff0c;并在较短的时间内完成项目的开发。 二、核心 1. 约定大于配置 Spring Boot 通过自动化配置、约定优…

【C++】静态成员

静态成员就是在成员变量和成员函数前加上关键字static&#xff0c;称为静态成员。 静态成员分为&#xff1a; 静态成员变量 所有对象共享同一份数据在编译阶段分配内存类内声明&#xff0c;类外初始化 静态成员函数 所有对象共享同一个函数静态成员函数只能访问静态成员变量 …

Java制作“简易王者荣耀”小游戏

第一步是创建项目 项目名自拟 第二部创建个包名 来规范class 然后是创建类 GameFrame 运行类 package com.sxt;import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; im…

Flask 运用Xterm实现交互终端

Xterm是一个基于X Window System的终端仿真器&#xff08;Terminal Emulator&#xff09;。Xterm最初由MIT开发&#xff0c;它允许用户在X Window环境下运行文本终端程序。Xterm提供了一个图形界面终端&#xff0c;使用户能够在图形桌面环境中运行命令行程序。而xterm.js是一个…