【python版CV】- 银行卡号识别项目

简介: 【python版CV】- 银行卡号识别项目

开头一叙:

这是第一次接触OpenCV的项目,在网上也看到很多大佬完成这个项目。首先感觉这个项目涉及的知识点都是之前学到,其中包括(【读取图像、灰度转换、二值化操作】【礼帽、归一化、闭操作】以及【获取图像轮廓、描绘轮廓、模板匹配操作】。接下来我会详细介绍项目操作步骤,涉及到的知识点我也会客串一下。

准备工作

需要读取的银行卡:把下面红框的圈起来的数字分别读取读取

模板匹配的数字

本来想着自己画的,不过自己画技不行,读取结果有点偏差,就去百度找了一张模板图

设计的库
from imutils import contours
import numpy as np
import argparse
import imutils
import cv2 as cv
def cv_show(name,img):
    cv.imshow(name, img)
    #等待时间,毫秒级别。0表示任意键终止,1000表示1000毫秒关闭
    cv.waitKey(0)
    cv.destroyAllWindows()#触发条件时,关闭

模板匹配操作

1 灰度转换、二值化
shu=cv.imread("E:\\Pec\\img\\shu.jpg",1)
# cv_show("shu",shu)
# #灰度图转换
ref=cv.cvtColor(shu,cv.COLOR_BGR2GRAY)
#二值图像
ref=cv.threshold(ref,0,255,cv.THRESH_BINARY_INV)[1]
cv_show('erzhi',ref)

二值化相关知识点:

ret,dst=cv2.threshold(src,thresh,maxval,type)

src:输入图,只能输入单通道图像,一般为灰度图

dst:输出图

thresh:阈值

maxval:当像素值超过阈值(或者小于阈值),所赋予的值

type:二值化(大于取一个值,小于取另一个值)操作的类型,包含五种类型

THRESH_BINARY:超过阈值部分取maxval,否则为0

THRESH_BINARY_INV: THRESH_BINARY的反转

THRESH_TRUNC:大于阈值部分设为阈值,否则不变

THRESH_TOZERO:大于阈值部分不改变,否则设为0

THRESH_TOZERO_INV: THRESH_TOZERO的反转

2 获取模板图的轮廓
refCnts,hierarchy=cv.findContours(ref,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
res=cv.drawContours(shu,refCnts,-1,(0,255,0),2)
cv_show('res',res)

两个参数解释:

参数contours:contours定义为“vector<vector> contours”,是一个双重向量(向量内每个元素保存了一组由连续的Point构成的点的集合的向量),每一组点集就是一个轮廓,有多少轮廓,contours就有多少元素;

参数hierarchy:hierarchy定义为“vector hierarchy”,Vec4i的定义:typedef Vec<int, 4> Vec4i;(向量内每个元素都包含了4个int型变量),所以从定义上看,hierarchy是一个向量,向量内每个元素都是一个包含4个int型的数组。向量hierarchy内的元素和轮廓向量contours内的元素是一一对应的,向量的容量相同。hierarchy内每个元素的4个int型变量是hierarchy[i][0] ~ hierarchy[i][3],分别表示当前轮廓 i 的后一个轮廓、前一个轮廓、父轮廓和内嵌轮廓的编号索引。如果当前轮廓没有对应的后一个轮廓、前一个轮廓、父轮廓和内嵌轮廓,则相应的hierarchy[i][*]被置为-1。

获取轮廓相关知识点:

cv.findContours(img,mode,method)

mode:轮廓检索模式

RETR_EXTERNAL:只检索最外面轮廓

RETR_LIST:检索所有轮廓,并将其保存到一条链表当中

RETR_CCOMP:检索所有轮廓,并将它们组织为两层;顶层是各部分的外部边界,第二层是空洞的边界

RETR_TREE:检索所有轮廓,并重构嵌套轮廓层次(常用)

method:轮廓逼近方法


CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点序列)

CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是函数保留它们终点部分绘制轮廓相关知识点:

res=cv.drawContours(shu,refCnts,-1,(0,255,0),2)

绘制图像轮廓函数drawContours():

  • shu:图像
  • refCnts:轮廓信息(上述获取的轮廓信息)
  • -1:轮廓索引(需要多少轮廓,-1默认全部)
  • 颜色模式
  • 线条厚度
3 遍历每一个轮廓

(1)测试:是否检测到十个轮廓

print(np.array(refCnts).shape)

(2)对轮廓进行排序,从左到右,从上到下

refCnts = sorted(refCnts,key=lambda x: x[0][0][0], reverse=False)

排序sorted(key=lambda)相关知识点:

sorted(iterable,key,reverse)

iterable:排序对象,如字符串,列表,元组,字典等可迭代对象。

key:key=lambda 元素: 元素[字段索引]

例如:想对元素第二个字段排序,则

key=lambda y: y[1] 备注:这里y可以是任意字母,等同key=lambda x: x[1]

reverse:排序规则,reverse=True 降序, reverse=False 升序(默认)。

(3)遍历每一个轮廓,指定轮廓对应的数字

#创建一个字典,轮廓对应相应数字
digits={}
#遍历每一个轮廓
for (i,c) in enumerate(refCnts):#i轮廓索引,c是每一个轮廓
    #计算外接轮廓矩形并且resize成合适大小
    (x,y,w,h)=cv.boundingRect(c)
    #找到感兴趣区域
    roi=ref[y:y+h,x:x+w]
    roi=cv.resize(roi,(57,88))
    cv_show('roi',roi)
    #每一个数字对应一个模板
    digits[i]=roi

遍历参考图像轮廓:

在循环中, i 保存数字名称/编号, c 保存轮廓。 我们围绕每个轮廓 c 计算一个边界框,用于存储矩形的 (x, y) 坐标和宽度/高度。使用边界矩形参数从 ref(参考图像)中提取 roi。 该 ROI 包含数字。将每个 ROI 大小调整为 57×88 像素的固定大小。 我们需要确保每个数字都调整为固定大小,以便在本教程后面的数字识别中应用模板匹配。


很尴尬的就是:最开始我把高度和宽度写反了,导致结果读取数字不准确

(4) 效果展示

银行卡的相关处理

1 对银行卡一些形态学操作

因为要读取银行卡里面的卡号,但是银行卡里面有许多干扰性,如下:我们只需要读取银行卡里面圈红色圈的部分,对于绿色圈里面的信息都是干扰项,我们不需要,通过形态学操作,去除干扰项

(1)形态学操作设计的核:内核视为我们在图像上滑动的小矩阵

根据字体大小,指定合适的核

rectKernel=cv.getStructuringElement(cv.MORPH_RECT,(9,3))
sqKernel=cv.getStructuringElement(cv.MORPH_RECT,(5,5))

(2)对银行卡预处理

#读取图像,预处理
image=cv.imread("E:\\Pec\\img\\kahao.jpg")
image = imutils.resize(image, width=300)
gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)
# cv_show('gray',gray)

