直接上源码,需要的copy,运行结果会在原图上打印出来,卡号也会在控制台上打印出来,注释很详细的,可以先看看,不懂的可以在下面留言,博主看到了会第一时间讲解的。
import numpy as np
import cv2
import myutils # 这是另一个py文件,代码贴在下方# 绘图展示
def cv_show(name, img):cv2.imshow(name, img)cv2.waitKey(0)cv2.destroyAllWindows()# 读取图像进行灰度处理,这里没有输出原图
img = cv2.imread('C:\Users\86151\desktop\test02\template02.png')
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 对图像的二值化处理需要输入灰度图
cv_show('gray', ref)
# 进行二值化处理
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]
cv_show('2', ref)
# 计算轮廓 contours:函数处理的图像 cnts:轮廓的点集 hierarchy:各层轮廓索引
contours, cnts, hierarchy = cv2.py(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, cnts, -1, (0, 0, 255), 3) # 这里使用了原本图像进行了轮廓描边
cv_show('img', img)
# 给每组数字画矩形边,使用函数boundingRect() 返回的是四个值x,y,w,h分别是(矩形左上角点的坐标,还有矩形的宽度和高度)
# 用boundingBoxes来接受这四个值,这个变量为一个列表
boundingBoxes = [cv2.boundingRect(c) for c in cnts] # cnts就是每个轮廓的序号列表
# 将每个矩形的点集(列表)与其(左上角点坐标和高、宽)(元组)一起组成了新的列表 ,
# 然后按照升序排序,也就是将数字0~9的矩形位置进行了升序排序,有利于后续匹配字样时候的查找
cnts = myutils.sort_contours(cnts, method='left-to-right')[0]digits = {} # 存储模板# i表示矩形的序号,从0开始,刚好可以代替0~9着十个数字
for (i, c) in enumerate(cnts):(x, y, w, h) = cv2.boundingRect(c)# ref是二值化处理图像 roi存入了所有包围中最小的框roi = ref[y:y + h, x:x + w]# 将模板扩大成一定大小的数字,后续输入的数字也要一样大roi = size(roi, (57, 88))digits[i] = roi
# 返回一个9*3的矩形和5*5的正方形元素,这就是卷积核大小,用在腐蚀/膨胀操作
rectKernel = StructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel = StructuringElement(cv2.MORPH_RECT, (5, 5))# 设置卷积核的大小,进要输入银行卡的读取
image = cv2.imread('C:\Users\86151\Desktop\test02\card03.png')
image = size(image, width=300)# 进行灰度化
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv_show('image', gray)# 进行礼帽操作,morphologyEx只是形态学变换函数,礼貌操作指的是:MORPH_TOPHAT
# 顶帽:原图像-开运算图,突出原图像中比周围亮的区域,为了更好的找出数字组
tophat = phologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) # 顶帽:原图像 - 开运算图像
cv_show('tophat', tophat)# 进行sobel算子操作 ksize=-1相当于用3*3的卷积核进行筛选ll(内置的卷积核)
gradx = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1) # 检索图像的边界,gradx是经过Sobel算子处理后的图像的像素点矩阵
gradx = np.absolute(gradx) # 对数组中每一个元素求绝对值
(minVal, maxVal) = (np.min(gradx), np.max(gradx)) # 找到最大边界差值和最小边界插值
gradx = (255 * ((gradx - minVal) / (maxVal - minVal))) # 归一化公式,将图像像素数据限制在0-1之间,便于后续的操作
gradx = gradx.astype("uint8") # 将gradx的矩阵元素改为数据类型uint8,一般图像的像素点类型都是uint8
print(np.array(gradx).shape) # 打印gradx的shape
cv_show('gradx', gradx)# 进行闭操作,把相近的数字连在一起 闭操作:先腐蚀后膨胀
gradx = phologyEx(gradx, cv2.MORPH_CLOSE, sqKernel)
cv_show('gradx', gradx)# 让opencv自己寻找合适的阈值,然后进行处二值化理
thres = cv2.threshold(gradx, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show('thres', thres)# 再进行一次闭操作 让图片更圆满
thres = phologyEx(thres, cv2.MORPH_CLOSE, sqKernel)
cv_show('thres2', thres)thres = phologyEx(thres, cv2.MORPH_DILATE, sqKernel)
cv_show('thres2', thres)thres = phologyEx(thres, cv2.MORPH_DILATE, sqKernel)
cv_show('thres2', thres)# 计算轮廓,标出原图片中的数字
contours, threshCnts, hierarchy = cv2.py(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 在原图进行展示
cnts = threshCnts # 代表图像轮廓的点集
cur_img = image # image:银行卡原图
cv2.drawContours(cur_img, cnts, -1, (0, 0, 255), 3)
cv_show('img', cur_img)# 在画边框的原图中识别出需要使用的字符,四个为一组
locs = []# 遍历轮廓
for (i, c) in enumerate(cnts):x, y, w, h = cv2.boundingRect(c)# 计算出框的比例,根据比例筛选是不是我们需要的数字模板ar = w / float(h) # 计算每一个模板的宽高比if (2 < ar) and (ar < 6.0):if (30 < w < 65) and (10 < h < 40):# 符合我们需要的条件,留下来,保存到locs数组locs.append((x, y, w, h)) # 向数字中加入这个找到的模板
# 根据四组模板的不同横坐标位置进行排序,按照升序
locs = sorted(locs, key=lambda x: x[0], reverse=False)
output = []# 遍历轮廓中的每个数字
for (i, (gx, gy, gw, gh)) in enumerate(locs):groupOut = [] # 用来存储模板匹配的最大得分# 将四个小组的每一组分别展现出来,gx,gy,gh,gw是近似矩形的左上角顶点坐标以及高度和宽度group = gray[gy - 5:gy + gh + 5, gx - 5:gx + gw + 5]cv_show('small_group', group)# 对小组图像进行阈值(二值化)处理group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]cv_show('small_group_2', group)# 找到每小组中的单个数字轮廓的点集 py()指的是每一个小组(包含着四个数字)contours, cnts, hierarchy = cv2.py(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)cnts = myutils.sort_contours(cnts, method="left-to-right")[0]# 利用找到的点集合展现出每一个数字for c in cnts: # 一个c就是一个具体数字的点集,cnts是四个数字的点集(x, y, w, h) = cv2.boundingRect(c) # 找到一个具体数字的左上角坐标和宽、高roi = group[y:y + h, x:x + w]roi = size(roi, (57, 88)) # 设置需要匹配的数字大小,与模板相对应cv_show('roi', roi)# 计算匹配得分scores = []# 利用近似矩阵的方法,将需要匹配的数字与我们先前设置好的十个数字的模板进行匹配,计算得分,加入到score[]for (digit, digiFact) in digits.items(): # digits是模板的十个数字的字典集合result = cv2.matchTemplate(roi, digiFact, cv2.TM_CCOEFF) # digiFact就是模板 result是一个矩阵min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) # 最小值、最大值、最小值位置、最大值位置scores.append(max_val)# argmax():取出列表中最大值对应的索引groupOut.append(str(np.argmax(scores))) # groupOut中一个数字(总共数字是银行卡上所有的数字)就是一个数字匹配十个模板的最大的分数# 将结果画出来angle(image, (gx - 5, gy - 5), (gx + gw + 5, gy + gh + 5), (0, 0, 255), 2)cv2.putText(image, "".join(groupOut), (gx, gy - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)# 控制台输出卡号,别忘了开头部分我们已经把模板的实际数值与他们的位置对应起来了,比如在第三个位置的模板数字就是数字3# groupOut里面装的又是每个数字的最大匹配分数的 模板 所在的位置,那么也就代表着数字实际的值d(groupOut)print("Credit Card #: {}".format("".join(output)))
cv_show('result', image)
myutils.py代码:
import cv2def sort_contours(cnts, method="left-to-right"):reverse = Falsei = 0if method == "right-to-left" or method == "bottom-to-top":reverse = True # 降序排列if method == "top-to-bottom" or method == "bottom-to-top":i = 1boundingBoxes = [cv2.boundingRect(c) for c in cnts] #用一个最小的矩形,把找到的形状包起来x,y,h,w 给每个小模版找一个矩形(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),key=lambda b: b[1][i], reverse=reverse))return cnts, boundingBoxes
def resize(image, width=None, height=None, inter=cv2.INTER_AREA):dim = None(h, w) = image.shape[:2]if width is None and height is None:return imageif width is None:r = height / float(h)# 计算高度大了多少倍dim = (int(w * r), height)# 第一个是高度,else:r = width / float(w)dim = (width, int(h * r)) # 高也扩大相同倍数resized = size(image, dim, interpolation=inter)return resized
运行结果:
控制台上:
SERENDIPITY
本文发布于:2024-01-31 08:30:46,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170666104727167.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |