在现代社会中,我们可能会面临需要手动输入信用卡号码的情况,这不仅费时费力,还存在输入错误的风险。因此,自动识别信用卡上的数字成为一个重要且有趣的任务(如下图,如果能自动识别出来该卡号为4000 1234 5678 9010,则会大大提高我们的效率)。
本文将探讨如何利用OpenCV库中的图像处理函数来预处理信用卡图像,以准备进行数字提取。介绍了包括图像去噪、边缘检测、轮廓提取等技术以求在图像中准确地定位信用卡数字。此外,还介绍了数字识别的关键步骤,即基于模板匹配的方法来识别数字。
ps:转载请标明原文出处!
整个任务可以分为三个步骤,模板的预处理和数字定位、信用卡图片预处理和数字定位、模板匹配。核心方法为模板匹配,它的基本思想是通过将待识别的数字与预先准备好的数字模板进行比较,从而找到最佳匹配的数字。
首先,我们需要准备一套数字模板,当然本文中已经有准备好的模板,如下图。(关注博主私聊获取全部资料!!)
接着进行一些图像预处理步骤,以确保匹配的准确性,包括图像去噪、二值化、边缘检测等。这些都可以用OpenCV中的内置函数来实现。
最后使用边缘检测和轮廓提取来找到各个数字的大致位置。
首先,对于信用卡图像,同样进行一些预处理步骤,包括图像去噪、尺寸调整、灰度化等。这些步骤有助于提高数字识别的准确性。
接着,可以通过使用边缘检测、轮廓提取、形态学操作等技术来找到数字的位置。
当数字的位置被定位后,将数字与事先准备好的数字模板进行比较。通过计算数字与模板之间的相似度,可以找到最佳匹配的数字。
本文编译环境为Pycharm(IDE) + python 3.6.3 + opencv3.4.1.15 + win10。
import cv2
import numpy as np
import argparse
# parameter setting
ag = argparse.ArgumentParser()
ag.add_argument('-i', '--image', required=True, help='Path to input image')
ag.add_argument('-j', '--template', required=True, help='Path to template')
args = vars(ag.parse_args())
def cv_show(img):
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
img = cv2.imread(args['template'])
cv_show(img)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ref = cv2.threshold(img_gray, 10, 255, cv2.THRESH_BINARY_INV)[1]
cv_show(ref)
binary, coutours, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, coutours, -1, (0, 0, 255), thickness=3)
cv_show(img)
# 对边界进行排序
def sort_coutours(conts, method='left-to-right'):
Reversed = False
if method == "right-to-left" or method == "bottom-to-top":
reverse = True
i = 0
boundingbox = [cv2.boundingRect(cont) for cont in conts]
(conts, boundingbox) = zip(*sorted(zip(conts, boundingbox), reverse=Reversed, key=lambda x: x[1][i]))
return conts, boundingbox
ref_coutours = sort_coutours(coutours)[0]
# 创建一个字典
digits = {}
for (i, c) in enumerate(ref_coutours):
(x, y, w, h) = cv2.boundingRect(c)
roi = ref[y:y+h, x:x+w]
roi = cv2.resize(roi, (57, 88))
digits[i] = roi
# 读入图片,进行预处理
image = cv2.imread(args['image'])
cv_show(image)
image = myutils.resize(image, width=300)
# 转换为灰度图
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv_show(image_gray)
# 礼帽(突出亮的区域)
rectkernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
tophat_image = cv2.morphologyEx(image_gray, cv2.MORPH_TOPHAT, rectkernel)
cv_show(tophat_image)
# sobel算子
gradx = cv2.Sobel(tophat_image, 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)
# 闭运算(先膨胀,再腐蚀)
sqkernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
closed_gradx = cv2.morphologyEx(gradx, cv2.MORPH_CLOSE, rectkernel)
cv_show(closed_gradx)
#
thresh = cv2.threshold(closed_gradx, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show(thresh)
# 闭运算(先膨胀,再腐蚀)d
closed2_gradx = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqkernel)
cv_show(closed2_gradx)
得到下图,红线圈出部分即为所需要轮廓,可见处理后的图像更容易获得所需轮廓。
binary_image, coutours_image, hierarchy_image = cv2.findContours(closed2_gradx.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
image_copy = image.copy()
cv2.drawContours(image_copy, coutours_image, -1, (0, 0, 255), thickness=2)
cv_show(image_copy)
# 定义所需要的轮廓
locs = []
for (i, cout) in enumerate(coutours_image):
groupOutput = []
(x, y, w, h) = cv2.boundingRect(cout)
ar = float(w/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])
对每个数字模板进行模板匹配,计算匹配得分。再将最高得分对应的数字添加到groupOutput列表中。将groupOutput列表中的数字合并到output列表中,最后输出结果。代码如下:
# 区域内部
output = []
for (i, (gx, gy, gw, gh)) in enumerate(locs):
groupOutput = []
group = image_gray[gy-5:gy+gh+5, gx-5:gx+gw+5]
thresh_group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show(thresh_group)
bin, cou, hie = cv2.findContours(thresh_group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cous = sort_coutours(cou, method="left-to-right")[0]
for c in cous:
(x, y, w, h) = cv2.boundingRect(c)
roi = thresh_group[y:y+h, x:x+w]
roi = cv2.resize(roi, (57, 88))
cv_show(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)))
output.extend(groupOutput)
print("Credit Card #: {}".format("".join(output)))
该任务为学习时练手任务,适合小白上手,欢迎各位大佬交流!!关注博主私聊即可获得全套源码!
ps:转载请标明原文出处!