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

先看效果:

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

首先模板是

需要预测的图像是

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

1.1 模板处理

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

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

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

python 复制代码
# 计算轮廓
#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来判断数字在模板中的位置。

python 复制代码
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代表每一个轮廓

python 复制代码
# 遍历每一个轮廓
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 对输入图像进行处理

总体流程:

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

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

python 复制代码
# 初始化卷积核
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
python 复制代码
#读取输入图像,预处理
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)
python 复制代码
#礼帽操作,突出更明亮的区域
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 的图像 像素 范围,并转换为可显示的整数格式

python 复制代码
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中可以使用闭操作来完成

python 复制代码
#通过闭操作(先膨胀,再腐蚀)将数字连在一起
gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel) 
cv_show('gradX',gradX)
python 复制代码
#THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
thresh = cv2.threshold(gradX, 0, 255,
    cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] 
cv_show('thresh',thresh)

再把细碎的小白空填充好就可以了

python 复制代码
#再来一个闭操作

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)

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

python 复制代码
# 遍历轮廓
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 = []
python 复制代码
# 遍历每一个轮廓中的数字
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]
python 复制代码
# 计算每一组中的每一个数值
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)))

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

相关推荐
测试员周周2 小时前
【Appium 系列】第16节-WebView-H5上下文切换 — 混合应用的自动化难点
运维·开发语言·人工智能·功能测试·appium·自动化·测试用例
K姐研究社4 小时前
怎么用AI制作电商口播视频,开拍APP一键生成
人工智能·音视频
LaughingZhu4 小时前
Product Hunt 每日热榜 | 2026-05-21
前端·人工智能·经验分享·chatgpt·html
传说故事5 小时前
【论文阅读】MotuBrain: An Advanced World Action Model for Robot Control
论文阅读·人工智能·具身智能·wam
北京耐用通信5 小时前
全域适配工业场景耐达讯自动化Modbus TCP 转 PROFIBUS 网关轻松实现以太网与现场总线互通
网络·人工智能·网络协议·自动化·信息与通信
火山引擎开发者社区5 小时前
TRAE × 火山引擎 Supabase:为你的 AI 应用装上“数据引擎”
人工智能
小a彤6 小时前
GE 在 CANN 五层架构中的位置
人工智能·深度学习·transformer
前端若水6 小时前
会话管理:创建、切换、删除对话历史
前端·人工智能·python·react.js
Upsy-Daisy6 小时前
AI Agent 项目学习笔记(八):Tool Calling 工具调用机制总览
人工智能·笔记·学习
企学宝6 小时前
企学宝5月专题课程丨《OpenClaw AI 智能体实战营:从零基础部署到全场景自动化落地》
人工智能·ai·企业培训