這段代碼用 OpenCV 做了一份“數字模板字典”,然后在銀行卡/身份證照片里自動找到身份證號那一行,把每個數字切出來跟模板比對,最終輸出并高亮顯示出完整的身份證號碼,下面是代碼解釋:
模塊 1 工具箱(通用函數)
目的:
cv_show
:調試時彈窗查看中間圖像sort_contours
:按指定順序(左→右、上→下等)排列輪廓,避免 OpenCV 隨機順序
代碼
def cv_show(name, image):cv2.imshow(name, image)cv2.waitKey(0)def sort_contours(cnts, method='left-to-right'):reverse = Falsei = 0if method in ('right-to-left', 'bottom-to-top'):reverse = Trueif method in ('top-to-bottom', '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
注意
返回值為元組,后續用
[0]
取排序后的輪廓若數字多行,可將
method
改為'top-to-bottom'
模塊 2 模板制作(生成 0-9 標準模板)
目的
從干凈模板圖中切出單個數字 → 統一尺寸(57×88)→ 白底黑字,供后續模板匹配
步驟
讀圖
img = cv2.imread("picture/TP.png") gray = cv2.imread("picture/TP.png", 0)
二值化(數字變白)
ref = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY_INV)[1]
找輪廓并排序
_, refCnts, _ = cv2.findContours(ref, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) refCnts = 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-2:y+h+2, x-2:x+w+2]roi = cv2.resize(roi, (57, 88))roi = cv2.bitwise_not(roi) # 白底黑字digits[i] = roi
注意
模板圖需無粘連、無干擾
若含小數點/空格,需額外過濾以保證
digits
長度為 10
模塊 3 輸入圖預處理(定位身份證號區域)
目的
在整幅銀行卡/身份證中,僅保留“身份證號”水平條帶,減少誤檢
步驟
讀圖
img = cv2.imread('picture/card_id.jpg') gray = cv2.imread('picture/card_id.jpg', 0)
二值化
ref = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY_INV)[1]
找輪廓
_, refCnts, _ = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
幾何過濾(針對當前圖寫死坐標)
注意
330/360/220 為經驗值,換圖需調整
光照不均時建議自適應閾值或亮度歸一化
模塊 4 單字符切割 + 模板匹配識別
目的
將單行 ROI 切成單個字符,與模板庫 0-9 匹配,得分最高者即為識別結果,并繪制邊框與文字
步驟
遍歷每個候選矩形
output = [] for (i, (gX, gY, gW, gH)) in enumerate(locs):group = gray[gY-2:gY+gH+2, gX-2:gX+gW+2]group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]roi = cv2.resize(group, (57, 88))
模板匹配打分
scores = [] for (digit, digitROI) in digits.items():result = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF)(_, score, _, _) = cv2.minMaxLoc(result)scores.append(score) jieguo = str(np.argmax(scores)) output.append(jieguo)
繪制結果
cv2.rectangle(imgg, (gX-5, gY-5), (gX+gW+5, gY+gH+5), (0, 0, 255), 1) cv2.putText(imgg, jieguo, (gX, gY-15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
打印整串號碼
print("Card ID #: {}".format("".join(output))) cv2.imshow("Image", imgg) cv2.waitKey(0) cv2.destroyAllWindows()
注意
若印刷為黑底白字,需再次
bitwise_not
僅支持數字 0-9;含字母/X 需擴展模板或改用 CNN
連體數字需先投影分割再識別