前言
在學完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()