项目简介:
本次项目使用python中的opencv库对银行卡进行形态学操作和模板匹配,最终提取出银行卡号。
数据准备:
进行模板匹配所用数据
进行训练所用的银行卡
正文:
1.导入第三方库
import cv2 as cv
import numpy as np
2.自定义函数
def show_photo(photo,title='title',waitTime=0):
# 展示图片
cv.imshow(title,photo)
cv.waitKey(waitTime)
def sort_inv(boxs):
# 数组排序
# 从小到大
boxs_sort = []
contour_index = list(map(lambda x:x[0],boxs))
for i in np.argsort(contour_index):
boxs_sort.append(boxs[i])
return boxs_sort
函数说明:
show_photo()函数主要用于便捷展示图片,非必须。
sort_inyov()函数用于数组之间的排序。程序在寻找轮廓时,优先寻找右下角,而我们的思维惯性是以左上角优先,所以此处构造sort_inyov()函数,非必须。
3.制作特征模板
# 制作特征字典
photo = cv.imread('all_num.png')
photo_gray = cv.cvtColor(photo,cv.COLOR_BGR2GRAY)
_,photo_2 = cv.threshold(photo_gray,175,255,cv.THRESH_BINARY_INV)
contours,his = cv.findContours(photo_2,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_NONE)
boxs = []
for contour in contours:
boxs.append(cv.boundingRect(contour))
boxs_sort = sort_inv(boxs)
# 得到的特征字典
dict_temp = dict(zip([i for i in range(10)],boxs_sort))
代码说明:
此处代码将特征转换为模板,dict_temp中储存的是all_num图片排序后的特征。
key为特征代表的数字,value为特征在原始图像中的x,y,w,h。
4.寻找到银行卡号所在区域
# 读图,并固定照片尺寸
photo_card = cv.resize(cv.imread('card_1.png'),(580,360))
# 转灰度
photo_card_gray = cv.cvtColor(photo_card,cv.COLOR_BGR2GRAY)
# 二值化| 自适应阈值
_,binary = cv.threshold(photo_card_gray,0,255,cv.THRESH_BINARY+ cv. THRESH_OTSU)
# 边缘提取
img_canny = cv.Canny(binary,100,200)
# 凸显数字区域
kernel = cv.getStructuringElement(cv.MORPH_RECT,(9,5))
dilation = cv.dilate(img_canny,kernel,iterations=3)
erosion = cv.erode(dilation,kernel,iterations=3)
show_photo(erosion)
代码说明
通过形态学操作,凸显出银行卡中的各个区域
运行效果:
根据寻找到的轮廓排除面积不匹配的区域,将剩下的轮廓进行排序
# 寻找轮廓
contours,his = cv.findContours(erosion,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_NONE)
count_infos = []
for contour in contours:
# 获取轮廓面积
area = cv.contourArea(contour)
# 面积 小|大 的都筛选掉
if (area < 500) | (area > 2500):
continue
x,y,w,h = cv.boundingRect(contour)
count_infos.append((x,y,w,h))
# cv.rectangle(photo_card,(x,y),(x+w,y+h),(0,0,255),2)
count_sort = sort_inv(count_infos)
5.模板匹配
for (x,y,w,h) in count_sort:
infos = []
photo_count = binary[y:y+h,x:x+w]
contours,_ = cv.findContours(photo_count,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_NONE)
boxs2 = []
for contour in contours:
# 待匹配图
boxs2.append(cv.boundingRect(contour))
qwe = sort_inv(boxs2)
for (x2,y2,w2,h2) in qwe:
img_photo1 = photo_count[y2:y2+h2,x2:x2+w2]
max_val = 0
max_temp = 0
for i in range(len(dict_temp)):
x3,y3,w3,h3 = dict_temp[i]
temp = photo_2[y3:y3+h3,x3:x3+w3]
img_temp_resize = cv.resize(temp,(w2,h2))
# 此处,img_temp_resize 是模板
# img_photo1是单个切割后的待预测图片
result = cv.matchTemplate(img_photo1, img_temp_resize, cv.TM_CCOEFF_NORMED)
(_, maxVal, _, _) = cv.minMaxLoc(result)
if maxVal>max_val:
max_val = maxVal
max_temp = i
infos.append(str(max_temp))
cv.rectangle(photo_card,(x,y-h-2),(x+w,y-2),(0,0,255),1)
# 绘制矩形框
cv.putText(photo_card,''.join(infos),(x,y-3),cv.FONT_HERSHEY_SIMPLEX,1,(0,0,0))
# 绘制检测出的值
show_photo(photo_card)
# 展示绘制后的图片
cv.destroyAllWindows()
# 关闭所有窗口
6.运行结果
ps
本篇文章仅实现完整项目jiangj,后续可能会出单独的知识点讲解。