身份证号识别案例
代码实现:
import cv2
import numpy as np
import argparse
import myutils
'''
-i shenfenzheng.jpg
-t moban2.png
'''
ap= argparse.ArgumentParser()
ap.add_argument("-i","--image", required=True,help="path to input image")
ap.add_argument("-t","--template", required=True,help="path to template 0cR-A image")
args = vars(ap.parse_args())def cv_show(name,img):cv2.imshow(name,img)cv2.waitKey(0)''' --------模板图像中数字的定位处理----------- '''
img = cv2.imread(args["template"])
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)
_,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)
refCnts = myutils.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:y + h, x:x + w]roi =cv2.resize(roi,(57,88))cv_show('ro',roi)digits[i]= roi
print(digits)
''' --------身份证的图像处理----------- '''
img=cv2.imread(args["image"])
cv_show("img", img)
img=myutils.resize(img,width=600)
cv_show("resize", img)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv_show("gray", gray)
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))
squareKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
tophat=cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel)
#
cv_show("tophat", tophat)
closeX=cv2.morphologyEx(gray,cv2.MORPH_CLOSE,rectKernel)
cv_show("closeX", closeX)
# #
thresh111=cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
cv_show("thresh", thresh111)#
thresh=cv2.morphologyEx(thresh111,cv2.MORPH_CLOSE,squareKernel)
cv_show("thresh1", thresh)
#
_,threshCnts,h=cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts=threshCnts
cur_img=img.copy()
cv2.drawContours(cur_img,cnts,-1,(0,0,255),3)
# cv_show("cur_img", cur_img)
locs=[]
for (i, cnt) in enumerate(cnts):(x, y, w, h) = cv2.boundingRect(cnt)ar=w/float(h)if y>310 and y<320:if x>210:locs.append((x, y, w, h))
locs = sorted(locs , key=lambda x: x[0])
print(locs)
output = []
# 遍历每一个轮廓中的数字
for (x,y,w,h) in locs:roi = thresh111[y:y + h, x:x + w]roi = cv2.resize(roi, (57, 88))'''--------使用模板匹配,计算匹配得分-----------'''scores = []# 在模板中计算每一个得分for (digit, digitROI) in digits.items():result = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF)(_, score, _, _) = cv2.minMaxLoc(result)scores.append(score)output.append(str(np.argmax(scores)))# 画出来cv2.rectangle(img, (x - 5, y - 5), (x + w + 5, y + h + 5), (0, 0, 255), 1)# cv2.putText()是OpenCV库中的一个函数,用于在图像上添加文本。cv2.putText(img, str(np.argmax(scores)), (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)# 得到结果 将一个列表的元素添加到另一个列表的末尾。
# # 打印结果print("shenfenzhenghao #: {}".format("".join(output)))
cv2.imshow("Image", img)
cv2.waitKey(0)
这段代码是一个基于 OpenCV 的身份证号码识别程序,核心逻辑是通过模板匹配识别身份证上的数字。整体流程分为两大部分:模板图像预处理(提取数字模板)和身份证图像预处理 + 数字识别。下面分步骤解析:
一、初始化与参数配置
import cv2
import numpy as np
import argparse
import myutils # 自定义工具函数(含之前提到的resize、sort_contours等)# 解析命令行参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="身份证图像路径")
ap.add_argument("-t", "--template", required=True, help="数字模板图像路径")
args = vars(ap.parse_args()) # 将参数转为字典格式# 自定义图像显示函数(封装imshow+waitKey,方便调试)
def cv_show(name, img):cv2.imshow(name, img)cv2.waitKey(0) # 等待按键输入后关闭窗口
作用:导入依赖库,通过命令行接收输入图像(身份证)和模板图像的路径,定义便捷的图像显示函数。
使用时需通过命令行传入参数,例如:python script.py -i shenfenzheng.jpg -t moban2.png
二、模板图像处理(提取数字模板)
这部分的目的是从模板图像中提取 0-9 的数字轮廓,作为后续识别的 "标准模板"。
# 读取模板图像
img = cv2.imread(args["template"])
cv_show('img', img) # 显示原始模板图像# 转为灰度图(去除颜色干扰)
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv_show('ref', ref)# 二值化处理(THRESH_BINARY_INV:黑底白字,数字为白色,背景为黑色)
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]
cv_show('ref', ref)# 查找模板中的轮廓(数字的边缘)
_, refCnts, hierarchy = cv2.findContours(ref, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 在原图上绘制轮廓(红色,线宽3),直观查看模板中的数字轮廓
cv2.drawContours(img, refCnts, -1, (0, 0, 255), 3)
cv_show('img', img)# 按从左到右排序轮廓(确保模板数字顺序为0-9)
refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0]# 存储数字模板(键:数字0-9,值:对应的图像ROI)
digits = {}
for (i, c) in enumerate(refCnts):# 获取轮廓的边界矩形(x,y:左上角坐标;w,h:宽高)(x, y, w, h) = cv2.boundingRect(c)# 裁剪数字区域(ROI)roi = ref[y:y + h, x:x + w]# 统一调整大小为(57,88)(与后续身份证数字区域尺寸一致,便于匹配)roi = cv2.resize(roi, (57, 88))cv_show('ro', roi) # 显示单个数字模板digits[i] = roi # 存储:digits[0]对应数字0的模板,digits[1]对应数字1,以此类推print(digits) # 打印模板字典(调试用)
核心逻辑:通过预处理(灰度化、二值化)突出模板中的数字,提取数字轮廓并排序,最终得到 0-9 的标准数字模板,用于后续匹配。
三、身份证图像处理(定位并识别号码)
这部分是核心,通过预处理定位身份证号码区域,再用模板匹配识别每个数字。
# 读取身份证图像
img = cv2.imread(args["image"])
cv_show("img", img) # 显示原始身份证图像# 调整图像大小(宽度600,保持比例),统一尺寸便于处理
img = myutils.resize(img, width=600)
cv_show("resize", img)# 转为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv_show("gray", gray)# 定义形态学操作的结构元素(用于增强数字特征)
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3)) # 9x3矩形(适合水平方向特征)
squareKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 5x5正方形(适合填充空洞)# 顶帽变换(突出比背景亮的区域,如身份证上的黑色数字在浅色背景上)
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
cv_show("tophat", tophat)# 闭运算(连接相邻的数字区域,让数字轮廓更完整)
closeX = cv2.morphologyEx(gray, cv2.MORPH_CLOSE, rectKernel)
cv_show("closeX", closeX)# 二值化处理(THRESH_BINARY_INV:数字为白色,背景为黑色;OTSU自动计算阈值)
thresh111 = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
cv_show("thresh", thresh111)# 二次闭运算(填充数字内部的小空洞,优化轮廓)
thresh = cv2.morphologyEx(thresh111, cv2.MORPH_CLOSE, squareKernel)
cv_show("thresh1", thresh)# 查找二值化图像中的轮廓(数字区域的边缘)
_, threshCnts, h = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = threshCnts # 保存所有轮廓# 在原图上绘制所有轮廓(红色,线宽3),查看轮廓检测效果
cur_img = img.copy()
cv2.drawContours(cur_img, cnts, -1, (0, 0, 255), 3)
# cv_show("cur_img", cur_img) # 可选:显示带轮廓的图像# 筛选身份证号码所在的轮廓(根据位置特征)
locs = [] # 存储符合条件的轮廓位置
for (i, cnt) in enumerate(cnts):(x, y, w, h) = cv2.boundingRect(cnt) # 获取轮廓的边界矩形# 筛选条件:根据身份证号码的大致位置(y在310-320之间,x>210)# 注:这是针对特定身份证图像的经验值,不同图像可能需要调整if y > 310 and y < 320:if x > 210:locs.append((x, y, w, h)) # 保存符合条件的轮廓位置# 按x坐标排序(确保数字顺序从左到右,与实际号码一致)
locs = sorted(locs, key=lambda x: x[0])
print(locs) # 打印筛选后的轮廓位置(调试用)
核心逻辑:通过一系列预处理( resize、灰度化、形态学操作、二值化 )增强身份证上的数字特征,再根据号码的大致位置筛选出目标轮廓,为后续识别做准备。
注意:y>310 and y<320
和x>210
是硬编码的位置条件,依赖于输入图像的尺寸和角度,实际使用时可能需要根据身份证图像调整。
四、模板匹配识别数字
output = [] # 存储识别结果# 遍历每个筛选出的数字区域
for (x, y, w, h) in locs:# 裁剪数字区域的ROI(从二值化图像中提取)roi = thresh111[y:y + h, x:x + w]# 调整尺寸为(57,88)(与模板大小一致,确保匹配准确性)roi = cv2.resize(roi, (57, 88))# 模板匹配:计算当前ROI与每个数字模板的匹配得分scores = []for (digit, digitROI) in digits.items():# 使用TM_CCOEFF方法进行模板匹配(得分越高,匹配度越高)result = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF)(_, score, _, _) = cv2.minMaxLoc(result) # 获取最高得分scores.append(score) # 存储与每个数字模板的匹配得分# 取得分最高的模板对应的数字作为识别结果output.append(str(np.argmax(scores)))# 在原图上绘制结果:画矩形框住数字,并标注识别出的数字cv2.rectangle(img, (x - 5, y - 5), (x + w + 5, y + h + 5), (0, 0, 255), 1) # 红色边框cv2.putText(img, str(np.argmax(scores)), (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2) # 红色文本# 输出最终识别结果
print("身份证号码: {}".format("".join(output)))# 显示标注后的身份证图像
cv2.imshow("Image", img)
cv2.waitKey(0)
核心逻辑:对每个筛选出的数字区域,与之前提取的 0-9 模板进行匹配,通过比较匹配得分确定数字内容,最后将结果绘制在原图上并输出。
总结
整个程序的流程是:
- 处理模板图像,提取 0-9 的数字模板;
- 预处理身份证图像,通过形态学操作和二值化增强数字特征;
- 根据位置特征筛选出身份证号码所在的轮廓;
- 用模板匹配识别每个数字,输出最终结果。
注意事项:位置筛选条件(y 和 x 的范围)是经验值,需根据实际身份证图像调整;
模板匹配的准确性依赖于预处理效果(数字轮廓是否清晰)和模板质量;
若身份证图像有倾斜,需先进行矫正,否则可能导致轮廓筛选失败。