imutils–图像处理工具包相关知识:

  • 图像平移Translation:图像在x轴方向左右平移,y轴方向上下平移,
#向右平移25像素,向上平移75像素
translated = imutils.translate(image,25,-75)

图像旋转Rotation

rotated = imutils.rotate(image,90)

图像大小Resizing:改变图像大小,但保持原来图像的长宽比不变。可以只单独设置width或者height;

resized = imutils.resize(image,width=300)
resized = imutils.resize(image,height=300)

(3)礼貌操作(Top-hat):在深色背景(即信用卡号)下显示浅色区域

礼帽=原始输入-开运算结果(开运算:先腐蚀再膨胀)

tophat=cv.morphologyEx(gray,cv.MORPH_TOPHAT,rectKernel)
cv_show('tophat',tophat)

(4)利用Sobel算子,计算图像在X方向的梯度

在学习Sobel算子的时候,是把在X方向梯度和在Y方向的梯度分别算出来,再相加来求出边缘信息。不过B站大佬测试,只需要求出X方向就可以了

gradX=cv.Sobel(tophat,ddepth=cv.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与最小值之间的距离占区间长度的几分之几
gradX=(255*((gradX-minVal)/(maxVal-minVal)))
gradX=gradX.astype("uint8")
cv_show('gradX',gradX)

Sobel算子相关知识:

cv.Sobel(pie,cv.CV_64F,1,0,ksize=3)
  • ddepth:图像的深度,通常指定-1
  • dx和dy分别表示水平和竖直方向
  • ksize是Sobel算子的大小,必须是1,3,5,7.默认是3

(5)通过闭操作(先膨胀,再腐蚀)将数字联系起来,形成一块一块的

gradX=cv.morphologyEx(gradX,cv.MORPH_CLOSE,rectKernel)
cv_show('gradX',gradX)

(6)再进行二值化处理

这是另一种二值化操作,平常的二值化处理阈值是我们指定好的,但是对于一些图像,有双峰,可以自动寻找阈值。THRESH_OTSU会自动寻找合适的阈值,适合双峰,把阈值参数设置为0

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

(7) 再进行一次闭操作

上面图像二值化处理之后,每一块之间还是有一些小缝。通过闭操作将这些小孔去除。如果闭操作一次不行,还可以继续,直到小缝差不多没有了

thresh=cv.morphologyEx(thresh,cv.MORPH_CLOSE,sqKernel)
# thresh=cv.morphologyEx(thresh,cv.MORPH_CLOSE,sqKernel)
# thresh=cv.morphologyEx(thresh,cv.MORPH_CLOSE,sqKernel)
# thresh=cv.morphologyEx(thresh,cv.MORPH_CLOSE,sqKernel)
cv_show('thresh',thresh)

2 计算轮廓

将预处理后轮廓画在原图上,以便于我们观察

threshCnts,hierarchy=cv.findContours(thresh.copy(),cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
cnts=threshCnts #复制操作,下面不会改变原数据
'''
#将预处理后轮廓画在原图上
cur_img=image.copy()
cv.drawContours(cur_img,cnts,-1,(0,0,255),3)
cv_show('img',cur_img)
'''

3 得到卡号轮廓

从上面的图片中我们可以看出,有许多轮廓。但是我们只想要银行卡号的轮廓。通过每个轮廓框的高度和宽度进行筛选

(1)遍历所有轮廓,得到想要的四组数字保存在locs

locs=[]

(2)遍历轮廓

for (i,c) in enumerate(cnts):
    #计算矩形
    (x,y,w,h)=cv.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))

