news 2026/1/29 2:46:45

Opencv总结4——项目实战-信用卡数字识别

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Opencv总结4——项目实战-信用卡数字识别

先看效果:

代码资料看本人博客资源。

首先模板是

需要预测的图像是

任务是:使用模板匹配,一定要是数字特别接近的模板,然后匹配银行卡上面的数字。

1.1 模板处理

灰度处理——> 二值处理——> 轮廓处理

# 灰度图 ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值图像 ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]

RETR_EXTERNAL表示计算外轮廓,计算外接矩形

# 计算轮廓 #cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图),cv2.RETR_EXTERNAL只检测外轮廓,cv2.CHAIN_APPROX_SIMPLE只保留终点坐标 #返回的list中每个元素都是图像中的一个轮廓 ref_, refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) # -1 表示画出所有的轮廓 cv2.drawContours(img,refCnts,-1,(0,0,255),3) refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0] #排序,从左到右,从上到下

上面调用了sort_contours方法,下面是计算并排序的方法,boundingBoxes 是一个元组,总共包含4个值x,y,h,w,直接按照横坐标x来判断数字在模板中的位置。

def sort_contours(cnts, method="left-to-right"): reverse = False i = 0 if method == "right-to-left" or method == "bottom-to-top": reverse = True if method == "top-to-bottom" or method == "bottom-to-top": i = 1 boundingBoxes = [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))

在得到10个轮廓之后,进行逐个遍历,c代表每一个轮廓

# 遍历每一个轮廓 for (i, c) in enumerate(refCnts): # 计算外接矩形并且resize成合适大小 (x, y, w, h) = cv2.boundingRect(c) roi = ref[y:y + h, x:x + w] # 得到外接矩形之后,把这个位置抠出来 roi = cv2.resize(roi, (57, 88)) # 每一个数字对应每一个模板 digits[i] = roi

1.2 对输入图像进行处理

总体流程:

对图片的处理——灰度处理——> 二值处理——>图像闭操作——>分组提取数字 ——> 切分成每个小的区域—— > 对于每个小区域都进行模板匹配。

信用卡的背景中有很多的干扰项,需要做一些额外的预处理操作

# 初始化卷积核 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 = myutils.resize(image, width=300) 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, ddepth=cv2.CV_32F, dx=1, dy=0, #ksize=-1相当于用3*3的 ksize=-1)

对梯度数据(gradX)进行归一化处理,将其数值范围映射到 0-255 的图像像素范围,并转换为可显示的整数格式

gradX = np.absolute(gradX) # 对梯度数组gradX的每一个元素取绝对值。 (minVal, maxVal) = (np.min(gradX), np.max(gradX)) # 计算取绝对值后梯度数组的最小值(minVal)和最大值(maxVal) gradX = (255 * ((gradX - minVal) / (maxVal - minVal))) # 将梯度值从原来的任意范围(比如[5.2, 189.7])映射到0-255的范围 gradX = gradX.astype("uint8") # 将归一化后的浮点型数组转换为uint8类型(8 位无符号整数,取值范围 0-255)。 print (np.array(gradX).shape) # 打印处理后数组的形状(维度)。 cv_show('gradX',gradX)

接下来就是如何让上面的数字更像一个块,opencv中可以使用闭操作来完成

#通过闭操作(先膨胀,再腐蚀)将数字连在一起 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)

我们需要过滤掉数字以外的轮廓

# 遍历轮廓 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, key=lambda x:x[0]) output = []
# 遍历每一个轮廓中的数字 for (i, (gX, gY, gW, gH)) in enumerate(locs): # initialize the list of group digits groupOutput = [] # 根据坐标提取每一个组 group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5] cv_show('group',group) # 见下图 # 预处理 group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] cv_show('group',group) # 二值化了,更明亮,见下图 # 计算每一组的轮廓 group_,digitCnts,hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) digitCnts = contours.sort_contours(digitCnts, method="left-to-right")[0]

# 计算每一组中的每一个数值 for c in digitCnts: # 找到当前数值的轮廓,resize成合适的的大小 (x, y, w, h) = cv2.boundingRect(c) roi = group[y:y + h, x:x + w] roi = cv2.resize(roi, (57, 88)) cv_show('roi',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)))

然后程序就可以输出预测啦。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/22 16:47:10

用%20Portainer%20部署%20Nginx%20很简单?加个%20cpolar%20远程访问更给力

文章目录前言1. 安装Portainer1.1 访问Portainer Web界面2. 使用Portainer创建Nginx容器3. 将Web静态站点实现公网访问4. 配置Web站点公网访问地址4.1公网访问Web站点5. 固定Web静态站点公网地址6. 固定公网地址访问Web静态站点前言 Portainer 的主要功能是提供可视化的 Web 界…

作者头像 李华
网站建设 2026/1/20 13:25:04

父子定律,准到吓人

爸爸爱干饭&#xff0c;孩子不挑食&#xff08;毕竟抢饭得快&#xff09;爸爸爱唠嗑&#xff0c;孩子嘴不笨&#xff08;从小耳濡目染练口才&#xff09;爸爸不宅家&#xff0c;孩子爱溜达&#xff08;出门疯玩比看电视香&#xff09;爸爸会认错&#xff0c;孩子不犟嘴&#xf…

作者头像 李华
网站建设 2026/1/26 9:08:01

✅2026最全Java毕业设计选题方向汇总|附难度分级+技术栈建议

Java作为高校计算机、软件工程专业核心编程语言&#xff0c;毕业设计选题既要贴合课程所学&#xff0c;又要兼顾技术可行性、创新点、答辩通过率&#xff0c;同时适配不同编程基础的同学。本文整理了6大热门选题方向&#xff0c;涵盖基础入门、进阶实战、前沿创新三类难度&…

作者头像 李华
网站建设 2026/1/19 10:03:34

DUT硬件调试接口集成:JTAG与UART配置指南

DUT调试接口实战&#xff1a;JTAG与UART如何协同构建可靠调试链路你有没有遇到过这样的场景&#xff1f;新板子第一次上电&#xff0c;烧录完固件却毫无反应——串口没输出、JTAG连不上、LED也不闪。这时候&#xff0c;是电源问题&#xff1f;晶振坏了&#xff1f;还是Bootload…

作者头像 李华
网站建设 2026/1/25 0:01:52

金仓数据库MongoDB兼容版深度评测:从性能到实战的全面解析

一、引言:数字化转型下的数据库新选择 如今企业数字化转型进入深水区,大家对数据库的要求早就不是"能存能取"那么简单。文档数据库因为天生适合处理半结构化数据,成了很多现代应用的标配。可现实情况是,随着技术自主可控、供应链安全成为必答题,再加上业务常常需要同…

作者头像 李华
网站建设 2026/1/23 14:58:51

PyTorch-CUDA-v2.6镜像适配主流GPU,训练速度提升3倍以上

PyTorch-CUDA-v2.6镜像适配主流GPU&#xff0c;训练速度提升3倍以上 在深度学习项目从实验室走向生产的今天&#xff0c;一个常见的痛点是&#xff1a;为什么同样的模型代码&#xff0c;在同事的机器上跑得飞快&#xff0c;而在自己的环境里却频频报错、训练缓慢&#xff1f;答…

作者头像 李华