深度学习图像处理基础工具——opencv 实战信用卡数字识别
任务 信用卡数字识别
穿插之前学的知识点 形态学操作 模板匹配 等
总体流程与方法
1.有一个模板 2 用轮廓检测把模板中数字拿出来 外接矩形(模板和输入图像的大小要一致
)3 一系列预处理操作
问题的解决思路
1.分析准备:准备模板,读取文件——转化为灰度图——转化为二值图——提取轮廓——遍历每一个轮廓 得到每个数字的模板,排序,大小裁剪为指定大小(57*88)digits[i] = roi
2.处理输入的带数字的银行卡 (1)预处理 我们需要拿到的是 一组一组的数字 一共四组 读取文件——变换大小——转化为灰度图——形态学操作(礼帽操作突出明亮区域——梯度操作计算轮廓信息——闭操作将数字连在一起——二值化操作寻找合适阈值——再来一个闭操作——计算轮廓——遍历轮廓(选择合适区域 排除不是数字的轮廓)——轮廓排序——遍历轮廓的每一个数字——预处理计算每一组轮廓——计算每一组中的每一个值——匹配得分——得到数字画出来——得到结果——打印结果)
#设置参数
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())#解析参数 将参数转化为字典的形式
img = cv2.imread(args["template"])
def cv_show(name,img):cv2.imshow(name, img)cv2.waitKey(0)# waitKey()#是在一个给定的时间内(单位ms)等待用户按键触发 0 无限等待 按任意键继续cv2.destroyAllWindows()
1.cv2.cvtColor颜色空间转换函数 cv2.imread()和cv2.cvtColor() 的使用-CSDN博客
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#颜色空间转换函数
2.cv2.threshold 二值化函数 [OpenCV] cv2.threshold二值化函数使用方法总结_cv2.threshold函数-CSDN博客 ref = cv2.threshold(ref, 100, 255, cv2.THRESH_BINARY_INV)[1]#二值化函数
3.计算轮廓
refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)#查找轮廓函数 只检测外轮廓 只保留四个点轮廓信息
4.画出轮廓
cv2.drawContours(img,refCnts,-1,(0,0,255),3) #画出轮廓函数
5.轮廓排序
refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0] #排序,从左到右,从上到下
6.遍历轮廓
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
2.0初始化卷积核 rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3)) sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
2.1 对要识别的图像进行大小灰度处理
#读取输入图像,预处理 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)
2.2 #礼帽操作,突出更明亮的区域
#礼帽操作,突出更明亮的区域 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相当于用3*3的ksize=-1)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)
2.3#通过闭操作(先膨胀,再腐蚀)将数字连在一起 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)2.4# 计算轮廓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 digitsgroupOutput = []# 根据坐标提取每一个组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)
总的代码
# 导入工具包
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)# waitKey()–是在一个给定的时间内(单位ms)等待用户按键触发 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, 100, 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相当于用3*3的ksize=-1)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 digitsgroupOutput = []# 根据坐标提取每一个组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)
相关文章:

深度学习图像处理基础工具——opencv 实战信用卡数字识别
任务 信用卡数字识别 穿插之前学的知识点 形态学操作 模板匹配 等 总体流程与方法 1.有一个模板 2 用轮廓检测把模板中数字拿出来 外接矩形(模板和输入图像的大小要一致 )3 一系列预处理操作 问题的解决思路 1.分析准备:准备模板&#…...
【HBase】HBase高性能架构:如何保证大规模数据的高可用性
HBase高性能原理 HBase 能够提供高性能的数据处理能力,主要得益于其设计和架构的几个关键方面。这些设计特点使得 HBase 特别适合于大规模、分布式的环境中进行高效的数据读写操作。以下是 HBase 高性能的主要原因: 1. 基于列的存储 HBase 是一个列式…...
JAVA基础两个项目案例代码
1.JAVA使用ArrayList上架菜品案例 视频参考链接 创建一个Food.java类 package org.example;// 菜品类 public class Food {private String name; // 菜品名private double price; // 价格private String desc; // 菜品描述public Food() {}public Food(String name, Double …...

asp.net core 网页接入微信扫码登录
创建微信开放平台账号,然后创建网页应用 获取appid和appsecret 前端使用的vue,安装插件vue-wxlogin 调用代码 <wxlogin :appid"appId" :scope"scope" :redirect_uri"redirect_uri"></wxlogin> <scri…...

