人工智能人工智能银行卡数字识别_OpenCv
Nuyoah 银行卡数字识别
该程序主要是依靠模板匹配来进行数字识别的
模板图像
待匹配的银行卡
这里银行卡中的数字和我们模板中的数字是同一个类型
模板操作
首先导入我们所需要的包:
1 2 3 4
| from imutils import contours import numpy as np import argparse import cv2
|
配置我们所需要的参数:
1 2 3 4 5
| 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())
|
设置绘图函数
1 2 3 4 5
| def cv_show(name, img): cv2.imshow(name, img) cv2.waitKey(0) cv2.destroyAllWindows()
|
模板图像读取:
1 2 3
| img = cv2.imread(args["template"]) cv_show("img", img)
|
模板图像的处理
1 2 3 4 5 6 7
| 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)
|
灰度图
逆二值图:
计算模板图像轮廓并画出
1 2 3 4 5 6 7 8
|
refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, refCnts, -1, (0, 255, 0), 3) cv_show("img", img)
|
画出图像轮廓
模板排序
这里面一共有10个轮廓,每一个轮廓代表一个数字,但是轮廓的先后位置我么不确定 ,我们需要给轮廓排序之后才能够知道轮廓的先后顺序,排完序之后,0这个轮廓就在第一位,1这个轮廓就在第二位
zip()函数是将两个列表对应位置的元素打包在一起,并组合成一个大列表的操作
sorted()函数有几个参数要注意:
首先,sorted 可以对所有可迭代的对象进行排序操作,而不仅仅是列表
语法:sorted(iterable, cmp=None, key=None, reverse=False)
iterable – 可迭代对象。
cmp – 比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,此函数必须遵守的规则为,大于则返回 1,小于则返回 - 1,等于则返回 0。
key – 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
reverse – 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
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
refCnts = sort_contours(refCnts, method="left-to-right")[0]
|
模板对应
经过模板排序之后,我们可以使用字典来将每一个模板对应成为一个数字:
1 2 3 4 5 6 7 8 9 10 11 12
|
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
|
图像操作
初始化卷积核
初始化两个卷积核,一个用来识别模板,一个用来识别图像
1 2 3
| rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3)) sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
|
读取图像并进行预处理
1 2 3 4 5 6 7 8 9
| image = cv2.imread(args["image"]) cv_show("image", image)
image = myutils.resize(image, width=300)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) cv_show("GRAY", gray)
|
原始图像
灰度图
礼帽操作
经过灰度处理之后我们发现,这张卡片中的一些背景的干扰信息变暗了许多,这时候我们可以使用礼帽操作,突出更加明亮的地方
1 2 3
| tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) cv_show("tophat", tophat)
|
计算梯度
我们是用Sobel算子来计算梯度,凸出边缘
目的:Sobel算子主要用于边缘检测,对噪声平滑抑制。
原理:图像梯度用于边缘检测。边缘是像素值发生跃迁的地方,是图像的显著特征之一。图像中有灰度值的变化就会有梯度,从而产生边缘,在边缘处,具有变化的强弱及方向。图像上可以使用一阶差分来计算相邻像素之间的变化率,我们利用卷积和特定的算子来计算相邻像素的变化率。sobel算子可以计算相邻三个点之间的变化率。它们用于一阶算子的边缘检测,利用像素点上下、左右相邻点的灰度差求取边缘。计算水平梯度,检测垂直边缘 ,计算垂直梯度,检测水平边缘,最后通过将水平边缘图和垂直边缘图相加可近似得总边缘图。
1 2 3 4 5 6 7 8 9 10
| 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")
cv_show("gradX", gradX)
|
梯度完之后的图像为:
图像闭运算
其中红色图像部分就是我们想要的部分
通过梯度运算完之后的图像我们可以看出大致的轮廓已经显示出来了,但是还有许多小空隙,这时候我们可以使用闭运算将其填充上
1 2 3
| gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel) cv_show("gradX", gradX)
|
这时候我们可以在使用二值处理,让其颜色更加突出一点:
1 2 3
| thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] cv_show("thresh", thresh)
|
这时候图像中还是有些许小孔这时候我们在进行一下闭操作
1 2
| thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel) cv_show("thresh", thresh)
|
这时候我们发现小孔几乎都合并了,得到了我们想要的四个比较清晰的轮廓
计算图像轮廓
我们得到了比较清楚的轮廓之后我们可以将我们想要的轮廓圈出来
1 2 3
|
threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
将我们的轮廓画出来:
1 2 3 4 5 6 7
| cnts = threshCnts
cur_img = image.copy()
cv2.drawContours(cur_img, cnts, -1, (0, 0, 255), 3) cv_show("cur_img", cur_img)
|
轮廓选取
现在我们要根据俄轮廓的长宽比来将我们所想要的轮廓选取出来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| locs = []
for (i, c) in enumerate(cnts): (x, y, w, h) = cv2.boundingRect(c) ar = w / float(h) if 2.5 < ar < 4.0: if 40 < w < 55 and 10 < h < 20: locs.append((x, y, w, h))
locs = sorted(locs, key=lambda x: x[0])
|
数字匹配
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| 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)
score = []
for (digit, digitROI) in digits.items(): resutl = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF) (minVal, maxVal, minLoc, maxLoc) = cv2.minMaxLoc(resutl) score.append(maxVal) groupOutput.append(str(np.argmax(score)))
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_SIMPLEX, 0.65, (0, 0, 255), 2)
output.extend(groupOutput)
|