前言:信用卡卡號識別技術的發展有利於提高銀行系統的業務水平和辦事效率。相信此次通過學習使用OpenCV中的圖像處理方法來實現信用卡卡號識別的項目,能讓大家清楚地了解圖像處理技術的一般方法與步驟以及如何使用OpenCV庫。
1、設計思路
不同銀行發行的信用卡,其卡號中的數字外觀形狀是有點區別的,由於小編是通過模板匹配的方法完成信用卡卡號識別的,既然是模板匹配,則必須有一套與信用卡中外形一模一樣的數字模板,通過比對信用卡中的數字和模板中數字的差別來確定識別結果,模板圖像和信用卡圖像分布如圖1和圖2所示。
圖1
圖2
在進行模板匹配之前,必須通過圖像處理方法,比如形態學等,先從信用卡圖像中找到本次項目的感興趣域——即卡號所在區域,並且將該區域分割出來,然後提取出該區域中的數字分別與模板中的10個數字進行比對,認為數字與模板中匹配得分最高的數字相同。
2、代碼實現
項目實現包括main.py和myutils.py兩部分代碼,要想觀察實驗結果運行main.py就行,myutils.py中主要包含輪廓排序方法和resize方法,main.py中會調用myutils.py模塊中的函數。
main.py文件中的代碼如下:
# 導入工具包
from imutils import contours
import numpy as np
import cv2
import myutils
# 指定信用卡類型
FIRST_NUMBER = {
"3": "American Express",
"4": "Visa",
"5": "MasterCard",
"6": "Discover Card"
}
# 繪圖函數
def cv_show(name, img1):
cv2.imshow(name, img1)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 讀取模板
img = cv2.imread("template.png")
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.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#print(np.size(refCnts))返回值refcnts返回的是10組輪廓及其每個輪廓所有組成點的坐標
cv2.drawContours(img, refCnts, -1, (0, 0, 255), 3)
cv_show('img', img)
print(np.array(refCnts).shape)
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))
digits[i] = roi
# 初始化卷積核
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 預處理
image = cv2.imread('object.png')
cv_show('image', image)
image = myutils.resize(image, width=300)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv_show('gray', gray)
# 禮貌操作,突出高亮
tophat = cv2.morphologyEx(gray, 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 = cv2.threshold(gradx, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show('thresh', thresh)
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)
cv_show('thresh', thresh)
# 計算輪廓
threshCnts, hierarchy = 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])
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 = contours.sort_contours(digitCnts, method="left-to-right")[0]
# 計算每一組中的每一個數值
for c in digitCnts:
(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(image, "".join(groupOutput), (gx, gy - 15), cv2.FONT_HERSHEY_SCRIPT_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)
myutils.py中的代碼如下:
import cv2
def sort_contours(cnts, method="left-to-right"):
reverse = False
i = 0
if method == "right-to-left" or method == "bottom-to-top":
reverse = True
if method == "top-to-bottom" or method == "bottom-to-top":
i = 1
boundingBoxes = [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
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 image
if 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
代碼中出現的template.png就是圖1,object.png就是圖2。信用卡卡號識別項目的最終識別結果如圖3和圖4所示。
圖3
圖4