https://mydreamambitious.blog.csdn.net/article/details/125249121
https://mydreamambitious.blog.csdn.net/article/details/125265838
https://mydreamambitious.blog.csdn.net/article/details/125392536
https://mydreamambitious.blog.csdn.net/article/details/125339604
https://mydreamambitious.blog.csdn.net/article/details/125315200
https://mydreamambitious.blog.csdn.net/article/details/125227338
https://mydreamambitious.blog.csdn.net/article/details/125298061
https://blog.csdn.net/weixin_42081389/article/details/87935735
a.首先要知道一张信用卡上面不仅仅是信用卡号,而且还有其他的标志和数字,所以为了提取信用卡上面的卡号,那么就需要将除卡号之外的都得从图像中去除或者通过设置阈值的方式排除掉(轮廓的周长和轮廓的面积)。
b.但是要找到图像中的数字和其他标志的轮廓,需要对查找轮廓的函数有所了解,而查找轮廓之前也需要对图像进行二值化处理和灰度处理,这是处理图像的基本方式。
c.信用卡号是四位一组,如果将这四位数画成一个轮廓,但是数字之间是有小空隙的,为了处理这种小空隙,使得这个轮廓成为一个全白(便于后面排除其他轮廓),那么就需要进行腐蚀和膨胀操作了,去除大图像中的小图像。
d.做完上面的大概之后基本上就可以从图像中通过设置阈值的方式,将数字的轮廓给提出来,提出数字的轮廓同时也得到了数字在应用卡上的坐标,通过坐标和轮廓的高宽就可以从灰度图中提取感兴趣区域(也就是数字的区域).
e.最后通过模版匹配的方式找到对应的数字并显示,而这里的模版以什么作为模版呢?就是给定义一张图,从0-9的数字,信用卡上的数字匹配对应与这个模版上的数字,所以在对信用卡处理之前需要对模版图像进行处理。大概的思路就是这样。
#按比例缩放图像
def ScalePic(img):
height,width,channel=img.shape
height=int(height*(300/width))
img=cv2.resize(img,dsize=(300,height))
return img
#读取模版图像
img=cv2.imread('ocr_a_reference.png')
#对模版图进行灰度处理和二值化处理
gray=cv2.cvtColor(imgSize,code=cv2.COLOR_BGR2GRAY)
ret,threshold=cv2.threshold(src=gray,thresh=10,maxval=255,type=cv2.THRESH_BINARY)
#查找模版中数字的轮廓和绘制轮廓
ret,contours_,hierarch=cv2.findContours(image=threshold,mode=cv2.RETR_EXTERNAL,method=cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(image=img,contours=contours_,contourIdx=-1,
color=(0,255,0),thickness=3)
#根据绘制的轮廓的坐标对其进行排序,得到0-9的数字,返回排序之后的每个数字的左上角坐标和
#绘制的矩形的长宽
contours_2=contours.sort_contours(contours_1,method='left-to-right')
#利用字典的形式存储数字对应的模版图像中的数字
dictRoi={}
for step,c in enumerate(contours_2):
(x,y,w,h)=cv2.boundingRect(c)
roi=threshold[y:y+h,x:x+w]
dictRoi[step]=roi
注意卷积核大小的选取。这个直接决定你输出的效果好不好。
#获取卷积核
kernel=cv2.getStructuringElement(shape=cv2.MORPH_RECT,ksize=(7,1))
#读取要检测的信用卡图片
imgCard=cv2.imread('images//credit_card_01.png')
#按长宽比缩放图片
imgCardSize=ScalePic(imgCard)
imgCardGray=cv2.cvtColor(src=imgCardSize,code=cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(imgCardGray, 0, 255,
cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
思考:为什么要进行顶帽操作?
顶帽=原图-开运算,其本身的作用就是得到大图像外的小图像;由于信用卡上的数字和标志都比较少,而如果我们直接进行开运算的话,那么导致最后图像上的数字和标志直接被腐蚀掉,但是又想要把图像中的其他小图(无用的小图)去除,那么首先进行开运算再使用原图减去开运算之后的图即可达到目的。
#进行顶帽操作
imgToPhat=cv2.morphologyEx(src=thresh,op=cv2.MORPH_TOPHAT,
kernel=kernel,iterations=1)
进行索贝尔算子的主要原因是因为我们的信用卡号是水平的,所以对X方向求梯度即可,结果输出的是一张二值化的图像。
imgCardGradX=cv2.Sobel(src=imgToPhat,ddepth=-1,dx=1,dy=0,ksize=-1)
#进行闭运算操作,主要是为了将数字之间的空隙连成一片
imgCardClosed=cv2.morphologyEx(src=imgCardGradX,op=cv2.MORPH_CLOSE,kernel=kernel,iterations=2)
#对信用卡号进行轮廓查找
ret_,contours_3,hierarchy=cv2.findContours(image=imgCardClosed,mode=cv2.RETR_EXTERNAL,
method=cv2.CHAIN_APPROX_SIMPLE)
#对信用卡号进行绘制轮廓
cv2.drawContours(image=imgCardSize,contours=contours_3,contourIdx=-1,
color=(0,255,0),thickness=3)
#筛选出卡号轮廓
LocateCard=[]
for step,c in enumerate(contours_3):
(x,y,w,h)=cv2.boundingRect(c)
#根据绘制的卡号轮廓,可以看到宽度与长度比有一定的规律
rate=w/float(h)
#进行筛选
if rate>2.5 and rate<4.0:
if (w>40 and w<55) and (h>10 and h<20):
LocateCard.append((x,y,w,h))
LocateCard=sorted(LocateCard,key=lambda x:x[0])
#遍历每个轮廓中的数字
for (step,(gX,gY,gW,gH)) in enumerate(LocateCard):
groupOutput=[]
#提取每一个卡号的轮廓
group=imgCardGray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5]
# 预处理
group = cv2.threshold(group, 0, 255,
cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# 计算每一组的轮廓
group_, 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:
# 找到当前数值的轮廓,缩放成和之前模版同样大小的图
(x, y, w, h) = cv2.boundingRect(c)
roi = group[y:y + h, x:x + w]
roi = cv2.resize(roi, (57, 88))
# 计算和模版匹配的相似度
scores = []
# 在模板中计算每一个得分
for (dictRoi, digitROI) in dictRoi.items():
# 模板匹配相似度
result = cv2.matchTemplate(roi, digitROI,
cv2.TM_CCOEFF)
(_, score, _, _) = cv2.minMaxLoc(result)
scores.append(score)
# 得到最合适的数字
groupOutput.append(str(np.argmax(scores)))
# 将结果绘制矩形框标出
cv2.rectangle(imgCardSize, (gX - 5, gY - 5),
(gX + gW + 5, gY + gH + 5), (0, 0, 255), 1)
cv2.putText(imgCardSize, "".join(groupOutput), (gX, gY - 15),
cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
# 最终的结果
Output.extend(groupOutput)
print("Credit Card #: {}".format("".join(Output)))
cv2.imshow("Image", imgCardSize)
cv2.waitKey(0)
cv2.destroyAllWindows()
import os
import cv2
import cvzone
import numpy as np
from imutils import contours
#按比例缩放图像
def ScalePic(img):
height,width,channel=img.shape
height=int(height*(300/width))
img=cv2.resize(img,dsize=(300,height))
return img
#读取模版图像
img=cv2.imread('ocr_a_reference.png')
#对模版图进行灰度处理和二值化处理
gray=cv2.cvtColor(img,code=cv2.COLOR_BGR2GRAY)
ret,threshold=cv2.threshold(src=gray,thresh=10,maxval=255,type=cv2.THRESH_BINARY_INV)
#查找模版中数字的轮廓和绘制轮廓
ret_1,contours_1,hierarch=cv2.findContours(image=threshold,mode=cv2.RETR_EXTERNAL,method=cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(image=img,contours=contours_1,contourIdx=-1,
color=(0,255,0),thickness=3)
#根据绘制的轮廓的坐标对其进行排序,得到0-9的数字,返回排序之后的每个数字的左上角坐标和
#绘制的矩形的长宽
contours_2,ret_2=contours.sort_contours(contours_1,method='left-to-right')
#利用字典的形式存储数字对应的模版图像中的数字
digits={}
for step,c in enumerate(contours_2):
(x,y,w,h)=cv2.boundingRect(c)
roi=threshold[y:y+h,x:x+w]
roi=cv2.resize(src=roi,dsize=(57,88))
digits[step]=roi
#获取卷积核
kernel=cv2.getStructuringElement(shape=cv2.MORPH_RECT,ksize=(7,1))
#读取要检测的信用卡图片
imgCard=cv2.imread('images//credit_card_05.png')
#按长宽比缩放图片
imgCardSize=ScalePic(imgCard)
cv2.imshow('img',imgCardSize)
imgCardGray=cv2.cvtColor(src=imgCardSize,code=cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(imgCardGray, 0, 255,
cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
#进行顶帽操作
imgToPhat=cv2.morphologyEx(src=thresh,op=cv2.MORPH_TOPHAT,
kernel=kernel,iterations=1)
#进行索贝尔算子
imgCardGradX=cv2.Sobel(src=imgToPhat,ddepth=-1,dx=1,dy=0,ksize=-1)
#进行闭运算操作,主要是为了将数字之间的空隙连成一片
imgCardClosed=cv2.morphologyEx(src=imgCardGradX,op=cv2.MORPH_CLOSE,kernel=kernel,iterations=2)
#对信用卡号进行轮廓查找
ret_,contours_3,hierarchy=cv2.findContours(image=imgCardClosed,mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE)
#对信用卡号进行绘制轮廓
cv2.drawContours(image=imgCardSize,contours=contours_3,contourIdx=-1,
color=(0,255,0),thickness=3)
#筛选出卡号轮廓
LocateCard=[]
for step,c in enumerate(contours_3):
(x,y,w,h)=cv2.boundingRect(c)
#根据绘制的卡号轮廓,可以看到宽度与长度比有一定的规律
rate=w/float(h)
#进行筛选
if rate>2.5 and rate<4.0:
if (w>40 and w<55) and (h>10 and h<20):
LocateCard.append((x,y,w,h))
LocateCard=sorted(LocateCard,key=lambda x:x[0])
#输出保存结果
Output=[]
#遍历每个轮廓中的数字
for (step,(gX,gY,gW,gH)) in enumerate(LocateCard):
groupOutput=[]
#提取每一个卡号的轮廓
group=imgCardGray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5]
# 预处理
group = cv2.threshold(group, 0, 255,
cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# 计算每一组的轮廓
group_, 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))
# 计算匹配得分
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(imgCardSize, (gX - 5, gY - 5),
(gX + gW + 5, gY + gH + 5), (0, 0, 255), 1)
cv2.putText(imgCardSize, "".join(groupOutput), (gX, gY - 15),
cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
# 得到结果
Output.extend(groupOutput)
# 打印结果
print("Credit Card #: {}".format("".join(Output)))
cv2.imshow("Image", imgCardSize)
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == '__main__':
print('Pycharm')