项目预览:
一. 找素材 (数字模板,银行卡照片等)
二. 识别数字则需要我们对数字模板进行处理。因此下面我们开始处理数字模板。
1.将数字模板处理成灰度图,再进行二值处理.这样图像就变成双通道图.
2.计算模板边缘轮廓.将数字模板每个数字的轮廓计算出来.
3.画出每个数字轮廓的外接矩形。最后将其分别保存到一个字典里.
三. 数字模板处理完成,已经洗净切块备用啦!现在让我们处理正餐-银行卡。
1.和模板一样先进行图像处理.灰度图 —— 二值处理
2.初始化一个(9,3)和(5,5)的卷积核放在旁边备用.
3.将已经处理的银行卡图像进行礼帽操作,去掉多余无用的部分.
4.运用Sobel算子,突出边界.
5.闭操作1,二值操作 , 接着闭操作2. 尽量让银行卡上轮廓中充满白色,利于我们下一步的边缘检测,使我们的轮廓检测的更准确.
6.计算轮廓。找出我们银行卡中数字部分呈现的大致比例,与我们计算的轮廓的长宽进行比较。符合条件我们就把它放到一个字典里。不需要则舍弃。
7.这时候我们的轮廓还是四个数字一组。接下来的步骤很明显啦。还是将这些轮廓预处理,接着按照每个数字大致的比例进行分别保存到字典里,进行轮廓检测。
8.将所有数字切成小块备用完成后,就可以将我们的数字轮廓与数字模板进行比较啦。先建一个数组,准备储存比对得分。接下来就是将这个数字轮廓与所有数字模板一个个比对。最后从所有的得分中选取得分最高的那个。
9.最后就是将我们每个数字轮廓的外界矩形画出来最后将数字放在它的头顶上。这样这道菜就做成啦!
下面上代码!!!!
想要看步骤展示的把每个cv_show前面的注释关掉运行就可以啦!
顺带一提,myutils是一个工具类哦。不是原有就有的包。
from imutils import contours
import numpy as np
import cv2
import imutils
import argparse
from cardnumber import myutils
# 设置参数
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 OCR-A image")
args = vars(ap.parse_args())
# 指定信用卡类型
FIRST_NUMBER = {
"3": "American Express",
"4": "Visa",
"5": "MasterCard",
"6": "Discovery Card"
}
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 读取一个模板图像
img = cv2.imread(args["template"])
# cv_show('img', img)
#灰度图
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# cv_show('img',ref)
#二值图像
ref = cv2.threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1]
# cv_show('ref',ref)
#轮廓检测
ref_, refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
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)
rol = ref[y:y + h, x:x + w]
rol = cv2.resize(rol, (57,88))
digits[i] = rol
#初始化卷积核
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
#读取,预处理
image = cv2.imread(args['image'])
#
image = myutils.resize(image, width=300)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# cv_show('img', gray)
#礼帽操作
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
# cv_show('image', tophat)
#Soble算子
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('grad', gradX)
#闭操作
gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
# cv_show('img',gradX)
#二值
thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# cv_show('thresh',thresh)
#闭操作*2
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)
# cv_show('img',thresh)
#计算轮廓
thresh_, threshCnds, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = threshCnds
cur_image = image.copy()
cv2.drawContours(cur_image, cnts, -1, (0,255))
# cv_show('img', cur_image)
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):
groupout = []
#根据坐标遍历每一个轮廓
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)
#计算每一组的轮廓
digit_thresh, digitCnts, hierarchy = cv2.findContours(group, 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)
#得到最适应的数字
groupout.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(groupout), (gX, gY - 15),
cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0,0,255), 2)
#得到结果
output.extend(groupout)
#展示结果
print("Credit card type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit card #:{}".format("".join(output)))
cv_show("result", image)
cv2.waitKey(0)