E:\python\opencv\bankCard OCR.py
对模版图片的处理
1,转为灰度图
tem_gray=cv.cvtColor(digitalTemplate,cv.COLOR_BGR2GRAY)

2,转为二值图,方便查找轮廓,要求前景为白色,背景为黑色的
tem_binary=cv.threshold(tem_gray,10,255,cv.THRESH_BINARY_INV)[1]

3,查找轮廓
contours,_=cv.findContours(tem_binary.copy(),cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)

4,将轮廓的外接矩阵进行排序
根据外接矩阵的x坐标将矩阵进行从左到右的排序
def sort_contours(contours, 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=[cv.boundingRect(c) for c in contours]
(contours,boundingBoxes)=zip(*sorted(zip(contours,boundingBoxes),
key=lambda b:b[1][i],reverse=reverse))
return contours,boundingBoxes
contours,boundingBoxes=sort_contours(contours,method='Left-to-right')

5,构建字典用于存储数字与各个数字图像的对应关系
#构建字典用于存储数字与图像的对应
digits={}
#遍历每一个轮廓
for (i,b) in enumerate(boundingBoxes):
#获取外接矩形并且resize成合适大小
(x,y,w,h)=b
roi=tem_binary[y:y+h,x:x+w]
roi=cv.resize(roi,(57,88))
digits[i]=roi

对银行卡的处理
1,转为灰度图
bankCard_gray=cv.cvtColor(bankCard,cv.COLOR_BGR2GRAY)

2,礼帽操作,突出明亮的部分
bankCard_tophat=cv.morphologyEx(bankCard_gray,cv.MORPH_TOPHAT,rectKernel)

3,通过sobel算子找到图像的边缘,分别在x轴和y轴进行梯度计算,最后系数加和
#sobel算子,找到边缘
gradX=cv.Sobel(bankCard_tophat,ddepth=cv.CV_32F,dx=1,dy=0,ksize=-1)
#对比一下直接缩放再绝对值的操作
# gradX1=cv.convertScaleAbs(gradX)
# cv_show(gradX1)
gradX=np.absolute(gradX)
(minmal,maxmal)=(np.min(gradX),np.max(gradX))
gradX=(255*((gradX-minmal)/(maxmal-minmal)))
gradX=gradX.astype("uint8")
# print(np.array(gradX).shape)
# cv_show(gradX)
gradY=cv.Sobel(bankCard_tophat,ddepth=cv.CV_32F,dx=0,dy=1,ksize=-1)
gradY=np.absolute(gradY)
(minmal,maxmal)=(np.min(gradY),np.max(gradY))
gradY=(255*((gradY-minmal)/(maxmal-minmal)))
gradY=gradY.astype("uint8")
# print(np.array(gradY).shape)
# cv_show(gradY)
gradXY=cv.addWeighted(gradX,0.5,gradY,0.5,0)

4,通过膨胀和闭操作(先膨胀,再腐蚀)将数字粘连到一起
gradXY=cv.dilate(gradXY,rectKernel)

gradXY=cv.morphologyEx(gradXY,cv.MORPH_CLOSE,rectKernel)

5,将图像转为二值图,方便寻找轮廓
thresh=cv.threshold(gradXY,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)[1]

6,寻找轮廓
threshcontours,_=cv.findContours(thresh.copy(),cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
contours=threshcontours

7,挑选出银行卡所在位置的轮廓的外接矩阵
locs=[]
for (i,c) in enumerate(contours):
(x,y,w,h)=cv.boundingRect(c)
ar=w/float(h)
if y/bankCard.shape[1]>0.32 and y/bankCard.shape[1]<0.35:
locs.append((x,y,w,h))
locs=sorted(locs,key=lambda x:x[0])

8,对每个数字块进行处理:

1.将数字块区域转为二值图

2.进行一个小小的膨胀,确保每个数字都是一个整体,核也不能太大,防止粘连到其他数字
3.寻找数字块中每个数字的轮廓并排序
4.获取每个数字轮廓的外接矩阵并排序
5.将数字矩阵与0--9数字模板进行匹配,得到数字矩阵与每个数字的相似得分,根据得分输出对应的数字
output=[]
for (i,(gx,gy,gw,gh)) in enumerate(locs):
groupOutput=[]
group1=bankCard_gray[gy-5:gy+gh+5,gx-5:gx+gw+5]
cv_show(group1)
# print(gy/bankCard.shape[1])
group=cv.threshold(group1,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)[1]
cv_show(group)
group_dilate=cv.dilate(group,kernel=np.ones((3,3),np.uint8))
# cv_show(group_dilate)
digitscnts,_=cv.findContours(group_dilate,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
# imgcnts=cv.drawContours(group1,digitscnts,-1,(0,0,0),1)
# cv_show(imgcnts)
# print(len(digitscnts))
digitscnts=sort_contours(digitscnts,method="Left-to-right")[0]
for c in digitscnts:
(x,y,w,h)=cv.boundingRect(c)
# cv.rectangle(group,(x,y),(x+w,y+h),(255,255,255),1)
# cv_show(group)
roi=group[y:y+h,x:x+w]
roi=cv.resize(roi,(57,88))
# cv_show(roi)
scores=[]
for (digit,digitROi) in digits.items():
result=cv.matchTemplate(roi,digitROi,cv.TM_CCOEFF_NORMED)
(_,score,_,_)=cv.minMaxLoc(result)
scores.append(score)
groupOutput.append(str(np.argmax(scores)))
cv.rectangle(bankCard,(gx-5,gy-5),(gx+gw+5,gy+gh+5),(0,0,255),1)
cv.putText(bankCard,"".join(groupOutput),(gx,gy-15),cv.FONT_HERSHEY_SIMPLEX,0.65,(0,0,255),2)
output.extend(groupOutput)
cv_show(bankCard)

完整代码
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.pyplot import gray
def cv_show(img,imgname='res'):
cv.imshow(imgname,img)
cv.waitKey(0)
cv.destroyAllWindows()
templatesrc='./data/digital template.png'
datasrc='./data/bankCard/bankCard2.png'
digitalTemplate=cv.imread(templatesrc)
bankCard=cv.imread(datasrc)
# cv_show('digitaltemplate',digitalTemplate)
# cv_show('bankCard',bankCard)
#灰度图
tem_gray=cv.cvtColor(digitalTemplate,cv.COLOR_BGR2GRAY)
# cv_show(tem_gray)
#二值图像
tem_binary=cv.threshold(tem_gray,10,255,cv.THRESH_BINARY_INV)[1]
# cv_show(tem_binary)
#计算轮廓
contours,_=cv.findContours(tem_binary.copy(),cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
# cv.drawContours(digitalTemplate,contours,-1,(0,0,255),3)
# cv_show(digitalTemplate)
# print(len(contours))
#排序,从左到右,从上到下
def sort_contours(contours, 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=[cv.boundingRect(c) for c in contours]
(contours,boundingBoxes)=zip(*sorted(zip(contours,boundingBoxes),
key=lambda b:b[1][i],reverse=reverse))
return contours,boundingBoxes
contours,boundingBoxes=sort_contours(contours,method='Left-to-right')
#验证
# for b in boundingBoxes:
# cv.rectangle(digitalTemplate,(b[0],b[1]),(b[0]+b[2],b[1]+b[3]),(0, 0, 255), 3)
# cv_show(digitalTemplate)
#构建字典用于存储数字与图像的对应
digits={}
#遍历每一个轮廓
for (i,b) in enumerate(boundingBoxes):
#获取外接矩形并且resize成合适大小
(x,y,w,h)=b
roi=tem_binary[y:y+h,x:x+w]
roi=cv.resize(roi,(57,88))
digits[i]=roi
#查看分隔后的图片
# for i in range(len(boundingBoxes)):
# plt.subplot(2,5,i+1),plt.imshow(digits[i])
# plt.title(f'{i}'),plt.xticks([]),plt.yticks([])
# plt.show()
# cv_show(digits[i])
#初始化卷积核
rectKernel=cv.getStructuringElement(cv.MORPH_RECT,(9,3))
sqKernel=cv.getStructuringElement(cv.MORPH_RECT,(5,5))
#获取输入图像,预处理
bankCard=cv.resize(bankCard,dsize=None,fx=0.8,fy=0.8)
# cv_show(bankCard)
#灰度图
bankCard_gray=cv.cvtColor(bankCard,cv.COLOR_BGR2GRAY)
# cv_show(bankCard_gray)
#礼帽操作,突出更明亮的区域
bankCard_tophat=cv.morphologyEx(bankCard_gray,cv.MORPH_TOPHAT,rectKernel)
# cv_show(bankCard_tophat)
#sobel算子,找到边缘
gradX=cv.Sobel(bankCard_tophat,ddepth=cv.CV_32F,dx=1,dy=0,ksize=-1)
#对比一下直接缩放再绝对值的操作
# gradX1=cv.convertScaleAbs(gradX)
# cv_show(gradX1)
gradX=np.absolute(gradX)
(minmal,maxmal)=(np.min(gradX),np.max(gradX))
gradX=(255*((gradX-minmal)/(maxmal-minmal)))
gradX=gradX.astype("uint8")
# print(np.array(gradX).shape)
# cv_show(gradX)
gradY=cv.Sobel(bankCard_tophat,ddepth=cv.CV_32F,dx=0,dy=1,ksize=-1)
gradY=np.absolute(gradY)
(minmal,maxmal)=(np.min(gradY),np.max(gradY))
gradY=(255*((gradY-minmal)/(maxmal-minmal)))
gradY=gradY.astype("uint8")
# print(np.array(gradY).shape)
# cv_show(gradY)
gradXY=cv.addWeighted(gradX,0.5,gradY,0.5,0)
# cv_show(gradXY)
#通过闭操作(先膨胀,再腐蚀)将数字连在一起
gradXY=cv.dilate(gradXY,rectKernel)
# cv_show(gradXY)
gradXY=cv.morphologyEx(gradXY,cv.MORPH_CLOSE,rectKernel)
# cv_show(gradXY)
thresh=cv.threshold(gradXY,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)[1]
# cv_show(thresh)
# thresh=cv.morphologyEx(thresh,cv.MORPH_CLOSE,sqKernel)
# cv_show(thresh)
threshcontours,_=cv.findContours(thresh.copy(),cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
contours=threshcontours
# cur_img=bankCard.copy()
# cv.drawContours(cur_img,contours,-1,(0,0,255),3)
# cv_show(cur_img)
locs=[]
# cur_img=bankCard.copy()
for (i,c) in enumerate(contours):
(x,y,w,h)=cv.boundingRect(c)
if y/bankCard.shape[1]>0.32 and y/bankCard.shape[1]<0.35:
locs.append((x,y,w,h))
# cv.rectangle(cur_img,(x,y),(x+w,y+h),(0,0,255),2)
# cv_show(cur_img)
locs=sorted(locs,key=lambda x:x[0])
output=[]
for (i,(gx,gy,gw,gh)) in enumerate(locs):
groupOutput=[]
group1=bankCard_gray[gy-5:gy+gh+5,gx-5:gx+gw+5]
cv_show(group1)
# print(gy/bankCard.shape[1])
group=cv.threshold(group1,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)[1]
cv_show(group)
group_dilate=cv.dilate(group,kernel=np.ones((3,3),np.uint8))
# cv_show(group_dilate)
digitscnts,_=cv.findContours(group_dilate,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
# imgcnts=cv.drawContours(group1,digitscnts,-1,(0,0,0),1)
# cv_show(imgcnts)
# print(len(digitscnts))
digitscnts=sort_contours(digitscnts,method="Left-to-right")[0]
for c in digitscnts:
(x,y,w,h)=cv.boundingRect(c)
# cv.rectangle(group,(x,y),(x+w,y+h),(255,255,255),1)
# cv_show(group)
roi=group[y:y+h,x:x+w]
roi=cv.resize(roi,(57,88))
# cv_show(roi)
scores=[]
for (digit,digitROi) in digits.items():
result=cv.matchTemplate(roi,digitROi,cv.TM_CCOEFF_NORMED)
(_,score,_,_)=cv.minMaxLoc(result)
scores.append(score)
groupOutput.append(str(np.argmax(scores)))
cv.rectangle(bankCard,(gx-5,gy-5),(gx+gw+5,gy+gh+5),(0,0,255),1)
cv.putText(bankCard,"".join(groupOutput),(gx,gy-15),cv.FONT_HERSHEY_SIMPLEX,0.65,(0,0,255),2)
output.extend(groupOutput)
cv_show(bankCard)