OpenCV 实战:基于模板匹配的身份证号自动识别系统

发布于:2025-09-11 ⋅ 阅读:(16) ⋅ 点赞:(0)

目录

项目概述

一、核心工具函数实现

二、模板图像预处理

1. 模板图像二值化处理

2. 提取并排序数字轮廓

三、身份证号识别流程

1. 身份证图像预处理

2. 定位身份证号区域

3. 提取并识别每个数字

五、总结


在计算机视觉领域,证件信息的自动识别是一个非常实用的技术方向。本文将详细介绍如何使用 OpenCV 库实现身份证号的自动识别系统,通过模板匹配的方法精准提取身份证上的数字信息。

项目概述

本项目旨在通过计算机视觉技术,自动识别身份证图片中的身份证号码。系统主要分为三个核心模块:自定义工具函数、模板图像预处理、身份证号定位与识别。整个流程基于 OpenCV 库实现,不需要复杂的深度学习框架,适合入门级学习者理解和实践。

技术栈:

  • Python 3.x
  • OpenCV (cv2) 库
  • NumPy 库

一、核心工具函数实现

首先,我们需要实现两个核心工具函数:图像展示函数和轮廓排序函数,这两个函数将在整个项目中反复使用。

import cv2
import numpy as np

def 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 = True
    if 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] = roi

cv2.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 实现了基于模板匹配的身份证号识别系统,主要流程包括:

  1. 自定义工具函数辅助图像处理
  2. 从模板图像中提取数字特征并建立模板库
  3. 对身份证图像进行预处理和轮廓分析
  4. 定位并提取身份证号区域的每个数字
  5. 通过模板匹配识别数字并输出结果

该方法不需要复杂的深度学习模型,实现简单且易于理解,适合作为计算机视觉入门实践项目。对于精度要求更高的场景,可以考虑结合深度学习方法(如 CNN)进行优化。


网站公告

今日签到

点亮在社区的每一天
去签到