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)))

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

相关推荐
许泽宇的技术分享1 小时前
2025年度技术之旅:在AI浪潮下的个人突破、持续创作与平衡之道
大数据·人工智能
俞凡2 小时前
AI 智能体高可靠设计模式:并行执行
人工智能
骚戴2 小时前
2025企业级架构演进:重构 Java/Python 的 RAG 与 Agent 系统的六种核心策略
java·人工智能·大模型·llm·api
Godspeed Zhao2 小时前
自动驾驶中的传感器技术85——Sensor Fusion(8)
人工智能·机器学习·自动驾驶
玄同7652 小时前
Python 项目实战中“高内聚低耦合”的设计方法 —— 基于七大设计原则与拓展技巧
开发语言·人工智能·python·语言模型·pycharm·设计原则·项目实战
三块可乐两块冰2 小时前
【第二十五周】机器学习笔记二十四
人工智能·笔记·机器学习
共绩算力2 小时前
Molmo2-8B:崛起为视频问答领域新黑马
人工智能·共绩算力
GA6666662 小时前
2026 年自托管 Wiki 推荐:为什么选择 PowerWiki
人工智能·log4j·blog·wiki
Sui_Network2 小时前
智能体支付时代:Sui 为 AI 构建可验证的金融基础设施
大数据·人工智能·游戏·金融·rpc·区块链·量子计算