前言
在学完OpenCV中对图像的已经基本操作之后,例如彩色空间变换、阈值处理、图像梯度、轮廓检测、最小矩形匹配以及模板匹配。我们肯定非常急切地想去做一些事情,这里的信用卡卡号识别便是基于这些知识来做的!
正文
一.任务说明
在生活中,我们经常会遇到一些需要识别的地方,比如说在道路上的车牌识别、指纹识别、人脸识别等等,在不同的场景中所需要识别的内容也就不同。
在生活中的某一场景中(模拟),我们需要对银行卡卡号进行识别,来减轻我们工作的强度,需要我们设计算法,实现银行卡卡号的识别。
二.算法设计
1.数字的模板获取
- 这里我们的识别算法是根据模板匹配来实现的,在进行处理之前,需要准备与要识别信用卡数字风格差别不大的数字模板;模板的准备就不详细说明了,这也不是重点,模板数字图片如下。
- 对于准备的模板图片,还要将其中的每一个数字抠出来,保存在一个字典中,便于之后进行模板匹配。
def get_template(path):
img = cv2.imread(path) #读取数字模板
img = cv2.resize(img,(400,64)) #resize到合适的大小
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #灰度处理
_,gray = cv2.threshold(gray,100,255,cv2.THRESH_BINARY_INV) #阈值处理
cout,_ = cv2.findContours(gray,cv2.CV_8UC1,cv2.RETR_CCOMP)
#寻找轮廓
point = []
for i in cout:
x, y, w, h = cv2.boundingRect(i)
point.append((x,y,w,h))
#将每一个数字对应的坐标保存起来
point = sorted(point,key=lambda x:x[0],reverse=False)#对坐标进行排序,寻找轮廓时,顺序不是0-9的
digit = {}
for i,(x,y,w,h) in enumerate(point):
digit[i] = cv2.resize(gray[y-2:y+h+2,x-2:x+w+2],(48,64)) #得到每个数字的图像
return digit #返回数字模板
2.信用卡卡号提取
- 对图片进行一些形态学操作
1 构造适合的卷积核
2 灰度处理
3 礼帽操作
#构造适合的卷积核
rectKernel=cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))
sqKernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#礼帽操作
gray = cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel)
- 求图形梯度
梯度操作,在这里非常重要,如果不进行梯度操作,信用卡上面的数字可能提取不全。
#求x方向的梯度
gradX = cv2.Sobel(gray,ddepth=cv2.CV_32F,dx=1,dy=0,ksize=1)
gradX = np.absolute(gradX)
#归一化
minVal,maxVal = np.min(gradX),np.max(gradX)
gradX = (255*((gradX-minVal)/(maxVal-minVal)))
gradX = gradX.astype("uint8")
- 重复一些形态学操作
1 闭操作
2 阈值处理
3 闭操作
gradX = cv2.morphologyEx(gradX,cv2.MORPH_CLOSE,rectKernel)
_,thresh = cv2.threshold(gradX,127,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)
thresh=cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,sqKernel,iterations=2)
- 然寻找轮廓
1 将匹配到的轮廓安装适合的太小进行过滤,只保存我们需要的轮廓,数字
2 然后排序,按照原始顺序进行排列
group = []
for i in cout:
x,y,w,h = cv2.boundingRect(i)
if(w/h)>3.2 and (w/h)<5:
if (cv2.contourArea(i))>650 and (cv2.contourArea(i))<1000:
group.append((x,y,w,h))
#排序
group = sorted(group,key=lambda x:x[0],reverse=False)
- 进行匹配
1 提取提取出来的信用卡数字,一一与模板进行匹配,以确定其数字是什么。
for x,y,w,h in group:
img = gray[y-5:y+h+5,x-5:x+w+5]
_, thresh = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
cout, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
tt = []
for i in cout:
x,y,w,h = cv2.boundingRect(i)
tt.append((x,y,w,h))
tt = sorted(tt,key=lambda x:x[0],reverse=False)
for x,y,w,h in tt:
dst = img[y-2:y+h+2,x-2:x+w+2]
dst = cv2.resize(dst,(48,64))
sortes = []
for j,temp in enumerate(digit.values()):
result = cv2.matchTemplate(dst,temp,cv2.TM_CCOEFF)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
sortes.append(max_val)
output.append(str(np.argmax(sortes)))
- 最后是画图操作
for i in range(4):
put = ""
for j in range(4):
put +=output[i*4+j]
cv2.putText(img,put,(dist[i][0],dist[i][1]-10),cv2.FONT_HERSHEY_SIMPLEX,0.8,(0,0,255),2)
三 总结
经过这个小项目的实践,让我更加熟悉了一些对图像处理的基本操作,也让我知道了这些基本操作组合起来的威力也是非常大的,也是可以处理实践问题的。
完整代码如下
import cv2
import numpy as np
"""
1. 模板
2. 二值化
3. 开闭操作
4. 轮廓匹配
5. 模板匹配
6. 顶帽
"""
#获取数字模板
def get_template(path):
img = cv2.imread(path)
img = cv2.resize(img,(400,64))
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
_,gray = cv2.threshold(gray,100,255,cv2.THRESH_BINARY_INV)
cout,_ = cv2.findContours(gray,cv2.CV_8UC1,cv2.RETR_CCOMP)
point = []
for i in cout:
x, y, w, h = cv2.boundingRect(i)
point.append((x,y,w,h))
point = sorted(point,key=lambda x:x[0],reverse=False)
digit = {}
for i,(x,y,w,h) in enumerate(point):
digit[i] = cv2.resize(gray[y-2:y+h+2,x-2:x+w+2],(48,64))
return digit
def get_digit_area(img,digit):
#构造卷积核
rectKernel=cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))
sqKernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#礼帽操作
gray = cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel)
cv2.imshow('a',gray)
#求x方向的梯度
gradX = cv2.Sobel(gray,ddepth=cv2.CV_32F,dx=1,dy=0,ksize=1)
gradX = np.absolute(gradX)
#归一化
minVal,maxVal = np.min(gradX),np.max(gradX)
gradX = (255*((gradX-minVal)/(maxVal-minVal)))
gradX = gradX.astype("uint8")
#闭操作
cv2.imshow("gra",gradX)
cv2.waitKey(0)
gradX = cv2.morphologyEx(gradX,cv2.MORPH_CLOSE,rectKernel)
_,thresh = cv2.threshold(gradX,127,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)
thresh=cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,sqKernel,iterations=2)
#寻找轮廓
cout,_ = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
group = []
for i in cout:
x,y,w,h = cv2.boundingRect(i)
if(w/h)>3.2 and (w/h)<5:
if (cv2.contourArea(i))>650 and (cv2.contourArea(i))<1000:
group.append((x,y,w,h))
#排序
group = sorted(group,key=lambda x:x[0],reverse=False)
output = []
for x,y,w,h in group:
img = gray[y-5:y+h+5,x-5:x+w+5]
_, thresh = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
cout, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
tt = []
for i in cout:
x,y,w,h = cv2.boundingRect(i)
tt.append((x,y,w,h))
tt = sorted(tt,key=lambda x:x[0],reverse=False)
for x,y,w,h in tt:
dst = img[y-2:y+h+2,x-2:x+w+2]
dst = cv2.resize(dst,(48,64))
sortes = []
for j,temp in enumerate(digit.values()):
result = cv2.matchTemplate(dst,temp,cv2.TM_CCOEFF)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
sortes.append(max_val)
output.append(str(np.argmax(sortes)))
return group,output
def main():
img = cv2.imread("credit_card_02.png")
img = cv2.resize(img,(400,250))
digit = get_template("template.png")
dist,output = get_digit_area(img,digit)
for i in range(4):
put = ""
for j in range(4):
put +=output[i*4+j]
cv2.putText(img,put,(dist[i][0],dist[i][1]-10),cv2.FONT_HERSHEY_SIMPLEX,0.8,(0,0,255),2)
print(output)
cv2.imshow("A",img)
cv2.waitKey(0)
if __name__ == '__main__':
main()
来源:CSDN
作者:奋斗小鹏
链接:https://blog.csdn.net/weixin_44678052/article/details/104076451