【板栗糖GIS】如何给微软拼音输入法加上小鹤双拼
【板栗糖GIS】如何给微软拼音输入法加上小鹤双拼 用过在注册表里新建的方法,结果弄完没有出现小鹤双拼方案,想到了自己写reg表 目录 1. 新建一个txt文件 2. 把.txt的后缀名改成.reg,双击运行 3. 在设置中找到微软输入法-常规 1. 新建一个…...
如何解决微信小程序无法使用css3过度属性transition
由于微信小程序不支持CSS3过度属性transition,所以我们需要利用微信小程序api进行画面过度的展示 首先是官方示例: wxml: <view animation="{{animationData}}" style="background:red;height:100rpx;width:100rpx"></view> js: Page(…...
【软件设计师知识点】九、网络与信息安全基础知识
文章目录 计算机网络的概念网络分类网络拓扑结构网络体系结构ISO/OSI 7层参考模型TCP/IP 4层模型TCP/IP 协议族应用层协议传输层协议网络层协议IP 地址IPV4 数据报IP 地址分类子网划分子网掩码IPv6地址...

广东省道路货物运输资格证照片回执可手机线上办理
广东省道路运输资格证是从事道路运输业务、危险品道路运输人员的必要证件,而在办理该证件的过程中,驾驶员照片回执是一项必不可少的材料。随着科技的发展和移动互联网的普及,现在办理驾驶员照片回执已经不再需要亲自前往照相馆,而…...

【微信小程序——案例——本地生活(列表页面)】
案例——本地生活(列表页面) 九宫格中实现导航跳转——以汽车服务为案例(之后可以全部实现页面跳转——现在先实现一个) 在app.json中添加新页面 修改之前的九宫格view改为navitage 效果图: 动态设置标题内容—…...
【设计模式】SOLID设计原则
1、什么是SOLID设计原则 SOLID 是面向对象设计中的五个基本设计原则的首字母缩写,它们是: 单一职责原则(Single Responsibility Principle,SRP): 类应该只有一个单一的职责,即一个类应该有且只…...

基于java+springboot+vue实现的智能停车计费系统(文末源码+Lw+ppt)23-30
摘 要 随着人们生活水平的高速发展,智能停车计费信息管理方面在近年来呈直线上升,人们也了解到智能停车计费的实用性,因此智能停车计费的管理也逐年递增,智能停车计费信息的增加加大了在管理上的工作难度。为了能更好的维护智能…...

IntelliJ IDEA 2022.3.2 解决decompiled.class file bytecode version:52.0(java 8)
1 背景 使用idea 打开一个Kotlin语言编写的demo项目,该项目使用gradle构建。其gradle文件如下: plugins {id javaid org.jetbrains.kotlin.jvm version 1.8.20 } group me.administrator version 1.0-SNAPSHOTrepositories {mavenCentral()jcenter()…...

C++11 设计模式1. 模板方法(Template Method)模式学习。UML图
一 什么是 "模板方法(Template Method)模式" 在固定步骤确定的情况下,通过多态机制在多个子类中对每个步骤的细节进行差异化实现,这就是模板方法模式能够达到的效果。 模板方法模式属于:行为型模式。 二 &…...

HarmonyOS实战开发-自定义分享
介绍 自定义分享主要是发送方将文本,链接,图片三种类型分享给三方应用,同时能够在三方应用中展示。本示例使用数据请求 实现网络资源的获取,使用屏幕截屏 实现屏幕的截取,使用文件管理 实现对文件,文件目录的管理&…...

Spring源码刨析之配置文件的解析和bean的创建以及生命周期
public void test1(){XmlBeanFactory xmlBeanFactory new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));user u xmlBeanFactory.getBean("user",org.xhpcd.user.class);// System.out.println(u.getStu());}先介绍一个类XmlBeanFac…...

如何使用 Grafana 监控文件系统状态
当 JuiceFS 文件系统部署完成并投入生产环境,接下来就需要着手解决一个非常重要的问题 —— 如何实时监控它的运行状态?毕竟,它可能正在为关键的业务应用或容器工作负载提供持久化存储支持,任何小小的故障或性能下降都可能造成不利…...

智能革命:未来人工智能创业的天地
智能革命:未来人工智能创业的天地 一、引言 在这个数字化迅速变革的时代,人工智能(AI)已经从一个边缘科学发展成为推动未来经济和社会发展的关键动力。这一技术领域的飞速进步,不仅影响着科技行业的每一个角落,更是为创业者提供了…...

4月14日总结
java学习 一.多线程 简介:多线程是计算机科学中的一个重要概念,它允许程序同时执行多个任务或操作。在单个程序内部,多线程使得代码可以并行执行,从而提高程序的性能和响应速度。 这里先来介绍一下创建多线程的几种方法。 1.扩展…...
kafka---broker相关配置
一、Broker 相关配置 1、一般配置 broker.id 当前kafka服务的sid(server id),在kafka集群中,该值是唯一的(unique),如果未设置此值,kafka会自动生成一个int值;为了防止自动生成的值与用户设置…...

【Golang学习笔记】从零开始搭建一个Web框架(二)
文章目录 模块化路由前缀树路由 前情提示: 【Golang学习笔记】从零开始搭建一个Web框架(一)-CSDN博客 模块化路由 路由在kilon.go文件中导致路由和引擎交织在一起,如果要实现路由功能的拓展增强,那将会非常麻烦&…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...

Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...