成都网站建设与网站制作,零购物网站怎么建设,凡科建站添加文章,长春可做微网站的公司一、思路分析
大体思路#xff1a;首先拿到一张银行卡#xff0c;我们得有银行卡号数字的0-9样式的模板#xff0c;然后再通过不同数字的轮廓的外接矩形来进行匹配#xff0c;最终识别出银行卡号所对应的数字。 银行卡数字模板#xff1a; 银行卡信息#xff1a;
拿到…一、思路分析
大体思路首先拿到一张银行卡我们得有银行卡号数字的0-9样式的模板然后再通过不同数字的轮廓的外接矩形来进行匹配最终识别出银行卡号所对应的数字。 银行卡数字模板 银行卡信息
拿到银行卡的时候因为银行卡上面不仅仅只是银行卡号还会存在一些干扰项这时候需要对这些干扰项进行过滤这些干扰项和数字所在的外接轮廓大小是不相同的故可以以此进行过滤筛选。当然一下次不会将单个数字给定位到拿到的也不是单个数字绝大多数的银行卡号都是四个为一组接下来再对每一组中的四个数字进行拆分。
二、导包以及相关函数
from imutils import contours
import numpy as np
import argparse
import cv2# 绘图展示
def cv_show(name,img):cv2.imshow(name, img)cv2.waitKey(0)cv2.destroyAllWindows()#因为银行卡分为四个大轮廓每个大轮廓中又包括四个数字这里需要定义一个函数用于将识别出的四个大轮廓依次排序存放
def sort_contours(cnts, methodleft-to-right):reverse Falsei 0if method right-to-left or method bottom-to-top:reverse Trueif 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),keylambda b: b[1][i], reversereverse))#对模板上的轮廓进行排序也就是将模板上的数字给贴个标签return cnts, boundingBoxes#返回轮廓以及轮廓信息一个列表#因为银行卡数字这块图像会较小需要重新指定一下图像大小
def resize(image, widthNone, heightNone, intercv2.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 cv2.resize(image, dim, interpolationinter)return resized
三、获取模板上的数字信息
指定测试图像以及模板图像位置
# 设置参数
ap argparse.ArgumentParser()
ap.add_argument(-i, --image, requiredTrue,helppath to input image)#指定输入图像
ap.add_argument(-t, --template, requiredTrue,helppath to template OCR-A image)#指定模板图像
args vars(ap.parse_args())设置银行卡类型参数
# 指定信用卡类型
FIRST_NUMBER {3: American Express,4: Visa,5: MasterCard,6: Discover Card
}首先处理模板图像模板图像存放这0-9数字读取模板图像灰度图、二值化
# 读取一个模板图像
img cv2.imread(args[template])
cv_show(img,img)
# 灰度图
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)对二值化之后的模板图像获取轮廓
# 计算轮廓
#cv2.findContours()函数接受的参数为二值图即黑白的不是灰度图,cv2.RETR_EXTERNAL只检测外轮廓cv2.CHAIN_APPROX_SIMPLE只保留终点坐标
#返回的list中每个元素都是图像中的一个轮廓
ref_, refCnts, hierarchy cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)用红色线进行绘制模板轮廓定义一个空字典digits用于存储后续得到模板图像轮廓信息
cv2.drawContours(img,refCnts,-1,(0,0,255),3) #用红色线画轮廓
cv_show(img,img)
print (np.array(refCnts).shape)#出现10个轮廓0-9数字所对应的图像信息
refCnts sort_contours(refCnts, methodleft-to-right)[0] #排序从左到右从上到下 得到模板排序完之后的轮廓
digits {}#指定一个空字典对模板图像中0-9个轮廓依次去遍历得到信息存储到digits字典中
# 遍历每一个轮廓
for (i, c) in enumerate(refCnts):#在模板轮廓中不断的去遍历i为轮廓的索引c为轮廓# 计算外接矩形并且resize成合适大小(x, y, w, h) cv2.boundingRect(c)#得到第i个轮廓的外接矩形信息roi ref[y:y h, x:x w]#用外接矩形对模板图像相同位置抠出来roi cv2.resize(roi, (57, 88))#抠出来的模板图像的大小可能会较小把图像给resize成一个差不多大小的图像# 每一个数字对应每一个模板digits[i] roi#把从模板中抠出来的数字存入到字典中模板为value索引为key
#循环结束之后digits字典中就存放有了0-9数字的模板图像信息三、对测试图像进行形态学操作消除其他的干扰因素
根据不同情景选取合适的卷积核
# 初始化卷积核 这里主要是根据具体情况具体分析 卷积核的定义需要看具体情况而定
rectKernel cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))对测试图像进行预处理操作
#读取输入图像预处理
image cv2.imread(args[image])#读取测试图片
cv_show(image,image)#显示一下测试图像
image resize(image, width300)#重新resize一下图像大小
gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)#因为传入的测试图像是彩色图需要转换为灰度图
cv_show(gray,gray)#显示一下灰度图将得到的灰度图图像通过礼帽操作使得数字特征信息更加明显
#礼帽操作突出更明亮的区域
tophat cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
cv_show(tophat,tophat)
gradX cv2.Sobel(tophat, ddepthcv2.CV_32F, dx1, dy0, ksize-1)#ksize-1相当于用3*3的gradX np.absolute(gradX)
(minVal, maxVal) (np.min(gradX), np.max(gradX))
gradX (255 * ((gradX - minVal) / (maxVal - minVal)))
gradX gradX.astype(uint8)print (np.array(gradX).shape)
cv_show(gradX,gradX)将数字部分进行融合到一块分成四个大轮廓
#通过闭操作先膨胀再腐蚀将数字连在一起
gradX cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
cv_show(gradX,gradX)
#THRESH_OTSU会自动寻找合适的阈值适合双峰需把阈值参数设置为0
thresh cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show(thresh,thresh)#因为实际效果并不好没有完全融合到一块故再来一个闭操作
thresh cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel) #再来一个闭操作
cv_show(thresh,thresh)绘制测试图像中的轮廓信息
# 计算轮廓
thresh_, threshCnts, hierarchy cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)cnts threshCnts
cur_img image.copy()
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 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, keylambda x:x[0])#对获取的大轮廓从左到右排序
output []遍历每个轮廓中的数字信息与模板中的数字信息比较评分
# 遍历每一个轮廓中的数字
for (i, (gX, gY, gW, gH)) in enumerate(locs):# initialize the list of group digitsgroupOutput []# 根据坐标提取每一个组group gray[gY - 5:gY gH 5, gX - 5:gX gW 5]#为了使得效果处理较好将拿到的大轮廓数据往上往下往左往右都扩大点cv_show(group,group)#显示第i个大轮廓# 预处理group cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]#对第i个大轮廓二值化为了找大轮廓中的四个数字轮廓cv_show(group,group)# 计算每一组的轮廓group_,digitCnts,hierarchy cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)#对第i个大轮廓依次进行轮廓检测digitCnts contours.sort_contours(digitCnts,methodleft-to-right)[0]#对检测出来的小轮廓从左到右排序# 计算每一组中的每一个数值for c in digitCnts:#每个大轮廓都有四个数字对每一个大轮廓中的每一个小数字分别进行计算此时c有4个值因为测试图像中有每个大轮廓有四个数字# 找到当前数值的轮廓resize成合适的的大小(x, y, w, h) cv2.boundingRect(c)#对大轮廓找取外接轮廓roi group[y:y h, x:x w]#找到每个数字的轮廓信息roi cv2.resize(roi, (57, 88))#这个resize的参数要与上列的模板设定的大小一致到时候需要做模板匹配大小应该一致cv_show(roi,roi)#依次展示第i个大轮廓中的四个数字# 计算匹配得分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)))#选出得分最高的那个数即可# 画出来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)#得到第c组大轮廓的四个数
五、输出展示
# 打印结果
print(Credit Card Type: {}.format(FIRST_NUMBER[output[0]]))
print(Credit Card #: {}.format(.join(output)))
cv2.imshow(Image, image)
cv2.waitKey(0)六、Pycharm参数设定方法 找到Edit Configurations --image images/credit_card_04.png --template ocr_a_reference.png 其中images/credit_card_04.png为测试图片的位置、ocr_a_reference.png为银行卡数字模板图片的位置
七、项目完整代码
# 导入工具包
from imutils import contours
import numpy as np
import argparse
import cv2
#import myutils# 设置参数
ap argparse.ArgumentParser()
ap.add_argument(-i, --image, requiredTrue,helppath to input image)#指定输入图像
ap.add_argument(-t, --template, requiredTrue,helppath to template OCR-A image)#指定模板图像
args vars(ap.parse_args())# 指定信用卡类型
FIRST_NUMBER {3: American Express,4: Visa,5: MasterCard,6: Discover Card
}
# 绘图展示
def cv_show(name,img):cv2.imshow(name, img)cv2.waitKey(0)cv2.destroyAllWindows()def sort_contours(cnts, methodleft-to-right):reverse Falsei 0if method right-to-left or method bottom-to-top:reverse Trueif 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),keylambda b: b[1][i], reversereverse))#对模板上的轮廓进行排序也就是将模板上的数字给贴个标签return cnts, boundingBoxes#返回轮廓以及轮廓信息一个列表def resize(image, widthNone, heightNone, intercv2.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 cv2.resize(image, dim, interpolationinter)return resized# 读取一个模板图像
img cv2.imread(args[template])
cv_show(img,img)
# 灰度图
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)# 计算轮廓
#cv2.findContours()函数接受的参数为二值图即黑白的不是灰度图,cv2.RETR_EXTERNAL只检测外轮廓cv2.CHAIN_APPROX_SIMPLE只保留终点坐标
#返回的list中每个元素都是图像中的一个轮廓
ref_, refCnts, hierarchy cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)cv2.drawContours(img,refCnts,-1,(0,0,255),3) #用红色线画轮廓
cv_show(img,img)
print (np.array(refCnts).shape)#出现十个轮廓
refCnts sort_contours(refCnts, methodleft-to-right)[0] #排序从左到右从上到下 得到模板排序完之后的轮廓
digits {}#指定一个空字典# 遍历每一个轮廓
for (i, c) in enumerate(refCnts):#在模板轮廓中不断的去遍历i为轮廓的索引c为轮廓# 计算外接矩形并且resize成合适大小(x, y, w, h) cv2.boundingRect(c)#得到第i个轮廓的外接矩形信息roi ref[y:y h, x:x w]#用外接矩形对模板图像相同位置抠出来roi cv2.resize(roi, (57, 88))#抠出来的模板图像的大小可能会较小把图像给resize成一个差不多大小的图像# 每一个数字对应每一个模板digits[i] roi#把从模板中抠出来的数字存入到字典中模板为value索引为key
#循环结束之后digits字典中就存放有了0-9数字的模板图像信息# 初始化卷积核
rectKernel cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))#读取输入图像预处理
image cv2.imread(args[image])#读取测试图片
cv_show(image,image)#显示一下测试图像
image resize(image, width300)#重新resize一下图像大小
gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)#因为传入的测试图像是彩色图需要转换为灰度图
cv_show(gray,gray)#显示一下灰度图#礼帽操作突出更明亮的区域
tophat cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
cv_show(tophat,tophat)
#
gradX cv2.Sobel(tophat, ddepthcv2.CV_32F, dx1, dy0, ksize-1)#ksize-1相当于用3*3的gradX np.absolute(gradX)
(minVal, maxVal) (np.min(gradX), np.max(gradX))
gradX (255 * ((gradX - minVal) / (maxVal - minVal)))
gradX gradX.astype(uint8)print (np.array(gradX).shape)
cv_show(gradX,gradX)#通过闭操作先膨胀再腐蚀将数字连在一起
gradX cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
cv_show(gradX,gradX)
#THRESH_OTSU会自动寻找合适的阈值适合双峰需把阈值参数设置为0
thresh cv2.threshold(gradX, 0, 255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show(thresh,thresh)#再来一个闭操作
thresh cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel) #再来一个闭操作
cv_show(thresh,thresh)# 计算轮廓
thresh_, threshCnts, hierarchy cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)cnts threshCnts
cur_img image.copy()
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 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, keylambda x:x[0])#对获取的大轮廓从左到右排序
output []# 遍历每一个轮廓中的数字
for (i, (gX, gY, gW, gH)) in enumerate(locs):# initialize the list of group digitsgroupOutput []# 根据坐标提取每一个组group gray[gY - 5:gY gH 5, gX - 5:gX gW 5]#为了使得效果处理较好将拿到的大轮廓数据往上往下往左往右都扩大点cv_show(group,group)#显示第i个大轮廓# 预处理group cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]#对第i个大轮廓二值化为了找大轮廓中的四个数字轮廓cv_show(group,group)# 计算每一组的轮廓group_,digitCnts,hierarchy cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)#对第i个大轮廓依次进行轮廓检测digitCnts contours.sort_contours(digitCnts,methodleft-to-right)[0]#对检测出来的小轮廓从左到右排序# 计算每一组中的每一个数值for c in digitCnts:#每个大轮廓都有四个数字对每一个大轮廓中的每一个小数字分别进行计算此时c有4个值因为测试图像中有每个大轮廓有四个数字# 找到当前数值的轮廓resize成合适的的大小(x, y, w, h) cv2.boundingRect(c)#对大轮廓找取外接轮廓roi group[y:y h, x:x w]#找到每个数字的轮廓信息roi cv2.resize(roi, (57, 88))#这个resize的参数要与上列的模板设定的大小一致到时候需要做模板匹配大小应该一致cv_show(roi,roi)#依次展示第i个大轮廓中的四个数字# 计算匹配得分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)))#选出得分最高的那个数即可# 画出来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)#得到第c组大轮廓的四个数# 打印结果
print(Credit Card Type: {}.format(FIRST_NUMBER[output[0]]))
print(Credit Card #: {}.format(.join(output)))
cv2.imshow(Image, image)
cv2.waitKey(0)八、测试图片和模板图片
模板图像 测试图像
九、运行效果展示