1 介绍
从一个银行卡中识别其中的银行卡号
2 流程m
读入模板,灰度图,二值化
使用模板匹配方式对模板,以及输入图像进行轮廓检测(检测外轮廓)
得到当前轮廓的外接矩形。
将模板中的外接矩形切割出来。
使用矩形的长宽比之间的差异使得信用卡的数字矩形框能够被选择出来。
将其进一步细分,与需要识别的信用卡当中的外接矩形resize成同样的大小。
使用for循环依次检测。
模板 :
二值化 :
轮廓检测:
cont, hier = cv2.findContours(model1.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
函数说明:
cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图),cv2.RETR_EXTERNAL只检测外轮廓,cv2.CHAIN_APPROX_SIMPLE只保留终点坐标, 返回的list中每个元素都是图像中的一个轮廓。
接着,输入图像(银行卡),灰度图,二值化(轮廓检测的输入均为二值图像),预处理(
用cv2.THRESH_BINARY_INV进行二值化感觉效果好一点
ret, img2 = cv2.threshold(img_gray, 100, 255, cv2.THRESH_BINARY_INV)
,SoelXY滤波,闭操作),闭操作是为了得到具体的矩形,轮廓检测(只检测外轮廓),轮廓近似(外接矩形近似,然后根据长宽比滤除不合适的对象。长宽比例不属于数字规范的,滤除掉,剩下的即为包含数字的模块)。
原图:
只进行二值化
进行了边缘检测(XY)
1次闭操作
再进行一次二值化
# THRESH_OTSU会自动寻找合适的阀值,适合双峰,需要把阀值参数设置为0
thresh = cv2.threshold(close, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
计算轮廓并绘制轮廓
函数:
cv2.drawContours
第一个参数是指明在哪幅图像上绘制轮廓;image为三通道才能显示轮廓
第二个参数是轮廓本身,在Python中是一个list;
第三个参数指定绘制轮廓list中的哪条轮廓,如果是-1,则绘制其中的所有轮廓。后面的参数很简单。其中thickness表明轮廓线的宽度,如果是-1(cv2.FILLED),则为填充模式。
threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = threshCnts
cur_img = img.copy()
cv2.drawContours(cur_img, cnts, -1, (0, 0, 255), 2)
防止原图被‘破坏’,cur_img = img.copy()
筛选匹配
最终结果
源代码
import cv2
import matplotlib.pyplot as plt
import numpy as np
import myutils
import argparse
import imutils.contours
def cv(img, name):
cv2.startWindowThread()
cv2.imshow('name', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# cv(img,img)
# img = cv2.imread('D:\\bank1.jpg', 0)
# ret, img1 = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY_INV)
# cv(img1, 'img1')
# model处理
model = cv2.imread('D:\\images\\model.jpg')
# 灰度图
model_gray = cv2.cvtColor(model, cv2.COLOR_BGR2GRAY)
cv(model, 's')
# 二值图像
ret, model1 = cv2.threshold(model_gray, 10, 255, cv2.THRESH_BINARY_INV)
cv(model1, 's')
cont, hier = cv2.findContours(model1.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # 只检测外轮廓。保留坐标点
# 绘制轮廓
cv2.drawContours(model, cont, -1, (0, 0, 255), 3)
cv(model, 'model')
print(np.array(cont, dtype=object).shape)
# 对轮廓进行排序,只考虑外轮廓。左上顶点的坐标值
cont = imutils.contours.sort_contours(cont, method="left-to-right")[0]
digits = {}
# 遍历每一个轮廓
# print(model.shape)
for (i, c) in enumerate(cont):
# 计算外接矩形并且resize成合适大小
(x, y, w, h) = cv2.boundingRect(c)
roi = model1[y:y + h, x:x + w]
roi = cv2.resize(roi, (57, 88))
# 每一个数字对应一个模板。
digits[i] = roi
# cv(digits[0], 'l')
# cv(digits[1], 'k')
# 初始化卷积核
rec = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sq = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 读入输入图像,预处理
img = cv2.imread('D:\\images\\bank6.jpg')
cv(img, 'img')
img = imutils.resize(img, width=300)# imutils中resize,我们一般就传入两个参数,一个需要修改大小的图像,一个weight或者是height,是根据的长宽比一起修改的
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv(img_gray, 'k')
# 礼帽操作,突出更明亮的区域
# img2 = cv2.morphologyEx(img_gray, cv2.MORPH_TOPHAT, rec)
# cv(tophat, 'tophat')
ret, img2 = cv2.threshold(img_gray, 100, 255, cv2.THRESH_BINARY_INV)
cv(img2, 'k')
# # 边缘检测
gradx = cv2.Sobel(img2, cv2.CV_32F, dx=1, dy=0, ksize=3)
gradx = cv2.convertScaleAbs(gradx)
grady = cv2.Sobel(img2, cv2.CV_64F, 0, 1, ksize=-1)
grady = cv2.convertScaleAbs(grady)
gradxy = cv2.addWeighted(gradx, 0.5, grady, 0.5, 0)
cv(gradxy, 'gradxy')
# (minVal, maxVal) = (np.min(gradxy), np.max(gradxy))
# gradxy = (255 * ((gradxy - minVal) / (maxVal - minVal))) # 归一化操作
# gradxy = gradxy.astype('uint8')
#
# print(np.array(gradxy).shape)
# cv(gradxy, 'gradxy')
#
close = cv2.morphologyEx(gradxy, cv2.MORPH_CLOSE, rec)
cv(close, 'close')
# THRESH_OTSU会自动寻找合适的阀值,适合双峰,需要把阀值参数设置为0
thresh = cv2.threshold(close, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv(thresh, 'thresh')
# 再次进行闭操作
# close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sq)
# cv(close, 'close')
# 计算轮廓
threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# threshCnts = imutils.contours.sort_contours(threshCnts, method="left-to-right")[0] # 排序,从左到右,从上到下
cnts = threshCnts
cur_img = img.copy()
cv2.drawContours(cur_img, cnts, -1, (0, 0, 255), 2)
cv(cur_img, 'img')
locs = []
# 遍历轮廓
for (i, c) in enumerate(cnts):
# 计算矩形
(x, y, w, h) = cv2.boundingRect(c)
ar = w / float(h)
# 选择合适的区域,根据实际任务来,这里的基本都是四个数字一组
if ar > 0 and ar < 5:
if (w > 40 and w < 55) and (h > 10 and h < 25):
# 符合的留下来
locs.append((x, y, w, h))
# 将符合的轮廓从左到右排序
locs = sorted(locs, key=lambda x: x[0])
output = []
# 遍历每一个轮廓中的数字
for (i, (gx, gy, gw, gh)) in enumerate(locs):
# initialize the list of group digits
groupOutput = []
# 根据坐标提取每一个组
group = img_gray[gy - 5:gy + gh + 5, gx - 5:gx + gw + 5]
cv(group, 'group')
# 预处理
group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]# 二值化
cv(group, 'group')
# 计算每一组的轮廓
digitCnts, hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
digitCnts = imutils.contours.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(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)
# 打印结果
print("Credit Card Type: {}".format([output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Img", img)
cv2.waitKey(0)