(3)将符合的轮廓从左到右排序

locs=sorted(locs,key=lambda x:x[0])
4 遍历每一个轮廓对应的模板数字

初始化一个列表 output ,它将保存图像的信用卡号。 知道每组四位数字的位置,让我们循环遍历四个排序的组并确定其中的数字。

output=[]
for (i,(gX,gY,gW,gH)) in enumerate(locs):
    groupOutpt=[]
    #根据坐标向四周再扩展一丢丢
    group=gray[gY-5:gY+gH+5,gX-5:gX+gW+5]
    # cv_show('group',group)
    #预处理
    group=cv.threshold(group,0,255,cv.THRESH_BINARY | cv.THRESH_OTSU)[1]
    # cv_show('group',group)
    #计算每一组轮廓
    digitCnts,hierarchy=cv.findContours(group.copy(),cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
    digitCnts = sorted(digitCnts, key=lambda x: x[0][0][0])
    #计算每一个轮廓中的数值
    for c in digitCnts:
        #找到当前数值的轮廓,resize成合适的大小
        (x,y,w,h)=cv.boundingRect(c)
        roi=group[y:y+h,x:x+w]
        roi=cv.resize(roi,(57,88))
        # cv_show('roi',roi)
        #计算匹配得分
        scores=[]
        #在模板中计算每一个得分
        for (digit,digitROI) in digits.items():
            #模板匹配
            result=cv.matchTemplate(roi,digitROI,cv.TM_CCOEFF)
            (_,score,_,_)=cv.minMaxLoc(result)
            scores.append(score)
        #得到最合适的数字
        groupOutpt.append(str(np.argmax(scores)))

(1)读取4个轮廓

(2)取每一个轮廓对应的4个数字,这里参考模板上面读取每一个数字的方法。把获取的4个轮廓近似看成上面的模板轮廓,操作如下:

#计算每一个轮廓中的数值
for c in digitCnts:
  #找到当前数值的轮廓,resize成合适的大小
    (x,y,w,h)=cv.boundingRect(c)
     roi=group[y:y+h,x:x+w]
     roi=cv.resize(roi,(57,88))

(3)计算匹配得分

for (digit,digitROI) in digits.items():
  #模板匹配
  result=cv.matchTemplate(roi,digitROI,cv.TM_CCOEFF)
  #模板匹配用到的参数是TM_CCOEFF,只要匹配结果的最大值
  (_,score,_,_)=cv.minMaxLoc(result)
   scores.append(score)

cv.matchTemplate()相关知识:

methods=['cv.TM_CCOEFF','cv.TM_CCORR','cv.TM_CCOEFF','cv.TM_SQDIFF_NORMED'
    ,'cv.TM_CCORR_NORMED','cv.TM_CCOEFF_NORMED']
#进行模板匹配
res=cv.matchTemplate(img,template,3)
#第三个参数是一个数值,1对应上面的TM_CCOEFF,同理下面
print(res.shape)
min_val,max_val,min_loc,max_loc=cv.minMaxLoc(res)
print(min_val)#最小值
print(max_val)#最大值
print(min_loc)#最小值位置
print(max_loc)#最大值位置

TM_SQDIFF:计算平方不同,计算出来的值越小,越相关

TM_CCORR:计算相关性,计算出来的值越大,越相关

TM_CCOEFF:计算相关系数,计算出来的值越大,越相关

TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关

TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关

TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关

(4)得到最合适的数字

银行卡号上面的每一个数字分别与模板上面0~9一一匹配,然后取里面数值最大的。 返回的是输入列表中最大值的位置

groupOutpt.append(str(np.argmax(scores)))

(5)将每组轮廓也就是4个数字用方框画在原图像上

 cv.rectangle(image,(gX-5,gY-5),(gX+gW+5,gY+gH+5),(0,0,255),1)
 cv.putText(image,"".join(groupOutpt),(gX,gY-15),cv.FONT_HERSHEY_SIMPLEX,0.65,(0,255,0),2)

cv.putText()相关知识:

  • 图片
  • 添加的文字
  • 左上角坐标
  • 字体
  • 字体大小
  • 颜色
  • 字体粗细
5 输出结果
print("Credit Card #: {}".format("".join(output)))
 
Credit Card #: 4000123456789010

总结

至此,整个项目结束了。总体上没有太多生的知识点,都是之前学过的知识点汇总。感觉就是一块一块学习还可以接受,汇总理解有点困难。下个项目文档扫描OCR识别等着下个星期再复习巩固,明天接着下一个知识点学习。不过都看到这了,就一键三连支持一下呗。

相关文章
|
1月前
|
机器学习/深度学习 数据采集 数据可视化
Python 数据分析:从零开始构建你的数据科学项目
【10月更文挑战第9天】Python 数据分析:从零开始构建你的数据科学项目
53 2
|
2月前
|
机器学习/深度学习 算法 TensorFlow
动物识别系统Python+卷积神经网络算法+TensorFlow+人工智能+图像识别+计算机毕业设计项目
动物识别系统。本项目以Python作为主要编程语言,并基于TensorFlow搭建ResNet50卷积神经网络算法模型,通过收集4种常见的动物图像数据集(猫、狗、鸡、马)然后进行模型训练,得到一个识别精度较高的模型文件,然后保存为本地格式的H5格式文件。再基于Django开发Web网页端操作界面,实现用户上传一张动物图片,识别其名称。
92 1
动物识别系统Python+卷积神经网络算法+TensorFlow+人工智能+图像识别+计算机毕业设计项目
|
14天前
|
弹性计算 Linux iOS开发
Python 虚拟环境全解:轻松管理项目依赖
本文详细介绍了 Python 虚拟环境的概念、创建和使用方法,包括 `virtualenv` 和 `venv` 的使用,以及最佳实践和注意事项。通过虚拟环境,你可以轻松管理不同项目的依赖关系,避免版本冲突,提升开发效率。
|
2月前
|
机器学习/深度学习 人工智能 算法
植物病害识别系统Python+卷积神经网络算法+图像识别+人工智能项目+深度学习项目+计算机课设项目+Django网页界面
植物病害识别系统。本系统使用Python作为主要编程语言,通过收集水稻常见的四种叶片病害图片('细菌性叶枯病', '稻瘟病', '褐斑病', '稻瘟条纹病毒病')作为后面模型训练用到的数据集。然后使用TensorFlow搭建卷积神经网络算法模型,并进行多轮迭代训练,最后得到一个识别精度较高的算法模型,然后将其保存为h5格式的本地模型文件。再使用Django搭建Web网页平台操作界面,实现用户上传一张测试图片识别其名称。
119 22
植物病害识别系统Python+卷积神经网络算法+图像识别+人工智能项目+深度学习项目+计算机课设项目+Django网页界面
|
27天前
|
JSON 搜索推荐 API
Python的web框架有哪些?小项目比较推荐哪个?
【10月更文挑战第15天】Python的web框架有哪些?小项目比较推荐哪个?
45 1
|
1月前
|
机器学习/深度学习 人工智能 算法
【玉米病害识别】Python+卷积神经网络算法+人工智能+深度学习+计算机课设项目+TensorFlow+模型训练
玉米病害识别系统,本系统使用Python作为主要开发语言,通过收集了8种常见的玉米叶部病害图片数据集('矮花叶病', '健康', '灰斑病一般', '灰斑病严重', '锈病一般', '锈病严重', '叶斑病一般', '叶斑病严重'),然后基于TensorFlow搭建卷积神经网络算法模型,通过对数据集进行多轮迭代训练,最后得到一个识别精度较高的模型文件。再使用Django搭建Web网页操作平台,实现用户上传一张玉米病害图片识别其名称。
54 0
【玉米病害识别】Python+卷积神经网络算法+人工智能+深度学习+计算机课设项目+TensorFlow+模型训练
|
2月前
|
机器学习/深度学习 算法 TensorFlow
交通标志识别系统Python+卷积神经网络算法+深度学习人工智能+TensorFlow模型训练+计算机课设项目+Django网页界面
交通标志识别系统。本系统使用Python作为主要编程语言,在交通标志图像识别功能实现中,基于TensorFlow搭建卷积神经网络算法模型,通过对收集到的58种常见的交通标志图像作为数据集,进行迭代训练最后得到一个识别精度较高的模型文件,然后保存为本地的h5格式文件。再使用Django开发Web网页端操作界面,实现用户上传一张交通标志图片,识别其名称。
102 6
交通标志识别系统Python+卷积神经网络算法+深度学习人工智能+TensorFlow模型训练+计算机课设项目+Django网页界面
|
2月前
|
机器学习/深度学习 人工智能 算法
【新闻文本分类识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台
文本分类识别系统。本系统使用Python作为主要开发语言,首先收集了10种中文文本数据集("体育类", "财经类", "房产类", "家居类", "教育类", "科技类", "时尚类", "时政类", "游戏类", "娱乐类"),然后基于TensorFlow搭建CNN卷积神经网络算法模型。通过对数据集进行多轮迭代训练,最后得到一个识别精度较高的模型,并保存为本地的h5格式。然后使用Django开发Web网页端操作界面,实现用户上传一段文本识别其所属的类别。
90 1
【新闻文本分类识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Django网页界面平台
|
1月前
|
存储 开发工具 Python
【Python项目】外星人入侵项目笔记
【Python项目】外星人入侵项目笔记
36 3
|
1月前
|
前端开发 JavaScript API
惊呆了!学会AJAX与Fetch API,你的Python Web项目瞬间高大上!
在Web开发领域,AJAX与Fetch API是提升交互体验的关键技术。AJAX(Asynchronous JavaScript and XML)作为异步通信的先驱,通过XMLHttpRequest对象实现了局部页面更新,提升了应用流畅度。Fetch API则以更现代、简洁的方式处理HTTP请求,基于Promises提供了丰富的功能。当与Python Web框架(如Django、Flask)结合时,这两者能显著增强应用的响应速度和用户体验,使项目更加高效、高大上。
50 2