代碼實現:
import cv2
import numpy as np
import argparse
import myutils
'''
-i moban.png
-t card1.png
'''
ap= argparse.ArgumentParser()
ap.add_argument("-i","--image", required=True,help="path to input image")
ap.add_argument("-t","--template", required=True,help="path to template 0cR-A image")
args = vars(ap.parse_args())
FIRST_NUMBER ={ "3":"American Express","4": "Visa","5":"MasterCard","6":"Discover Card"}
def cv_show(name,img):cv2.imshow(name,img)cv2.waitKey(0)''' --------模板圖像中數字的定位處理----------- '''
img = cv2.imread(args["template"])
cv_show('img',img)
ref =cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv_show('ref',ref)
ref = cv2.threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1]
cv_show('ref',ref)
_,refCnts, hierarchy = cv2.findContours(ref, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,refCnts,-1,(0,0,255),3)
cv_show('img',img)
refCnts = myutils.sort_contours(refCnts,method="left-to-right")[0]
digits = {}
for(i,c)in enumerate(refCnts):(x,y,w,h)=cv2.boundingRect(c)roi = ref[y:y + h, x:x + w]roi =cv2.resize(roi,(57,88))cv_show('ro',roi)digits[i]= roi
print(digits)
''' --------信用卡的圖像處理----------- '''
image = cv2.imread(args['image'])
cv_show('image',image)
image = myutils.resize(image,width=300)
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv_show('gray',gray)
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
tophat = cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel)
cv_show('tophat',tophat)
closeX = cv2.morphologyEx(tophat,cv2.MORPH_CLOSE,rectKernel)
cv_show('gradX',closeX)
thresh = cv2.threshold(closeX,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]
cv_show('thresh',thresh)
thresh = cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,sqKernel)
cv_show('thresh1',thresh)
_,threshCnts,h = cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = threshCnts
cur_img = image.copy()
cv2.drawContours(cur_img,cnts,-1,(0,0,255),3)
cv_show('img',cur_img)
locs = []
for (i,c) in enumerate(cnts):(x,y,w,h) = cv2.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])
print(locs)
output = []
# 遍歷每一個輪廓中的數字
for (i, (gX, gY, gW, gH)) in enumerate(locs):groupOutput = []group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5] # 適當加一點邊界cv_show('group', group)# 預處理group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]cv_show('group', group)# 計算每一組的輪廓group_, digitCnts, hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)digitCnts = myutils.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))cv_show('roi', roi)'''--------使用模板匹配,計算匹配得分-----------'''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(image, (gX - 5, gY - 5), (gX + gW + 5, gY + gH + 5), (0, 0, 255), 1)# cv2.putText()是OpenCV庫中的一個函數,用于在圖像上添加文本。cv2.putText(image, "".join(groupOutput), (gX, gY - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)output.extend(groupOutput) # 得到結果 將一個列表的元素添加到另一個列表的末尾。
# 打印結果
print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Image", image)
cv2.waitKey(0)
這段代碼實現了基于模板匹配的信用卡數字識別,核心流程分為「模板圖像預處理」「信用卡圖像預處理」「輪廓檢測與分組」「數字模板匹配與識別」四個階段,以下是詳細解析:
1. 初始化與參數解析
- 參數解析:通過?
argparse
?解析命令行參數,-i
?指定信用卡輸入圖像路徑,-t
?指定數字模板圖像(包含 0-9 數字的參考圖)。 - 信用卡品牌映射:
FIRST_NUMBER
?字典通過卡號首數字映射信用卡品牌(如 Visa 以 4 開頭)。 - 輔助函數:
cv_show
?用于調試時顯示圖像并等待按鍵。
2. 模板圖像預處理(制備數字模板)
目的是從模板圖像中提取?0-9 的標準數字模板,供后續匹配使用:
- 讀入與灰度轉換:讀取模板圖像并轉為灰度圖。
- 反相二值化:
cv2.threshold(..., THRESH_BINARY_INV)
?將數字轉為白色、背景轉為黑色,便于輪廓檢測。 - 輪廓檢測與排序:
cv2.findContours
?提取所有外部輪廓(每個數字對應一個輪廓)。myutils.sort_contours
(自定義函數)按從左到右排序輪廓,確保數字順序與視覺一致。
- 提取數字 ROI 并標準化:遍歷每個輪廓,提取數字區域(ROI),并縮放到統一尺寸?
(57, 88)
,存儲到?digits
?字典(鍵為索引,值為數字模板)。
3. 信用卡圖像預處理(增強數字區域)
目的是從信用卡圖像中突出數字區域,便于后續輪廓檢測:
- 讀入與尺寸調整:讀取信用卡圖像,按比例縮放到寬度 300(方便統一處理)。
- 灰度轉換:轉為灰度圖,消除顏色干擾。
- 形態學操作(增強數字):
- 頂帽操作(TOPHAT):用長方形結構核?
rectKernel
?突出比背景亮的區域(即數字區域)。 - 閉運算(CLOSE):先膨脹后腐蝕,用?
rectKernel
?連接水平方向的數字,使 “4 位一組的數字” 形成整體區域;再用正方形核?sqKernel
?連接數字內部間隙,讓輪廓更完整。
- 頂帽操作(TOPHAT):用長方形結構核?
- 二值化(OTSU 自適應閾值):
cv2.threshold(..., THRESH_BINARY | THRESH_OTSU)
?自動確定閾值,將圖像轉為黑白二值圖(數字為白,背景為黑)。
4. 輪廓篩選與數字分組
目的是從預處理后的圖像中,篩選出 ** 信用卡數字組(通常 4 位一組)** 的輪廓:
- 遍歷所有輪廓,通過寬高比(
ar
)和寬 / 高的數值范圍篩選符合 “數字組特征” 的輪廓(如信用卡數字組寬高比約 2.5-4,寬度 40-55,高度 10-20,需根據實際圖像調整)。 - 按輪廓的?x 坐標排序,確保從左到右處理數字組。
5. 數字模板匹配與識別
對每個篩選出的數字組,逐數字進行模板匹配:
- 提取數字組 ROI:擴展數字組的邊界(
gY -5
?等),避免數字被截斷。 - 數字組二值化:對提取的區域再次二值化,與模板格式一致。
- 單數字輪廓檢測與排序:提取數字組內的單個數字輪廓,按從左到右排序。
- 模板匹配:
- 對每個單數字 ROI,縮放到模板尺寸?
(57, 88)
。 - 用?
cv2.matchTemplate
(方法為?TM_CCOEFF
)與?digits
?中所有數字模板匹配,計算 “匹配得分”。 - 選擇得分最高的模板,其索引對應數字(如索引 0 對應模板中最左側數字)。
- 對每個單數字 ROI,縮放到模板尺寸?
- 結果繪制與存儲:在原圖上繪制數字組的矩形框,并將識別出的數字用?
cv2.putText
?顯示;同時將數字存入?output
?列表,最終拼接為完整卡號。
6. 信用卡品牌與卡號輸出
- 從?
output
?中取首數字,通過?FIRST_NUMBER
?字典判斷信用卡品牌。 - 打印品牌與完整卡號,并顯示最終圖像。
關鍵注意事項
- 參數適配:形態學核的尺寸、輪廓篩選的寬高比 / 范圍,需根據圖像分辨率、數字大小調整。
- 模板質量:模板圖像的清晰度、數字完整性,直接影響匹配準確率。
- 排序依賴:
myutils.sort_contours
(自定義輪廓排序函數)是確保 “數字順序正確” 的關鍵。 - 匹配方法:
TM_CCOEFF
?適合黑白模板匹配,因它對亮度差異不敏感,更關注形狀相關性。