当前位置: 首页 > news >正文

计算机视觉(opencv)——基于模板匹配的身份证号识别系统


案例教学:基于 OpenCV 的身份证号识别系统

在计算机视觉的应用中,OCR(光学字符识别)是一个非常常见的任务。本案例展示了如何利用 OpenCV + 模板匹配 来识别信用卡上的卡号。整个流程分为 模板建立目标图像处理 两大部分。


图片准备:

运行结果:


一、导入库与准备工作

import numpy as np
import cv2
import myutils
  • cv2:OpenCV 核心库,主要用于图像处理。

  • numpy:矩阵运算库,图像在内存中就是一个矩阵。

  • myutils:自定义工具模块,包含 sort_contours(轮廓排序)等常用函数。

自建模块:myutils.py

import cv2"""
myutils.py - 自定义工具函数模块
包含轮廓排序和图像缩放两个常用功能,
用于银行卡号识别系统中的图像处理
"""
def sort_contours(cnts, method='left-to-right'):"""对轮廓进行排序(按指定方向)参数:cnts: 轮廓列表,由cv2.findContours()返回method: 排序方法,可选值包括:'left-to-right' (默认) - 从左到右'right-to-left' - 从右到左'top-to-bottom' - 从上到下'bottom-to-top' - 从下到上返回:排序后的轮廓列表和对应的边界框列表"""# 初始化排序方向标志和排序依据索引reverse = False  # 是否反向排序i = 0  # 排序依据的维度索引(0表示x轴,1表示y轴)# 确定是否需要反向排序if method == 'right-to-left' or method == 'bottom-to-top':reverse = True# 确定排序依据是x轴还是y轴# 垂直方向排序(上下)用y坐标,水平方向排序(左右)用x坐标if method == 'top-to-bottom' or method == 'bottom-to-top':i = 1  # 使用y坐标排序# 为每个轮廓计算边界框(x, y, w, h)boundingBoxes = [cv2.boundingRect(c) for c in cnts]# 将轮廓与对应的边界框组合,按指定维度排序后再拆分# sorted()的key参数指定按边界框的第i个值(x或y)排序(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),key=lambda b: b[1][i],  # b[1]是边界框,b[1][i]是x或y坐标reverse=reverse))return cnts, boundingBoxesdef resize(image, width=None, height=None, inter=cv2.INTER_AREA):"""按比例缩放图像(保持原图宽高比)参数:image: 输入图像width: 目标宽度(若为None则按height计算)height: 目标高度(若为None则按width计算)inter: 插值方法,默认cv2.INTER_AREA(适合缩小图像)返回:缩放后的图像"""# 初始化目标尺寸dim = None# 获取原图高度和宽度(h, w) = image.shape[:2]  # shape[:2]取前两个值(高度、宽度),忽略通道数# 若宽高均未指定,则返回原图if width is None and height is None:return image# 若未指定宽度,则按高度计算缩放比例if 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, interpolation=inter)return resized

接着定义信用卡类型字典,用来根据首位数字判断卡种:

FIRST_NUMBER = {"3": "American Express","4": "Visa","5": "MasterCard","6": "Discover Card"}

辅助函数 cv_show 用于快速显示图像:

def cv_show(name, img):  # 绘图展示cv2.imshow(name, img)cv2.waitKey(0)

二、模板图像中数字的定位处理

识别卡号之前,需要先有一份 数字模板,作为对比参考。

"""----------模板图像中数字的定位处理----------"""
img = cv2.imread("kahao.png")
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压缩水平垂直
_, refCnts, hierarchy = cv2.findContours(ref, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, refCnts, -1, (0, 0, 255), 3)
cv_show('img', img)

然后按从左到右排序,依次提取每个数字,统一缩放到 (57, 88) 的大小:

refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0]  # 排序,从左到右,从上到下
digits = {}  # 保存每一个数字对应的模板
for (i, c) in enumerate(refCnts):  # 遍历每一个轮廓(x, y, w, h) = cv2.boundingRect(c)  # 计算外接矩形并且resize成合适大小roi = cv2.resize(ref[y:y + h, x:x + w], (57, 88))  # 缩放成指定的大小cv_show('roi', roi)digits[i] = roi  # 每一个数字对应每一个模板
print(digits)

此时,digits 就是一个数字模板库,包含 0-9 的标准图案


三、身份证图像的处理

接下来处理实际的信用卡图片 card1.jpg

"""----------身份证的图像处理----------"""
image = cv2.imread("card1.jpg")
cv_show('image', image)
# image = myutils.resize(image, width=300)  # 设置图像的大小
image = cv2.resize(image,(640,400))
print(image.shape)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv_show('gray', gray)

使用 顶帽操作 突出明亮的数字区域:

# 顶帽操作,突出更明亮的区域,消除背景元素,原因是谱系图下变化小,不被腐蚀掉。
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 5))  # 初始化卷积核
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)  # 顶帽 = 原始图像 - 开运算结果(来增强亮部区域)
cv_show('tophat', tophat)

四、找到卡号所在区域

  1. 闭操作 让数字连成一片。

  2. 二值化 + OTSU 自动分割前景和背景。

  3. 再闭操作 去掉细小噪声。

  4. 轮廓提取 找到数字所在的大区域。

"""-----------找到数字边框-----------"""
# 1、通过闭操作(先膨胀,再腐蚀)将数字连在一起
closeX = cv2.morphologyEx(tophat, cv2.MORPH_CLOSE, rectKernel)
cv_show('gradX', closeX)
# 2、THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
thresh = cv2.threshold(closeX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show('thresh', thresh)
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)  # 再来一个闭操作
cv_show('thresh1', thresh)
# 3、计算轮廓
_, threshCnts, h = 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)

