目錄
信用卡數字識別系統:前言與代碼解析
前言
項目代碼
??????結果演示
代碼模塊解析
1. 參數解析模塊
2. 輪廓排序函數
3. 圖像預處理模塊
4. 輸入圖像處理流程
5. 卡號區域定位
6. 數字識別與輸出
系統優勢
信用卡數字識別系統:前言與代碼解析
前言
信用卡數字識別是金融自動化處理的核心技術之一,通過計算機視覺技術自動提取卡面信息,可應用于支付驗證、身份認證等場景。本系統基于模板匹配和圖像處理技術,實現對信用卡卡號的自動識別。系統通過預處理、輪廓檢測和特征匹配三個關鍵階段,準確識別信用卡上的16位數字,并自動判斷發卡機構(Visa/MasterCard等)。以下將詳細解析代碼各模塊功能。
項目代碼
import argparse
import imutils
import numpy as np
import myutils
from imutils import contours
import cv2# 設置參數結果
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,help="path to input image containing credit card")
ap.add_argument("-t", "--template", required=True,help="path to input template image")
args = vars(ap.parse_args())# 指定信用卡類型
FIRST_NUMBER = {"3": "American Express","4": "Visa","5": "MasterCard","6": "Discover Card"
}def sort_contours(cnts, method="left-to-right"):reverse = Falsei = 0if method == "right-to-left" or method == "bottom-to-top":reverse = Trueif method == "top-to-bottom" or method == "bottom-to-top":i = 1boundingBoxes = [cv2.boundingRect(c) for c in cnts] # 用一個最小的矩形,把找到的形狀包起來x,y,h,w(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),key=lambda b: b[1][i], reverse=reverse))return cnts, boundingBoxesdef resize(image, width=None, height=None, inter=cv2.INTER_AREA):dim = None(h, w) = image.shape[:2]if width is None and height is None:return imageif width is None:r = height / float(h)dim = (int(w * r), height)else:r = width / float(w)dim = (width, int(h * r))resized = cv2.resize(image, dim, interpolation=inter)return resized# 繪圖展示
def cv_show(name,img):cv2.imshow(name,img)cv2.waitKey(0)cv2.destroyAllWindows()return None# 讀取一個模板
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)# 計算輪廓果
ref_,refCnts = cv2.findContours(ref.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,ref_,-1,(0,0,255),3)
cv_show("img",img)
print(np.array(refCnts).shape)
refCnts =sort_contours(ref_, 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))digits[i] = roi# 初始化卷積核
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))
squareKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))# 讀取輸入圖像、預處理
image = cv2.imread(args["image"])
cv_show("image",image)
image = resize(image,width=300)
gary= cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv_show("gary",gary)# 禮帽操作,突出更明亮的區域
tophat = cv2.morphologyEx(gary,cv2.MORPH_TOPHAT,rectKernel)
cv_show("tophat",tophat)
gradx=cv2.Sobel(tophat,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")
print(np.array(gradx).shape)
cv_show("gradx",gradx)# 通過閉操作(先膨脹,再腐蝕)將數字連在一起
gradx = cv2.morphologyEx(gradx,cv2.MORPH_CLOSE,rectKernel)
cv_show("gradx",gradx)
# THRESH_OTSU會自動尋找全局閾值,適合雙峰,需要把閾值參數設置為0
thresh = cv2.threshold(gradx,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show("thresh",thresh)# 再來一個閉操作
thresh = cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,squareKernel)
cv_show("thresh",thresh)# 計算輪廓
thresh_,threshCnts = cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = thresh_
cur_img=image.copy()
cv2.drawContours(cur_img,cnts,-1,(0,0,255),3)
cv_show("cur_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])
output=[]
# 遍歷每一個輪廓中的數字
for (i,(gx,gy,gw,gh)) in enumerate(locs):# initialzie the list of group digitsgroupOutput = []# 根據坐標提取數字的區域group = gary[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_,groupCnts = cv2.findContours(group.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)group_ = contours.sort_contours(group_,method="left-to-right")[0]# 計算每一個組中的數字for c in group_:(x,y,w,h) = cv2.boundingRect(c)digit = group[y:y+h,x:x+w]digit = cv2.resize(digit,(57,88))cv_show("digit",digit)# 計算匹配得分scores = []# 在模板中計算每一個得分for (digitt,digitTempl) in digits.items():result = cv2.matchTemplate(digit,digitTempl,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(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)
注意事項:
由于作者用于較新的PyCharm版本是2024版本,它與傳統的PyCharm有區分,而且作者在學習過程中同樣發現一些問題,這個代碼前面可以不用寫排序,而最新版本里面不知什么原因調用接口時候報錯說沒有這個模塊函數,于是作者只能將排序等需要的模塊分開出來,寫入項目中。
??????結果演示
代碼模塊解析
1. 參數解析模塊
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="輸入信用卡圖像路徑")
ap.add_argument("-t", "--template", required=True, help="模板圖像路徑")
args = vars(ap.parse_args())
- 功能:通過命令行參數接收輸入圖像和模板圖像路徑
- 參數說明:
-i/--image
:待識別的信用卡圖像-t/--template
:數字模板圖像(包含0-9標準數字)
PyCharm2024通過argparse模塊操作命令行設置方法如下,接下來的步驟是中文展示,作者PyCharm已經漢化,若未漢化也可記作者選擇按鈕的位置點擊:
第一步:將需要的圖片存放在本人知道的文件路徑
原圖片如下:
模板圖片如下:
我存放的路徑如下:
第二步:右擊項目代碼,選擇“更多運行/調試”后,在選擇下拉列表中的“修改運行配置”
第三步:在新打開的對話框選擇“運行”欄下最后一行點擊“展開”
第四步:輸入之前存放模板和原圖片的路徑
注:由于作者存放圖片和模板與項目同一個文件夾目錄下,因此可以省略具體的路徑,若用戶存放不在同一個文件夾目錄下,那么需要你加上如下格式:
--image
盤符:\文件夾1名稱\文件夾2名稱\.....\圖片名稱和后綴名(.png,.jpg等等)
--template
盤符:\文件夾1名稱\文件夾2名稱\.....\圖片名稱和后綴名(.png,.jpg等等)
2. 輪廓排序函數
def sort_contours(cnts, method="left-to-right"):reverse = Falsei = 0if method == "right-to-left" or method == "bottom-to-top":reverse = Trueif method == "top-to-bottom" or method == "bottom-to-top":i = 1boundingBoxes = [cv2.boundingRect(c) for c in cnts] # 用一個最小的矩形,把找到的形狀包起來x,y,h,w(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),key=lambda b: b[1][i], reverse=reverse))return cnts, boundingBoxes
- 功能:對檢測到的輪廓按空間位置排序
- 排序邏輯:
- 計算每個輪廓的邊界框
(x,y,w,h)
- 按
method
參數選擇排序基準(X軸或Y軸坐標) - 支持四種排序方向:左→右、右→左、上→下、下→上
- 計算每個輪廓的邊界框
3. 圖像預處理模塊
# 模板預處理流程
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度化
ref = cv2.threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1] # 二值化反轉
- 關鍵操作:
- 灰度轉換:將RGB圖像轉為單通道灰度圖
- 二值化:通過閾值處理突出數字區域
- 輪廓提取:
findContours
定位每個數字的獨立輪廓 - 模板存儲:將0-9數字按索引存入字典
digits
4. 輸入圖像處理流程
# 核心處理鏈
image = resize(image, width=300) # 尺寸標準化
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 灰度化
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) # 頂帽運算
gradx = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0) # Sobel邊緣檢測
gradx = cv2.morphologyEx(gradx, cv2.MORPH_CLOSE, rectKernel) # 閉運算
- 處理階段:
- 尺寸歸一化:固定寬度為300像素,保持比例
- 頂帽運算:突出亮色區域(信用卡數字通常為亮色)
- Sobel算子:檢測垂直邊緣(數字的豎直筆畫)
- 形態學閉操作:連接數字筆畫形成連續區域
5. 卡號區域定位
# 數字區域篩選
for (i,c) in enumerate(cnts):(x,y,w,h) = cv2.boundingRect(c)ar = w / float(h) # 寬高比if 2.5 < ar < 4.0 and 40<w<55 and 10<h<20:locs.append((x,y,w,h)) # 保存候選區域
- 篩選條件:
- 寬高比 $2.5 < \frac{w}{h} < 4.0$(信用卡數字的典型比例)
- 寬度 $40 < w < 55$ 像素
- 高度 $10 < h < 20$ 像素
- 結果:獲得4組數字區域的坐標
locs
6. 數字識別與輸出
# 模板匹配識別
for c in group_:digit = cv2.resize(roi, (57,88)) # 標準化尺寸scores = []for digitTempl in digits.values():score = cv2.matchTemplate(digit, digitTempl, cv2.TM_CCOEFF)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(image, "".join(groupOutput), (gx,gy-15), cv2.FONT_HERSHEY_SIMPLEX, ...)
# 得到結果
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)
- 識別流程:
- 截取單個數字區域并縮放至模板尺寸
- 與0-9模板進行相似度匹配(
TM_CCOEFF
相關系數法) - 選擇最高匹配得分對應的數字
- 輸出形式:
- 紅色矩形框標記數字組區域
- 在區域上方顯示識別出的4位數字
系統優勢
- 形態學操作鏈:通過頂帽、閉操作等組合優化數字區域提取
- 動態模板匹配:適應不同字體和尺寸的數字
- 空間約束:利用寬高比和尺寸過濾誤檢區域
- 實時可視化:各階段結果可實時顯示便于調試
該系統實現了從原始圖像到卡號識別的完整流程,準確率依賴模板質量和圖像清晰度,可通過優化預處理參數進一步提升性能。