任务:使用opencv利用模板实现对信用卡卡号的识别
模板图像如图:
使用opencv对图像处理得到图像对应的数字并使用字典存储
def template_process(img):
temp_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 转换颜色
ret, temp_img = cv.threshold(temp_img, 127, 255, cv.THRESH_BINARY_INV)
# show('temp', temp_img)
# 模板轮廓检测
contours, hierarchy = cv.findContours(temp_img, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 画出轮廓-->在原来的图像上才能画出彩色
res = cv.drawContours(img, contours, -1, (255, 0, 0), 3)
show('temp-counteour', res)
# 对轮廓进行排序
contours, bounding_loc = sort_contour(contours)
# 图形与数字匹配
digitals = {}
for i in range(len(bounding_loc)):
x, y, w, h = bounding_loc[i]
digit_area = temp_img[y:y + h, x:x + w]
digit_area = cv.resize(digit_area, (70, 120))
# show("num", digit_area)
digitals[i] = digit_area
return digitals
信用卡原图:
对信用卡进行顶帽操作突出数字区域
计算水平梯度将4个数字为一组让其更像一个块
进行闭操作将数字连在一起
要获取4个轮廓之前先进行阈值处理
获取轮廓
通过特征选取数字部分的4个轮廓,对每个轮廓再获取每个数字的轮廓并使用模板进行匹配
对以上4个轮廓进行分别进行获取4个数字的图像进行模板匹配匹配后得到数字是
可以看出可以将信用卡中的数字识别出来
主程序
def recog_card():
"""识别信用卡上的数字"""
#对模板检测
# 读取模板图片
img = cv.imread("image/card-recog/ocr_a_reference.png")
# 返回一个模板字典
digitals = template_process(img)
# 信用卡操作
# 读取信用卡
card_img = cv.imread('image/card-recog/credit_card_05.png')
card_gray = cv.cvtColor(card_img, cv.COLOR_BGR2GRAY)
# show('card-img', card_gray)
# 自定义卷积和
rect_Kernel = cv.getStructuringElement(cv.MORPH_RECT,(9,3))
digit_Kernel = cv.getStructuringElement(cv.MORPH_RECT,(5,4))
erosion_Kernel = np.ones((3,3),np.uint8)
# 对信用卡尺度变换
resi_img = resized_img(card_img, width=800)
resi_img_gray = resized_img(card_gray, width=800)
show('resized-img', resi_img)
# 突出数字区域
tophat_img = cv.morphologyEx(resi_img_gray, cv.MORPH_TOPHAT, rect_Kernel)
show("digit-img", tophat_img)
# 计算梯度
gradX = cv.Sobel(tophat_img, ddepth=cv.CV_32F, dx=1, dy=0, # ksize=-1相当于用3*3的
ksize=-1)
gradX = np.absolute(gradX) # absolute: 计算绝对值
(minVal, maxVal) = (np.min(gradX), np.max(gradX))
gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
gradX = gradX.astype("uint8")
show("sobelx-img", gradX)
# 通过闭操作(先膨胀,再腐蚀)将数字连在一起. 将本是4个数字的4个框膨胀成1个框,就腐蚀不掉了
gradX = cv.morphologyEx(gradX, cv.MORPH_CLOSE, rect_Kernel, iterations=4)
show('Input_CLOSE_gradX', gradX)
# 阈值化
ret2, th2 = cv.threshold(gradX, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
show('Input_thresh', th2)
# 再来一个小的闭操作
gradX = cv.morphologyEx(th2, cv.MORPH_CLOSE, rect_Kernel, iterations=2)
show('Input_CLOSE_gradX', gradX)
contours, hierarchy = cv.findContours(gradX, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE )
# 画出轮廓-->在原来的图像上才能画出彩色
res = cv.drawContours(resi_img, contours, -1, (255,0 , 0), 3)
show('temp-counteour', res)
# 保存符合的矩形
fit_rect = min_dist(contours, 4)
fit_rect = sorted(fit_rect, key=lambda x:x[0])
print(fit_rect)
# 匹配到的数字
match_digital = []
for (i,(x,y,w,h)) in enumerate(fit_rect):
# 得到每一个小区域
group_img = resi_img[y-5:y+h+5, x-5:x+w+5]
# group_img = resized_img(group_img, width=500)
group_gray = resi_img_gray[y-5:y+h+5, x-5:x+w+5]
# group_gray = resized_img(group_gray, width=500)
show('group',group_gray)
# 阈值处理
ret2, th_group = cv.threshold(group_gray, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)
show('Input_thresh', th_group)
# 轮廓
contours, hierarchy = cv.findContours(th_group, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 画出轮廓-->在原来的图像上才能画出彩色
res = cv.drawContours(group_img, contours, -1, (255, 0, 0), 1)
show('temp-counteour', res)
# 得到4个轮廓
fit_contour = []
for (i,contour) in enumerate(contours):
x,y,w,h = cv.boundingRect(contour)
if h/w>1.1 :
# print(w,h,h/w)
fit_contour.append(contour)
# print(len(contours))
# 轮廓排序
sort_cont = sort_contour(fit_contour)
# print(sort_cont[1])
# 保存每个group中对应数字
group_match_digit = []
# 进行模板匹配
for rect in sort_cont[1]:
x,y,w,h = rect
# 得到每一个小块的图片
roi = th_group[y:y+h, x:x+w]
roi = cv.resize(roi,(70,120))
# show('roi',roi)
# 获取每一个小块的最佳评分
score = []
best_score = 1
best_index = 0
for (digital, digital_area) in digitals.items():
temp_score = cv.matchTemplate(roi, digital_area,cv.TM_SQDIFF_NORMED)
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(temp_score)
score.append(min_val)
if min_val
工具函数
# 显示图片
def show(name,img):
cv.imshow(name, img)
cv.waitKey(0)
cv.destroyAllWindows()
# 对找到的轮廓进行排序
def sort_contour(contour, direction='left-right'):
"""
对传递进来的轮廓进行排序
排序后并返回轮廓和轮廓坐标
x, y, w, h = cv2.boundingRect(cnt)
"""
reverse = False
i = 0
if direction == 'bottom-top':
reverse = False
i = 1
if direction == 'right-left':
reverse = True
if direction == 'top-bottom':
i = 1
if direction == 'left-right':
None
# 获得外接的矩形
bounding_loc = [cv.boundingRect(cont) for cont in contour]
sort = sorted(zip(contour, bounding_loc), key=lambda b:b[1][i], reverse=reverse)
contour, bounding_loc = list(zip(*sort))
return contour, bounding_loc
def resized_img(img, width=None, height=None, inter = cv.INTER_AREA):
"""
等比例重新调整大小
:param img:
:param width: 宽度整数
:param height: 高度整数
:param inter:
:return: 返回调整后的图形矩阵
"""
# 如果宽高都为0
if height is None and width is None:
return img
h,w = img.shape[:2]
# print(w,h)
# 同比例放缩图 形
prop = w/h
# print(prop)
if height is not None and width is not None: # 宽高都给定
return cv.resize(img,(width,height),interpolation=inter)
elif height is not None:# 给定高度计算宽度
w = int(height*prop)
return cv.resize(img,(w,height),interpolation=inter)
else:
h = int(width/prop)# 给定宽度计算高度
# print(h)
return cv.resize(img,(width,h),interpolation=inter)