引言
在現代金融科技應用中,銀行卡號的自動識別是一項重要技術。本文將詳細介紹如何使用Python和OpenCV庫構建一個完整的銀行卡號識別系統。該系統能夠從銀行卡圖像中提取卡號信息,并根據卡號首數字判斷銀行卡類型。
技術棧
- ?OpenCV: 計算機視覺庫,用于圖像處理和特征提取
- ?NumPy: 科學計算庫,用于數組操作和數值計算
- ?argparse: 命令行參數解析庫
- ?自定義工具函數: 用于輪廓處理和圖像調整
創造函數
這里為了優化主函數的簡潔性,我們另寫了一個py文件,然后在主函數內導入。
這里寫了一個排序函數,是我們對我們在圖像中得到的輪廓進行從左到右排。
import cv2def 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](cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),key=lambda b: b[1][i],reverse=reverse))return cnts, boundingBoxes
還一個resize函數,這個和最初的resize不一樣,這個變大變小是等比變化的。
def 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
系統架構
主要思路:
? ? ? ? 先對模板圖像(包含數字的圖像)進行灰度圖處理,二值化。然后找出全部輪廓(這里輪廓是亂序的,我們用我們寫的函數,對順序進行整理),找出輪廓的的外接矩形。然后對輪廓進行裁剪,然后把用字典把圖像和對應數字保存下來。
? ? ? ? 再對我們要檢測的圖像進行處理。灰度圖,二值化,然后進行圖像形態學處理(把我們的數字變的更明顯,然后把背景給去除),找出我們的所在位置。然后裁剪出來,然后對每一個圖像再進行每一個數字的定位。然后再對比,找出最大值的對應的索引,然后再保存就ok了。
代碼詳解
1 模板處理
image=cv2.imread(args["template"])
cv_show("image", image)
ref=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv_show("ref", ref)
先進行二值化處理,不過我們的的圖像原本就是二值化的,所以沒啥區別
這里進行了一次圖像轉化,因為輪廓檢測對白色檢測。原本是黑色的。
ref=cv2.threshold(ref,127,255,cv2.THRESH_BINARY_INV)[1]
cv_show("ref", ref)
refCount,_=cv2.findContours(ref,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(image,refCount,-1,(0,255,0),3)
cv_show("ref", image)
refCnts=myfun.sort_contours(refCount,method='left-to-right')[0]
把每個輪廓畫出,并進行排序。
digits={}
for (i, cnt) in enumerate(refCnts):(x, y, w, h) = cv2.boundingRect(cnt)roi = ref[y:y+h, x:x+w]roi=cv2.resize(roi,(57,88))cv2.imshow("ROI", roi)digits[i] = roi
print(digits)
然后把每個數字的輪廓找外界矩形,然后保存下來
裁剪成這樣,然后再保存下來。
2 圖片處理
傳入圖片進行展示
進行二值化處理
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv_show("gray", gray)
定義核
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))
squareKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
這里比較大,效果好。
進行頂帽處理
????????頂帽操作是原圖像與開運算之間的差。開運算是先腐蝕后膨脹,頂帽操作用于突出比周圍亮的區域,常用于增強圖像中的細小亮細節或去除不均勻光照。如下圖,我們用原圖減去我們進行開運算的圖,就可以看到我們數字的地方消失了,只剩背景了,減去之后就剛好只剩數字了。
tophat=cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel)
cv_show("tophat", tophat)
進行腐蝕
closeX=cv2.morphologyEx(gray,cv2.MORPH_CLOSE,rectKernel)
cv_show("closeX", closeX)
這樣我們的數字就更好選取一點了,然后這里四個一組可以直接選取了,注意這里我們要觀察數字的特征,以便于我們對輪廓更好選取。
進行二值處理
thresh=cv2.threshold(closeX,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show("thresh", thresh)
畫出輪廓
threshCnts,h=cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts=threshCnts
cur_img=img.copy()
cv2.drawContours(cur_img,cnts,-1,(0,255,0),3)
對數字部分排序
cv_show("cur_img", cur_img)
locs=[]
for (i, cnt) in enumerate(cnts):(x, y, w, h) = cv2.boundingRect(cnt)ar=w/float(h)if ar>2.5 and ar<4.0:if (w>40 and h<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):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)# 計算每一組的輪廓digitCnts, hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)digitCnts = myfun.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(img, (gX - 5, gY - 5), (gX + gW + 5, gY + gH + 5), (0, 0, 255), 1)cv2.putText(img, "".join(groupOutput), (gX, gY - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)output.extend(groupOutput) # 得到結果,將一個列表的元素添加到另一個列表的末尾。
結果展示
結論
本文介紹的銀行卡號識別系統展示了傳統計算機視覺技術在金融科技中的應用。通過合理的圖像預處理、特征提取和模板匹配,實現了較高的識別準確率。這種技術不僅適用于銀行卡識別,還可推廣到其他卡證識別場景,具有廣泛的應用前景。