当前位置:网站首页>信用卡数字识别(opencv,代码分析)
信用卡数字识别(opencv,代码分析)
2022-07-26 15:05:00 【csp_】
目录
项目源码
可在github下载:
https://github.com/chenshunpeng/Credit-card-digital-identification
环境配置与预处理
导入工具包
# imutils是在OPenCV基础上的一个封装,达到更为简结的调用OPenCV接口的目的
from imutils import contours
# 主要用于对多维数组执行计算,极大地简化了向量和矩阵的操作处理
import numpy as np
# argparse 是 python 用于解析命令行参数和选项的标准模块
import argparse
# OpenCV2(OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库)
import cv2
# 导入myutils.py文件里面的方法
import myutils
设置参数
关于argparse的使用可看:
Python, argparse, and command line arguments(推荐)
python之parser.add_argument()用法——命令行选项、参数和子命令解析器
首先以参数形式设置输入图像和模板的位置,这里参数指定了默认值,因此需要将 required=True 改成 required=False,否则会报错(借鉴:解决报错:设置了默认参数,仍报错:error: the following arguments are required:)
# argparse 模块是 Python 内置的一个用于命令项选项与参数解析的模块,
# argparse 模块可以让人轻松编写用户友好的命令行接口。
# 通过在程序中定义好我们需要的参数,然后 argparse 将会从 sys.argv 解析出这些参数。
# argparse 模块还会自动生成帮助和使用手册,并在用户给程序传入无效参数时报出错误信息。
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image",default='./images/credit_card_03.png', required=False,
help="path to input image")
ap.add_argument("-t", "--template",default='./images/ocr_a_reference.png', required=False,
help="path to template OCR-A image")
args = vars(ap.parse_args())
模板处理方法
转二值图像
cv2.threshold()方法可看:CV2简单阈值函数:cv2.threshold()
# 绘图展示
def cv_show(name,img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 读取一个模板图像(因为BGR,所以每个像素都是[255 255 255])
img = cv2.imread(args["template"])
cv_show('img',img)
# 转换为灰度图(BGR 到 GRAY)
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv_show('ref',ref)
# 二值图像(选项为cv2.THRESH_BINARY_INV)
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]
cv_show('ref',ref)
- 灰度图:在RGB模型中,如果R=G=B时,则彩色表示一种灰度颜色,其中R=G=B的值叫灰度值,因此,灰度图像每个像素只需一个字节存放灰度值(又称强度值、亮度值),灰度范围为0-255。一般常用的是加权平均法来获取每个像素点的灰度值。
- 二值图:图像的二值图,就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的只有黑和白的视觉效果。
- 彩色图象:多光谱图象的一种特殊情况,对应于人类视觉的三基色即红、绿、蓝三个波段,是对人眼的光谱量化性质的近似。
可以看到灰度图和二值图占空间较小,是彩色图象(BGR)的三分之一
单步调试(方法可看 :传送门):


原图:

二值图像如下:

计算轮廓
之后计算这10个数的轮廓,因为这10个轮廓的排列顺序并不一定是按照这个0-9的轮廓对应着来的,我们需要根据每个轮廓左上角的坐标值,先从小到大排序,这样就可以保证在匹配时顺序是对应的(比如 “354” 对应的的确是template中第4,6,5个数字)
# 计算轮廓
#cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图),cv2.RETR_EXTERNAL只检测外轮廓,cv2.CHAIN_APPROX_SIMPLE只保留终点坐标
#返回的list中每个元素都是图像中的一个轮廓
# ref_, refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# -1表示绘制所有轮廓,后面是画笔颜色,画笔大小
# refCnts返回绘制了轮廓的图像
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 = {
}
排序前refCnts:

排序后refCnts:

排序函数sort_contours:
boundingBoxes有4个返回值(x,y,h,w),其中凭借 x 即可判断出数字的前后位置关系
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
结果:

之后对应每个数字,resize成合适大小,放入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
digits数组最后结果:

输入数据处理方法
初始化卷积核,图像预处理
首先初始化卷积核,读取输入图像,预处理
# 初始化卷积核
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)
rectKernel:

sqKernel:

图像:

调整大小,转灰度图后:

