OpenCV 实战:基于模板匹配的身份证号自动识别系统
目录
项目概述
一、核心工具函数实现
二、模板图像预处理
1. 模板图像二值化处理
2. 提取并排序数字轮廓
三、身份证号识别流程
1. 身份证图像预处理
2. 定位身份证号区域
3. 提取并识别每个数字
五、总结
在计算机视觉领域,证件信息的自动识别是一个非常实用的技术方向。本文将详细介绍如何使用 OpenCV 库实现身份证号的自动识别系统,通过模板匹配的方法精准提取身份证上的数字信息。
项目概述
本项目旨在通过计算机视觉技术,自动识别身份证图片中的身份证号码。系统主要分为三个核心模块:自定义工具函数、模板图像预处理、身份证号定位与识别。整个流程基于 OpenCV 库实现,不需要复杂的深度学习框架,适合入门级学习者理解和实践。
技术栈:
- Python 3.x
- OpenCV (cv2) 库
- NumPy 库
一、核心工具函数实现
首先,我们需要实现两个核心工具函数:图像展示函数和轮廓排序函数,这两个函数将在整个项目中反复使用。
import cv2
import numpy as npdef cv_show(name, image):"""展示图像的通用函数:param name: 窗口名称:param image: 要展示的图像"""cv2.imshow(name, image)cv2.waitKey(0) # 等待按键输入cv2.destroyWindow(name) # 关闭当前窗口def sort_contours(cnts, method='left-to-right'):"""对轮廓进行排序:param cnts: 轮廓列表:param method: 排序方式,可选值:'left-to-right'、'right-to-left'、'top-to-bottom'、'bottom-to-top':return: 排序后的轮廓列表和对应的边界框列表"""reverse = False # 排序方向标识i = 0 # 排序依据的维度# 根据排序方式调整参数if method == 'right-to-left' or method == 'bottom-to-top':reverse = Trueif method == 'top-to-bottom' or method == 'bottom-to-top':i = 1 # 按y坐标排序# 计算每个轮廓的边界框并排序bounding_boxes = [cv2.boundingRect(c) for c in cnts](cnts, bounding_boxes) = zip(*sorted(zip(cnts, bounding_boxes),key=lambda b: b[1][i], reverse=reverse))return cnts, bounding_boxes
函数说明:
cv_show()
:封装了 OpenCV 的图像显示功能,自动等待用户按键并关闭窗口sort_contours()
:根据指定方式对轮廓进行排序,这在处理数字序列时非常重要,确保数字顺序正确
二、模板图像预处理
模板匹配的核心是先建立数字模板库,我们需要从包含 0-9 数字的模板图像中提取每个数字的特征。
模版如下:
1. 模板图像二值化处理
# 读取模板图像
img = cv2.imread("shuzi.png")
cv_show('模板原图', img)# 转换为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv_show('模板灰度图', gray)# 二值化处理(反色处理,使数字为白色,背景为黑色)
ref = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY_INV)[1]
cv_show('模板二值化图', ref)
运行结果如下:
处理说明:
- 首先读取包含 0-9 数字的模板图像
- 转换为灰度图以简化处理
- 应用反相二值化,将数字变为白色 (255),背景变为黑色 (0),便于后续轮廓检测
2. 提取并排序数字轮廓
# 寻找轮廓
_, refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 在原图上绘制轮廓
cv2.drawContours(img, refCnts, -1, (0, 255, 0), 2)
cv_show('带轮廓的模板图', img)# 按从左到右顺序排序轮廓
refCnts = sort_contours(refCnts, method='left-to-right')[0]# 存储每个数字的模板
digits = {}# 遍历每个轮廓,提取数字并预处理
for (i, c) in enumerate(refCnts):# 计算轮廓的边界框(x, y, w, h) = cv2.boundingRect(c)# 裁剪数字区域(适当扩展边界)roi = ref[y-2:y+h+2, x-2:x+w+2]# 调整为统一尺寸roi = cv2.resize(roi, (57, 88))# 按位取反,使数字为黑色,背景为白色roi = cv2.bitwise_not(roi)# 显示每个提取的数字cv_show(f'数字{i}', roi)# 存储到字典中digits[i] = roicv2.destroyAllWindows()
处理说明:
- 使用
cv2.findContours()
函数检测数字轮廓,只检测外轮廓 - 对轮廓进行从左到右排序,确保数字顺序正确
- 裁剪每个数字区域并调整为统一尺寸 (57x88),便于后续匹配
- 将处理好的数字模板存储在字典中,建立索引与数字的对应关系
三、身份证号识别流程
1. 身份证图像预处理
# 读取身份证图像
img = cv2.imread('./shenfen.jpg')
imgg = img.copy() # 保存副本用于后续绘制结果
cv_show('身份证原图', img)# 转换为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv_show('身份证灰度图', gray)# 二值化处理
ref = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY_INV)[1]
cv_show('身份证二值化图', ref)
2. 定位身份证号区域
# 寻找身份证图像中的所有轮廓
_, refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 绘制所有轮廓,观察轮廓检测效果
a = cv2.drawContours(img.copy(), refCnts, -1, (0, 255, 0), 2)
cv_show('带轮廓的身份证', a)# 筛选身份证号区域的轮廓
locs = []
for (i, c) in enumerate(refCnts):# 计算轮廓的边界框(x, y, w, h) = cv2.boundingRect(c)# 根据位置筛选身份证号区域(根据实际图像调整坐标范围)# 注意:不同身份证图像的坐标范围可能不同,需要根据实际情况调整if (y > 330 and y < 360) and x > 220:locs.append((x, y, w, h))# 按x坐标排序,确保数字顺序正确
locs = sorted(locs, key=lambda x: x[0])
定位说明:
- 首先检测身份证图像中的所有外轮廓
- 根据身份证号的大致位置(通常在身份证下方)设置坐标筛选条件
- 对筛选出的轮廓按 x 坐标排序,确保数字从左到右排列
3. 提取并识别每个数字
output = []# 遍历每个数字的位置信息
for (i, (gX, gY, gW, gH)) in enumerate(locs):groupOutput = []# 提取数字区域(适当扩展边界)group = gray[gY-2:gY+gH+2, gX-2:gX+gW+2]cv_show(f'数字区域{i}', group)# 二值化处理(使用OTSU自动阈值)group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]cv_show(f'二值化数字{i}', group)# 调整为与模板相同的尺寸roi = cv2.resize(group, (57, 88))cv_show(f'标准化数字{i}', 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(imgg, (gX-5, gY-5), (gX+gW+5, gY+gH+5), (0, 0, 255), 1)cv2.putText(imgg, "".join(groupOutput), (gX, gY-15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)output.extend(groupOutput)# 显示最终结果
print("识别到的身份证号: {}".format("".join(output)))
cv_show("识别结果", imgg)
cv2.destroyAllWindows()
识别说明:
- 逐个提取身份证号区域的每个数字
- 对每个数字进行二值化和尺寸标准化,使其与模板保持一致
- 使用
cv2.matchTemplate()
进行模板匹配,计算与每个数字模板的匹配得分 - 选择得分最高的数字作为识别结果,并在原图上标记
- 最终将所有识别结果拼接成完整的身份证号
五、总结
本项目通过 OpenCV 实现了基于模板匹配的身份证号识别系统,主要流程包括:
- 自定义工具函数辅助图像处理
- 从模板图像中提取数字特征并建立模板库
- 对身份证图像进行预处理和轮廓分析
- 定位并提取身份证号区域的每个数字
- 通过模板匹配识别数字并输出结果
该方法不需要复杂的深度学习模型,实现简单且易于理解,适合作为计算机视觉入门实践项目。对于精度要求更高的场景,可以考虑结合深度学习方法(如 CNN)进行优化。