opencv进阶篇---银⾏卡数字识别
执⾏结果:
主要思想:对模板图像以及待检测图像进⾏外轮廓检测,并得到各⾃外接矩形,将模板图像的外接矩形做resize()操作,使其外接矩形的⼤⼩与待检测图像外接矩形的⼤⼩相⼀致,然后与待检测图形做模板匹配
准备⼯作:
1、转为为灰度图像
2、转化为⼆值图像,才能做轮廓检测
3、根据轮廓的长宽⽐例的不同,过滤掉⼀些银⾏卡上⽆⽤的⼲扰信息
4、上⾯的步骤仅能得到⼀些⼤致的轮廓,还需做⼀些形态学操作,然后对数字进⾏拆分,得到更为精确的数字信息
注意:在找模板时,应该找字体⼗分接近的字体作为模板
专项施工方案内容
(1)模板预处理过程:将模板中的每⼀个数字分别进⾏灰度转换、⼆值化、轮廓查找、轮廓绘制、resize()轮廓的⼤⼩并且将每个数字的轮廓值(排好序的)存⼊字典类型的变量中
模板图:
预处理之后的模板图:
桥吧英语
代码⽚段:
img=cv.imread("E:\OpenCVTests\Samples\sample/nine/template-matching-ocr\images\ocr_a_reference.png")
gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret,ref=cv.threshold(gray,10,255,cv.THRESH_BINARY_INV)#此步骤,应该加前⾯的ret,否则会报错
refCnts,hierarchy=cv.py(),cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
#后⾯还要继续使⽤ref,因此需使⽤py(),否则会对原图做出改变;第⼆个参数为指定检测外轮廓;第三个参数为轮廓逼近的⼀种⽅法
cv.drawContours(img,refCnts,-1,(0,0,255),3)#-1表⽰绘制所有轮廓,当指定为其他值时,只在图像中选择⼀个绘制单个轮廓
refCnts=contours.sort_contours(refCnts,method="left-to-right")[0]#返回排序完的轮廓
digits={}#建⽴⼀个字典类型,i是轮廓索引,c是轮廓----字典类型:每个索引号对应⼀个索引值
for(i,c)in enumerate(refCnts):#i是轮廓索引,c是对应轮廓,则完成了对检测出来的轮廓进⾏了排序
(x,y,w,h)=cv.boundingRect(c)#得到没⼀个外接矩形的左上坐标点以及长度、宽度
roi=ref[y:y+h,x:x+w]#每个数字的外接矩形的尺⼨
size(roi,(57,88))#重置外接矩形的尺⼨⾄合适⼤⼩
digits[i]=roi#每个数字对应⼀个模板
(2)待检测图像预处理过程:主要包括形态学操作去噪点、灰度转换、⼆值化、Sobel()函数求梯度、轮廓相关操作(轮廓的查找、轮廓的绘制、外接轮廓、根据轮廓的长宽⽐来对轮廓就⾏筛选、排序)再到遍历每⼀块中的每⼀个数字
待检测图像原图:
执⾏⼀次顶帽操作:
执⾏⼀次Sobel()求X⽅向上的梯度操作:求图中较为明亮的区域
快乐春天
执⾏⼀次闭操作:使图像上的内容成块出现
执⾏⼀次⼆值化操作:过滤掉杂乱信息
再执⾏⼀次闭操作:填补⽩⾊块中的⼩⿊块
执⾏轮廓检测、轮廓绘制:
后续再根据轮廓的长宽⽐对轮廓进⾏筛选,筛选出需要的轮廓信息,⼯寄检测出4组有⽤的轮廓信息,并循环遍历这四组轮廓中的每⼀个数字,⽅法与上⾯同。
完整代码:
import cv2 as cv
from imutils import contours
import matplotlib as plt
import numpy as np
FIRST_NUMBER = {
"3": "American Express",
"4": "Visa",
"5": "MasterCard",
"6": "Discover Card"
}
#对模板图像做预处理
img=cv.imread("E:\OpenCVTests\Samples\sample/nine/template-matching-ocr\images\ocr_a_reference.png")
gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret,ref=cv.threshold(gray,10,255,cv.THRESH_BINARY_INV)#此步骤,应该加前⾯的ret,否则会报错南烟斋
refCnts,hierarchy=cv.py(),cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)如律令
#后⾯还要继续使⽤ref,因此需使⽤py(),否则会对原图做出改变;第⼆个参数为指定检测外轮廓;第三个参数为轮廓逼近的⼀种⽅法
cv.drawContours(img,refCnts,-1,(0,0,255),3)#-1表⽰绘制所有轮廓,当指定为其他值时,只在图像中选择⼀个绘制单个轮廓
refCnts=contours.sort_contours(refCnts,method="left-to-right")[0]#返回排序完的轮廓
digits={}#建⽴⼀个字典类型,i是轮廓索引,c是轮廓----字典类型:每个索引号对应⼀个索引值
for(i,c)in enumerate(refCnts):#i是轮廓索引,c是对应轮廓,则完成了对检测出来的轮廓进⾏了排序
(x,y,w,h)=cv.boundingRect(c)#得到没⼀个外接矩形的左上坐标点以及长度、宽度
roi=ref[y:y+h,x:x+w]#每个数字的外接矩形的尺⼨
size(roi,(57,88))#重置外接矩形的尺⼨⾄合适⼤⼩
digits[i]=roi#每个数字对应⼀个模板
#对待检测图像做预处理
StructuringElement(cv.MORPH_RECT,(10,3))#为保证检测信息准确,需去除银⾏卡页⾯杂乱信
StructuringElement(cv.MORPH_RECT,(2,2))#因此需要对图像做形态学操作,故在此设⽴卷积核火锅底料的做法
image=cv.imread("E:\OpenCVTests\Samples\sample/nine/template-matching-ocr\images\credit_card_01.png")
size(image,(250,200))
gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)
phologyEx(gray,cv.MORPH_TOPHAT,recKernel)#根据字体的⼤⼩来选定合适的核;顶帽操作来突出明亮的区域
gradx=cv.Sobel(tophat,ddepth=cv.CV_32F,dx=1,dy=0,ksize=3)#对X还是对Y需要或者同时需要根据实际需要来设定,图像梯度
gradx=np.absolute(gradx)#取绝对值妻子用英语怎么说
(minVal,maxVal)=(np.min(gradx),np.max(gradx))#归⼀化
gradx=(255*((gradx-minVal)/(maxVal-minVal)))
gradx=gradx.astype("uint8")
phologyEx(gradx,cv.MORPH_CLOSE,recKernel)#执⾏闭操作,使图像上的内容成块出现
ret,thresh=cv.threshold(gradx,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)#低阈值之所以设为0,是因为后⾯的⽅法选⽤了OTSU⾃动设定阈值,适合双峰的图像操作
phologyEx(thresh,cv.MORPH_CLOSE,sqKernel)#本次闭操作是为了填补⼆值化图像中块中的不完整⼩块
Cnts,hierarchy=cv.py(),cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
cnts=Cnts
py()
cv.drawContours(curImage,cnts,-1,(0,0,255),3)#此处轮廓不是原图像的轮廓,⽽是经历了⼀些列运算之后的图像的轮廓
locs=[]
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))
locs=sorted(locs,key=lambda x:x[0])#经筛选之后的轮廓
output=[]
for (i,(gx,gy,gw,gh))in enumerate(locs):#遍历每⼀块中的每⼀个数字
groupOutput=[]
groupOutput=[]
group=gray[gy-5:gy+gh+5,gx-5:gx+gw+5]#取轮廓及其周围的区域
cv.imshow("group",group)
group=cv.threshold(group,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)[1]#后⾯的[]要加,否则会报错元组类型不能copy,下⾯再对每个块进⾏轮廓检测、绘制
digitCnts,hierarchy=cv.py(),cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)#每⼀个group再进⾏轮廓检测、绘制
digitCnts=contours.sort_contours(digitCnts,method="left-to-right")[0]
for c in digitCnts:#计算每⼀组数中的每⼀个数值
(x,y,w,h)=cv.boundingRect(c)
roi=group[y:y+h,x:x+w]
size(roi,(57,88))#尺⼨需与模板的尺⼨对应,得到每⼀个数字所在的区域
scores=[]#新建⼀个空列表,⽤来存储检测到的数字
for (digit,digitROI)in digits.items():#在模板预处理中建⽴了数值的字典类型,⼀个为索引、⼀个为值
result=cv.matchTemplate(roi,digitROI,cv.TM_CCOEFF)#匹配,返回与之匹配度最⾼的数值
(_,score,_,_)=cv.minMaxLoc(result)#做10次匹配,取最⼤值(注意:取最⼤值还是最⼩值跟选取的模板匹配⽅法有关)
scores.append(score)
groupOutput.append(str(np.argmax(scores)))
cv.putText(image,"".join(groupOutput),(gx,gy-15),cv.FONT_HERSHEY_SIMPLEX,0.65,(0,0,255),2)
生日快乐的歌
print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv.imshow("Image",image)
cv.waitKey(0)
cv.destroyAllWindows()