顶帽操作
一些形态学操作可看
图像形态学操作之顶帽操作(TopHat)与黑帽操作(BlackHat)
【OPENCV3.3+PYTHON3.6】图像形态学操作之顶帽,黑帽以及形态学梯度
#顶帽操作,突出更明亮的区域
#根据自己指定的核大小进行顶帽操作
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
cv_show('tophat',tophat)
结果:

Sobel算子进行高通滤波
一些形态学操作可看:Sobel算子
# ksize:是Sobel算子的大小,即卷积核的大小,必须为奇数,默认值为3(ksize=-1相当于用3*3的)
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, 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次闭操作(先膨胀,再腐蚀)将数字连在一起,中间可以用THRESH_OTSU进行二值化处理,其优点在于可以自动寻找合适的阈值
#通过闭操作(先膨胀,再腐蚀)将数字连在一起
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)
第1次闭操作:

二值化处理:

第2次闭操作:

计算轮廓
# 计算轮廓
# thresh_, threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
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)
结果:

过滤轮廓
之后过滤轮廓,即通过计算所有轮廓外接矩形的大小,找到有价值的数字区域,把这4个区域放入locs列表进行排序
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])
locs列表内容:

模板匹配得出识别结果
模板匹配
先把轮廓稍微放大,之后遍历每一个轮廓中的数字,和上面的方法类似,找到每个数字后,在模板中计算每一个得分,得到最合适的数字并画出来:
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)
# 计算每一组的轮廓
# group_,digitCnts,hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
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)
部分结果:




output列表:

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

边栏推荐
- How much help does solid state disk have for game operation
- [leetcode daily question] - 268. Missing numbers
- MYSQL 命令大全
- 广州地铁十三号线二期全线土建已完成53%,预计明年开通
- 食品制造企业想要实现智能协同的供应商管理,选择SRM供应商系统就够了
- 关于工控网关物联网串口转WiFi模块与串口转网口模块的选型
- 【LeetCode】33、 搜索旋转排序数组
- 固态硬盘对游戏运行的帮助有多少
- Chuhuan technology is listed on Shenzhen Stock Exchange: Minsheng securities, with a market value of 2.7 billion, is a shareholder
- Qt开发高级进阶:如何在显示时适合视窗宽度和高度(fitWidth+fitHeight)
猜你喜欢

How to search literature on nature?

driver开发环境

晋拓股份上交所上市:市值26亿 张东家族企业色彩浓厚
![[basic] the difference between dynamic link library and static link library](/img/d5/fe7880e3fa91faff10a1c31870cce0.png)
[basic] the difference between dynamic link library and static link library

Unity URP入门实战

How much help does solid state disk have for game operation

如何查找国内各大学本科学位论文?

带你熟悉云网络的“电话簿”:DNS

Prometheus adds redis and MySQL node monitoring

数商云:引领化工业态数字升级,看摩贝如何快速打通全场景互融互通
随机推荐
装备制造业的变革时代,SCM供应链管理系统如何赋能装备制造企业转型升级
Yifang biological fell 16% on the first day of listing: the company's market value was 8.8 billion, and Hillhouse and Lilly were shareholders
R语言使用lm函数构建带交互项的多元回归模型、使用step函数构建逐步回归模型筛选预测变量的最佳子集(step regression)
In the changing era of equipment manufacturing industry, how can SCM supply chain management system enable equipment manufacturing enterprises to transform and upgrade
Soft test (VII) performance test (1) brief introduction
driver开发环境
What are the skills and methods of searching foreign literature
[leetcode daily question] - 268. Missing numbers
The practice of software R & D should start from the design
Data preprocessing of data mining
Xiaobai, which securities firm is the best and safest to open an account
固态硬盘对游戏运行的帮助有多少
SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame
Use of oscilloscope
怎样在nature上查文献?
Data permissions should be designed like this, yyyds!
R language ggplot2 visualization: visual line graph, visual line graph for different groups using the group parameter in AES function
R language wilcox The test function compares whether there is a significant difference in the central position of the population of two nonparametric samples (if the two sample data are paired data, s
楚环科技深交所上市:市值27亿 民生证券是股东
How to find undergraduate dissertations of domestic universities?