遍历这些轮廓,筛选出长宽比合适的区域作为卡号段:

# 4、遍历轮廓,找到数字部分轮廓区域
locs = []
for (i, c) in enumerate(cnts):(x, y, w, h) = cv2.boundingRect(c)  # 计算外接矩形ar = w / float(h)# 选择合适的区域,根据实际任务来,if ar > 16 and ar < 20:# if (w > 100 and w < 400) and (h > 3 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):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]# print(group.shape)# group = cv2.resize(group, (300, 20))# cv_show('group', group)# 颜色反转group = cv2.bitwise_not(group)cv_show('group', group)# 计算每一组的轮廓group_, digitCnts, hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)digitCnts = myutils.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)))# 画出来cv2.rectangle(image, (gX - 5, gY - 5), (gX + gW + 5, gY + gH + 5), (0, 0, 255), 1)# cv2.putText()是OpenCV库中的一个函数,用于在图像上添加文本。cv2.putText(image, "".join(groupOutput), (gX, gY - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)output.extend(groupOutput)  # 得到结果 将一个列表的元素添加到另一个列表的末尾。

六、最终结果输出

根据卡号首位判断卡种,并输出卡号:

print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imwrite('card_result.jpg', image)
cv2.imshow("result", image)
cv2.waitKey(0)

最终结果会在窗口显示,同时保存为 card_result.jpg


七、案例总结

  1. 模板构建:利用标准数字图像,建立模板库。

  2. 图像预处理:灰度化 → 顶帽操作 → 闭运算 → 二值化,得到清晰的卡号区域。

  3. 区域筛选:根据宽高比和大小,精确定位卡号段。

  4. 逐位识别:通过模板匹配,识别每一位数字。

  5. 结果展示:输出卡种 + 卡号,并可视化标注。

这个流程思路清晰,适合入门 OCR 与模板匹配 的学习案例。


文章转载自:

http://CRgzITuv.mkhwx.cn
http://O9281dqY.mkhwx.cn
http://NYnvXMhE.mkhwx.cn
http://jusWH23i.mkhwx.cn
http://UDvKdX5B.mkhwx.cn
http://wgpSz5OV.mkhwx.cn
http://vgCsov1M.mkhwx.cn
http://fLo60ZgZ.mkhwx.cn
http://LrnER57A.mkhwx.cn
http://G2tNcpCV.mkhwx.cn
http://EjIk13Ak.mkhwx.cn
http://VpSV6Y3g.mkhwx.cn
http://dTDbwdIN.mkhwx.cn
http://O83aInMq.mkhwx.cn
http://sE3nU6H1.mkhwx.cn
http://3cZDyeE0.mkhwx.cn
http://lfGSwvQR.mkhwx.cn
http://c6qW6epN.mkhwx.cn
http://r8K270MH.mkhwx.cn
http://EJsm2BiH.mkhwx.cn
http://t6VLPaom.mkhwx.cn
http://Wy3BOqFq.mkhwx.cn
http://BSCnpfco.mkhwx.cn
http://PSXbZFpC.mkhwx.cn
http://sSx909U1.mkhwx.cn
http://OGyPFZd6.mkhwx.cn
http://Ajkh7zFQ.mkhwx.cn
http://xE7QvyMS.mkhwx.cn
http://8lIPLTsY.mkhwx.cn
http://wqOpTAJM.mkhwx.cn
http://www.dtcms.com/a/374432.html

相关文章:

  • 腾讯推出AI CLI工具CodeBuddy,国内首家同时支持插件、IDE和CLI三种形态的AI编程工具厂商
  • 前后端联调时出现的一些问题记录
  • 网络编程;套接字;TCP通讯;UDP通讯;0909
  • 最后一公里文件传输难题Localsend+cpolar破解
  • Windows 命令行:cd 命令3,当前目录,父目录,根目录
  • 医疗连续体机器人模块化控制界面设计与Python库应用研究(下)
  • Nginx 优化与防盗链
  • Spring Web 异步响应实战:从 CompletableFuture 到 ResponseBodyEmitter 的全链路优化
  • Linux基础命令使用
  • 第二章、PyTorch 入门笔记:从张量基本操作到线性回归实战
  • 【参数详解与使用指南】PyTorch MNIST数据集加载
  • Ruoyi-vue-plus-5.x第六篇Web开发与前后端交互: 6.4 WebSocket实时通信
  • vlan(局部虚拟网)
  • MissionPlanner架构梳理之(十)-参数编辑器
  • Hadoop Windows客户端配置与实践指南
  • 【NVIDIA-B200】 ‘CUDA driver version is insufficient for CUDA runtime version‘
  • 从源码视角全面解析 Chrome UI 布局系统及 Views 框架的定制化实现方法与实践经验
  • 9.9 ajax的请求和封装
  • CTFshow系列——PHP特性Web101-104
  • MCP学习一——UV安装使用教程
  • 【Java实战㊳】Spring Boot实战:从打包到监控的全链路攻略
  • Go语言实战案例-开发一个Markdown转HTML工具
  • idea、服务器、数据库环境时区不一致问题
  • HarmonyOS 5.1.1版本图片上传功能
  • 2025最新超详细FreeRTOS入门教程:第八章 FreeRTOS任务通知
  • Puter+CPolar低成本替代商业网盘,打造私有云新势力
  • Deepoc科技之暖:智能助盲设备如何为视障家人点亮生活
  • 详细的vmware虚拟机安装教程
  • uni-app 项目中使用自定义字体
  • springboot maven 多环境配置入门与实战