目录
项目Introduce:
项目名称:
通过导入模板数字,对银行卡面上的数字进行识别,提取出银行卡面上的银行卡号。
项目流程预览:
1.处理模板图像,获取模板数字
2.导入银行卡图像
3.提取银行卡上的ROI
4.将模板数字与ROI二者的二值图像进行模板匹配
5.将匹配结果展示在银行卡上
项目与知识衔接:
图像预处理、边缘检测、模板匹配......
具体操作步骤以及代码:
1.导入工具包(库)
from imutils import contours
import numpy as np
import argparse #用于添加参数
import imutils #依赖numpy
import cv2
import myutils #自设定的一个文件
myutils代码:
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
2.调参
有两个参数需要处理(-i : 需要识别的图像、-t :模板)
#设置参数
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':'Viso',
'5':'MasterCard',
'6':'Discover Card'
}
#自定义创建窗口函数
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
参数调完后需要输入形参数据,就是你想进行匹配的模板和待检测图像,Pycharm中可以进行如下操作:
1.右键文件点击Modify Run Configuration(修改运行配置)
2.找到Parameters(形参数据)
输入如下:(自行修改其中数据,第一个路径为待检测的银行卡图片路径,不含中字且将\改为/,第二个为模板数字图片的路径)
-i D:/works/pyton_pic/credit_card.png -t D:/works/pyton_pic/ocr_a_reference.png
点击Apply-OK
3.导入模板并处理
(1)读入模板图像
#读入模板图像
img = cv2.imread(args['template'])
cv_show('img',img)
(2)模板图像转换为灰度图
#灰度处理
ref = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv_show('ref',ref)
(3)灰度图转换为二值图
#二值处理
ref = cv2. threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1]
#[1]代表取元组的第一位展示(本来应该是ref,TRESHOLD_BINARY_INV双参)
cv_show('ref',ref)
(4)找到所有数字的外轮廓
#轮廓检测
refCnts,hierarcy = cv2.findContours(ref.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
#cv2.RETR_EXTERMAL是只检测外轮廓
#cv2.CHAIN_APPROX_SIMPLE 是保留坐标点
(5)将轮廓画出
#画出轮廓
cv2.drawContours(img,refCnts,-1,(0,0,255),3)
cv_show('img',img)
(6)对找到的轮廓排序,得到轮廓集合
#轮廓排序
refCnts = myutils.sort_contours(refCnts,method='left-to=right')[0]
(7)将每个数字制成一个模板
#定义一个空字典
digits = {}
#遍历轮廓
for (i,c) in enumerate(refCnts):
(x,y,w,h) = cv2.boundingRect(c)
roi = ref[y:y+h,x:x+w]
roi = cv2.resize(roi,(57,88))
digits[i] = roi
cv_show('roi',roi)
4.导入银行卡图像并处理
(1)初始化卷积核
cv2.getStructingElement 是cv2中定义核的一个函数,MORPH_CROSS代表卷积核的形状是矩形,还有交叉形MORPH_ELLIPSE,(9,3)和(5,5)是卷积核的大小,默认(-1,-1)为中心点。
#初始化卷积核
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
(2)读取银行卡图像
#读取图像
image = cv2.imread(args['image'])
cv_show('image',image)
(3)银行卡图像转为灰度图
#调整图像的尺寸、并进行图像灰度化
image = myutils.resize(image,width=300)
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv_show('gray',gray)
(4)灰度图进行礼帽操作
#礼帽操作,使用(9,3)大小的卷积核
tophat = cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel)
cv_show('tophat',tophat)
(5)利用Sobel算子进行边缘检测
#计算横向梯度 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')#把图像从CV_32F转化为uint8
cv_show('gradX',gradX)
(6)Sobel算子运算后的图像进行闭操作
#闭运算
gradX = cv2.morphologyEx(gradX,cv2.MORPH_CLOSE,rectKernel)
cv_show('gradX',gradX)
(7)闭操作后的图像进行二值化(threshold)
#二值处理
thresh = cv2.threshold(gradX,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]
#cv2.cv2.THRESH_OTSU为自动判断合适值代替0
cv_show('thresh',thresh)
(8)二值化后的图像再次进行闭操作
#闭运算 目的是把区域中的黑色填充成白色
thresh = cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,sqKernel)
cv_show('thresh-close',thresh)
(9)找出并画出轮廓,对轮廓进行筛选得出ROI(根据数字区域的W/H;以及图像大小范围)
#计算轮廓
#轮廓检测
threshCnts,hierarcy = cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
#cv2.RETR_EXTERMAL是只检测外轮廓
#cv2.CHAIN_APPROX_SIMPLE 是保留坐标点
#画出轮廓
cur_img = image.copy()
cv2.drawContours(cur_img,threshCnts,-1,(0,0,255),3)
# cv_show('cur_img',cur_img)
筛选:
#过滤轮廓
locs = []
for(i,c) in enumerate(threshCnts):
(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))
print(locs)
遍历四个轮廓,找出外界轮廓位置图像:
#轮廓排序
locs = sorted (locs,key = lambda x:x[0])
#对轮廓中的内容进行轮廓检测
output = []
for (i,(gX,gY,gW,gH)) in enumerate(locs):
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)
(10)对ROI进行排序,提取ROI中的每一位数字进行模板匹配,筛选出最符合的数字
对ROI进行排序:
#检测轮廓并且排序
digitCnts,hierarchy = cv2.findContours(group.copy(),
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
digitCnts = imutils.contours.sort_contours(digitCnts,method = 'left-to-right')[0]
#提取轮廓矩形获得ROI
for c in digitCnts:
(x,y,w,h) = cv2.boundingRect(c)
roi = group[y:y+h,x:x+w]
roi = cv2.resize(roi,(57,99))
cv_show('roi',roi)
进行模板匹配获得的数值,取每10个中最大的数值,加入groupOutput:
#对ROI进行模板匹配
scores = []
for (digit,digitROI) in digits.items():
result = cv2.matchTemplate(roi,digitROI,cv2.TM_CCOEFF)
(_,score, _, _) = cv2.minMaxLoc(result)
scores.append(score)
print(scores)
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)
cv_show('image',image)
5.展示结果
将模板匹配获得的索引放入output,然后根据output[0]判断银行卡类型,最后显示结果
output.extend(groupOutput)
# print(output)
print(FIRST_NUMBER[output[0]])
print(output)
cv_show('image',image)
实现结果展示:
代码整体展示:
#导入工具包
from imutils import contours
import numpy as np
import argparse
import imutils
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':'Viso',
'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] #[1]代表取元组的第一位展示(本来应该是ref,TRESHOLD_BINARY_INV双参)
cv_show('ref',ref)
#轮廓检测
refCnts,hierarcy = cv2.findContours(ref.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
#cv2.RETR_EXTERMAL是只检测外轮廓
#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):
(x,y,w,h) = cv2.boundingRect(c)
roi = ref[y:y+h,x:x+w]
roi = cv2.resize(roi,(57,88))
digits[i] = roi
cv_show('roi',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)
#礼帽操作,使用(9,3)大小的卷积核
tophat = cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel)
cv_show('tophat',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')#把图像从CV_32F转化为uint8
cv_show('gradX',gradX)
#闭运算
gradX = cv2.morphologyEx(gradX,cv2.MORPH_CLOSE,rectKernel)
cv_show('gradX',gradX)
#二值处理
thresh = cv2.threshold(gradX,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]
#cv2.cv2.THRESH_OTSU为自动判断合适值代替0
cv_show('thresh',thresh)
#闭运算 目的是把区域中的黑色填充成白色
thresh = cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,sqKernel)
cv_show('thresh-close',thresh)
#计算轮廓
#轮廓检测
threshCnts,hierarcy = cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
#cv2.RETR_EXTERMAL是只检测外轮廓
#cv2.CHAIN_APPROX_SIMPLE 是保留坐标点
#画出轮廓
cur_img = image.copy()
cv2.drawContours(cur_img,threshCnts,-1,(0,0,255),3)
cv_show('cur_img',cur_img)
#过滤轮廓
locs = []
for(i,c) in enumerate(threshCnts):
(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))
# print(locs)
#轮廓排序
locs = sorted (locs,key = lambda x:x[0])
#对轮廓中的内容进行轮廓检测
output = []
for (i,(gX,gY,gW,gH)) in enumerate(locs):
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 = imutils.contours.sort_contours(digitCnts,method = 'left-to-right')[0]
#提取轮廓矩形获得ROI
for c in digitCnts:
(x,y,w,h) = cv2.boundingRect(c)
roi = group[y:y+h,x:x+w]
roi = cv2.resize(roi,(57,99))
cv_show('roi',roi)
#对ROI进行模板匹配
scores = []
for (digit,digitROI) in digits.items():
result = cv2.matchTemplate(roi,digitROI,cv2.TM_CCOEFF)
(_,score, _, _) = cv2.minMaxLoc(result)
scores.append(score)
print(scores)
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)
cv_show('image',image)
output.extend(groupOutput)
# print(output)
print(FIRST_NUMBER[output[0]])
print(output)
cv_show('image',image)