【OpenCV】(十四)信用卡识别项目
系列内容:OpenCV概述与环境配置,OpenCV基础知识和绘制图形,图像的算数与位运算,图像视频的加载和显示,图像基本变换,滤波器,形态学,图像轮廓,图像直方图,车辆统计项目,特征检测和匹配,图像查找和拼接,虚拟计算器项目,信用卡识别项目,图像的分割与修复,人脸检测与车牌识别,目标追踪,答题卡识别判卷与文档ocr扫描识别,光流估计
(一)模板匹配
模板匹配和卷积原理很像,模板在原图像上从原点开始滑动,计算模板与(图像被模板覆盖的地方)的差别程度,这个差别程度的计算方法在opencv里有6种,然后将每次计算的结果放入一个矩阵里,作为结果输出。假如原图形是AxB大小,而模板是axb大小,则输出结果的矩阵是(A - a + 1)x(B - b + 1)
TM_SQDIFF:计算平方不同,计算出来的值越小,越相关
TM_CCORR:计算相关性,计算出来的值越大,越相关
TM_CCOEFF:计算相关系数,计算出来的值越大,越相关
TM_SQDIFF_NORMED:计算归一化平方不同,计算出来的值越接近0,越相关
TM_CCORR_NORMED:计算归一化相关性,计算出来的值越接近1,越相关
其中:
TM_CCOEFF_NORMED:计算归一化相关系数,计算出来的值越接近1,越相关
建议使用归一化的计算方法会相对公平一些.
(1)matchTemplate(image, templ, method[, result[, mask]]) 进行模板匹配:
- image是要匹配的图片
- templ是模板图片
- method是计算方式
- result是进行匹配计算后得到的矩阵.
- mask是掩膜
(2)minMaxLoc(src[, mask]) 获取最大值和最小值的位置:
返回四个值,分别是最小值,最大值,最小值坐标,最大值坐标
(a)模板匹配,示例代码:
import cv2
import numpy as np
img=cv2.imread('./lena1.png')
template=cv2.imread('./face.png')
print(img.shape)
print(template.shape)
res=cv2.matchTemplate(img,template,cv2.TM_SQDIFF)
print(res.shape)
min_val,max_val,min_loc,max_loc=cv2.minMaxLoc(res)
print(min_val)
print(max_val)
print(min_loc)
print(max_loc)
#画出匹配到的位置,注意,这里要交叉写,template的(206,161)对应(161,206)
img=cv2.rectangle(img,min_loc,(min_loc[0]+161,min_loc[1]+206),(0,0,255),3)
cv2.imshow('template',template)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果:
(512, 512, 3)
(206, 161, 3)
(307, 352)
0.0
724556672.0
(199, 184)
(298, 96)
lena的人脸模板匹配结果,如图14.1所示:
(b)六种匹配方式的不同,示例代码:
import cv2
import numpy as np
import matplotlib.pyplot as plt
img=cv2.imread('./lena1.png',0)
template=cv2.imread('./face.png',0)
#定义六种方法的列表
methods=['cv2.TM_SQDIFF','cv2.TM_CCORR','cv2.TM_CCOEFF','cv2.TM_SQDIFF_NORMED','cv2.TM_CCORR_NORMED',
'cv2.TM_CCOEFF_NORMED']
w,h=template.shape[1],template.shape[0]
for method in methods:
img2=img.copy()
#匹配
res=cv2.matchTemplate(img,template,eval(method))
min_val,max_val,min_loc,max_loc=cv2.minMaxLoc(res)
if method in[cv2.TM_SQDIFF,cv2.TM_SQDIFF_NORMED]:
top_left=min_loc
else:
top_left=max_loc
bottom_right=(top_left[0]+w,top_left[1]+h)
#画矩形
cv2.rectangle(img2,top_left,bottom_right,(0,0,255),2)
#画子图,都画在一起
plt.subplot(121),plt.imshow(res,cmap='gray')
plt.axis('off')
plt.subplot(122),plt.imshow(img2,cmap='gray')
plt.axis('off')
plt.suptitle(method)
plt.show()
六种匹配方式的不同,运行结果:
匹配结果1,如图14.2(1)所示:
匹配结果2,如图14.2(2)所示:
匹配结果3,如图14.2(3)所示:
匹配结果4,如图14.2(4)所示:
匹配结果5,如图14.2(5)所示:
匹配结果6,如图14.2(6)所示:
(二)匹配多个对象
import cv2
img = cv2.imread('./cd.jpg') # 读取原始图像
templ = cv2.imread('./cd_temp.png') # 读取模板图像
width, height, c = templ.shape # 获取模板图像的宽度、高度和通道数
results = cv2.matchTemplate(img, templ, cv2.TM_CCOEFF_NORMED) # 按照标准相关系数匹配
for y in range(len(results)): # 遍历结果数组的行
for x in range(len(results[y])): # 遍历结果数组的列
if results[y][x] > 0.99: # 如果相关系数大于0.99则认为匹配成功
# 在最佳匹配结果位置绘制红色方框
cv2.rectangle(img, (x, y), (x + width, y + height), (0, 0, 255), 2)
cv2.imshow("img", img) # 显示匹配的结果
cv2.waitKey()
cv2.destroyAllWindows()
cd(多物体)的原图,如图14.3(1)所示:
cd的模版,如图14.3(2)所示:
运行结果如图14.3所示:
(三)信用卡数字识别
(1)数字模版图片的处理
#先处理数字模版图片
import cv2
import numpy as np
#封装显示图片的函数
def cv_show(name,img):
cv2.imshow(name, img)
cv2.waitKey()
cv2.destroyAllWindows()
#读取模版图片
img=cv2.imread('./card_temp.png')
# print(img.shape)
# 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)
# cv_show('ref',ref)
#计算轮廓
ref_,ref_contours,_=cv2.findContours(ref.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
#画出外轮廓
cv2.drawContours(img,ref_contours,-1,(0,0,255),3)
cv_show('img',img)
#表示数字的轮廓
print(np.array(ref_contours,dtype='object').shape)
#对轮廓进行排序,按照数字大小进行排序,方便后面使用
#排序思路:根据每个数字的最大外接矩形的x轴坐标进行排序
#计算每个轮廓的外接矩形
bounding_boxes=[cv2.boundingRect(c) for c in ref_contours]
print(bounding_boxes)
print(sorted(bounding_boxes,key=lambda b:b[0]))
#要把排序之后的外接矩形和轮廓建立对应的关系
print(sorted(bounding_boxes,key=lambda b:b[1]))
(ref_contours,bounding_boxes)=zip(*sorted(zip(ref_contours,bounding_boxes),key=lambda b:b[1][0]))
digits={}
for (i,c) in enumerate(ref_contours):
#重新计算外接矩形
(x,y,w,h)=cv2.boundingRect(c)
#region of interest 感兴趣的区域
#取出每个数字
roi=ref[y:y+h,x:x+w]
#resize成合适的大小
roi=cv2.resize(roi,(57,88))
digits[i]=roi
print(digits)
数字模版图片的处理结果,如图14.4所示:
(2)数字图像图片的匹配
#对信用卡进行处理
image=cv2.imread('./card02.jpg')
# image=cv2.imread('./card.png')
# cv_show('image',image)
#对信用卡进行resize
#为了保证原图不拉伸,需要计算原图的长宽比
h,w=image.shape[:2]
width=300
r=width/w
image=cv2.resize(image,(300,int(h*r)))
# cv_show('image',image)
#灰度化处理
gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
# cv_show('gray',gray)
#接下来是形态学的各种操作
#顶帽操作,突出更明亮的区域
#初始化卷积核
rect_kernel=cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))
tophat=cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rect_kernel)
# cv_show('tophat',tophat)
#sobel算子
#只用x轴方向的梯度
grad_x=cv2.Sobel(tophat,ddepth=cv2.CV_32F,dx=1,dy=0,ksize=-1)
print(grad_x)
#对grad_x进行处理
grad_x=np.absolute(grad_x)
#再把grad_x变成0到255之间的整数
min_val,max_val=np.min(grad_x),np.max(grad_x)
grad_x=(grad_x-min_val)/(max_val-min_val)*255
#修改一下数据类型
grad_x=grad_x.astype('uint8')
# cv_show('grad_x',grad_x)
# grad_y=cv2.Sobel(tophat,ddepth=cv2.CV_32F,dx=0,dy=1,ksize=-1)
# print(grad_y)
# #对grad_y进行处理
# grad_y=np.absolute(grad_y)
# #再把grad_y变成0到255之间的整数
# min_val,max_val=np.min(grad_y),np.max(grad_y)
# grad_y=(grad_y-min_val)/(max_val-min_val)*255
# #修改一下数据类型
# grad_y=grad_y.astype('uint8')
# cv_show('grad_y',grad_y)
# cv_show('grad',grad_x+grad_y)
#闭操作,先膨胀,再腐蚀,可以把数字连在一起
cv2.morphologyEx(grad_x,cv2.MORPH_CLOSE,rect_kernel)
# cv_show('gradx',grad_x)
#通过大津(OTSU)算法找到合适的阈值,进行全局二值化操作
_,thresh=cv2.threshold(grad_x,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# cv_show('thresh',thresh)
#中间还有空洞,再来一个闭操作
sq_kernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
thresh=cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,sq_kernel)
# cv_show('thresh',thresh)
#找轮廓
thres_,thresh_contours,_=cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
#在原图上画轮廓
image_copy=image.copy()
cv2.drawContours(image_copy,thresh_contours,-1,(0,0,255),3)
cv_show('img',image_copy)
#遍历轮廓,计算外接矩形,然后根据实际信用卡数字区域的长宽比,找到真正的数字区域
locs=[]
output=[]
for c in thresh_contours:
#计算外接矩形
(x,y,w,h)=cv2.boundingRect(c)
#计算外接矩形的长宽比例
ar=w/float(h)
#选择合适的区域
print(ar)
if ar >2 and ar<3.5:
#根据实际的长宽做进一步的筛选
if(w>40 and w<55) and(h>10 and h<20):
#符合条件的外接矩形留下来
locs.append((x,y,w,h))
#对符合要求的轮廓进行从左到右的排序,
sorted(locs,key=lambda x:x[0])
#遍历每一个外接矩形,通过外接矩形把原图中的数字抠出来
for(i,(gx,gy,gw,gh))in enumerate(locs):
#抠出数字,并加余量
group=gray[gy-5:gy+gh+5,gx-5:gx+gw+5]
# cv_show('group',group)
#对取出的灰色group做全局二值化处理
group=cv2.threshold(group,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]
# cv_show('group',group)
#计算轮廓
_,digit_contours,_=cv2.findContours(group.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
#对轮廓进行排序
bounding_boxes=[cv2.boundingRect(c) for c in digit_contours]
(digit_contours,_)=zip(*sorted(zip(digit_contours,bounding_boxes),key=lambda b:b[1][0]))
#定义每一组匹配好的数字的存放列表
group_output=[]
#遍历排好序的轮廓
for c in digit_contours:
#找到当前数字的轮廓,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,digit_roi) in digits.items():
result=cv2.matchTemplate(roi,digit_roi,cv2.TM_CCOEFF)
#只要最大值,即分数
(_,score,_,_)=cv2.minMaxLoc(result)
scores.append(score)
#找到分数最高的数字,即我们匹配到的数字
group_output.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(group_output),(gx,gy-15),cv2.FONT_HERSHEY_SIMPLEX,0.6,(0,0,255),2)
output.extend(group_output)
cv_show('image',image)
数字图像图片的匹配结果(1),如图14.5(1)所示:
数字图像图片的匹配结果(2),如图14.5(2)所示:
注意:由于字体模板和选取银行卡字体模板不一致,导致匹配结果较差,如果选择轮廓清晰的字体模板且对应银行的字体的银行卡,效果应该不错。
更多推荐
所有评论(0)