首页 > 其他分享 >《OpenCV计算机视觉》—— 身份证号码识别案例

《OpenCV计算机视觉》—— 身份证号码识别案例

时间:2024-09-12 22:21:21浏览次数:14  
标签:数字 号码 show img cv2 OpenCV 身份证 cv 模板

文章目录

一、案例实现的整体思路

  • 下面是一个数字0~9的模板图片
    在这里插入图片描述
  • 案例身份证如下:
    在这里插入图片描述
  • 对数字模板的处理
    • 通过对模板中的数字进行定位处理,将每个数字的轮廓和外接矩形都一一对应,并由小到大的排序
    • 再将每一个数字都对应一个模板,并设置成相同的大小,用于对身份证号码进行匹配并识别
  • 对身份证的处理
    • 确定出身份证中信息部分的轮廓,确定出每个部分的外接矩形,通过外接矩形的坐标关系确定出身份证号码区域
    • 对身份证号码区域的数字与模板数字做相同的处理
    • 最后将处理后的模板数字与处理后的身份证号码区域的数字进行模板匹配,识别出对应的号码数字

二、代码实现

1.首先定义两个函数

  • def cv_show()用于绘图展示

  • def sort_contours()用于对模板数字的排序

    """ 绘图展示函数 """
    def cv_show(name, img):
        cv2.imshow(name, img)
        cv2.waitKey(0)
    """ 用于对模板数字的排序的函数 """
    # sort_contours() 函数传入的参数:
    # cnts:包含所有数字轮廓的列表
    # method='left-to-right':排序的反向
    # cv2.boundingRect() 函数用于绘制轮廓的最小外接矩形,
    # 返回一个包含四个值的元组:(x, y, w, h),分别代表边界框左上角的x坐标、y坐标、宽度和高度
    # 通过每个数字外接接矩形框的左上角点的x和y坐标的大小,对每个模板数字进行排序
    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]
        (cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
                                            key=lambda b: b[1][i], reverse=reverse))
        # zip(*...)使用星号操作符解包排序后的元组列表,并将其重新组合成两个列表:一个包含所有轮廓,另一个包含所有边界框。
        # 返回梳理轮廓,和外接矩形
        return cnts, boundingBoxes
    

2.模板图像中数字的定位处理

  • 代码如下:
    # 读取模板图片
    img = cv2.imread('template.png')
    cv_show('img', img)
    # 转换为灰度图
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换为灰度图
    cv_show('gray', gray)
    # 转换为二值化图
    ref = cv2.threshold(gray, 155, 255, cv2.THRESH_BINARY_INV)[1]  # 再转换为二值图像
    cv_show('ref', ref)
    
    # 计算轮廓: cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图)
    # cv2.RETR_EXTERNAL 只检测外轮廓,cv2.CHAIN_APPROX_SIMPLE 只保留终点坐标
    _, refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cv2.drawContours(img, refCnts, -1, (0, 0, 255), 2)
    cv_show('img', img)
    
    refCnts = sort_contours(refCnts, method="left-to-right")[0]  # 排序 ,从左到右,从上到下
    digits = {}  # 保存模板中每个数字对应的像素值
    for (i, c) in enumerate(refCnts):  # 遍历每一个轮廓
        # 计算外接矩形并且resize成合适大小
        (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))  # 缩放到指定的大小
        # cv2.bitwise_not() 位非操作:反转图像中每个像素的位值,即将白色变为黑色,黑色变为白色,
        # 对于灰度图像,较亮的像素会变暗,较暗的像素会变亮。
        roi = cv2.bitwise_not(roi)
        cv_show('roi', roi)
        digits[i] = roi  # 每一个数字对应每一个模板
    
  • 结果如下:
    在这里插入图片描述
    • 处理后的每一个数字模板如下所示
      在这里插入图片描述

3.身份证号码数字的定位处理

  • 代码如下:

    # 读取身份证照片
    image = cv2.imread('sfz.jpg')
    cv_show('image', image)
    # 转换为灰度图
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    cv_show('gray', gray)
    # 转换为二值图
    ref = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY_INV)[1]
    cv_show('ref', ref)
    
    # 计算轮廓
    t_, threshCnts, h = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = threshCnts
    cur_img = image.copy()
    # 画出轮廓
    cv2.drawContours(cur_img, cnts, -1, (0, 0, 255), 2)
    cv_show('img', cur_img)
    
    # 遍历轮廓,找到数字部分像素区域
    locs = []
    for (i, c) in enumerate(cnts):
        # 算出所有轮廓的外接矩形
        (x, y, w, h) = cv2.boundingRect(c)
        # 通过每个号码数字外接矩形的y轴坐标的大小,和x轴坐标的大小来确定号码数字的区域
        if (y > 330 and y < 360) and x > 220:
            locs.append((x, y, w, h))   # 将符合的数字轮廓信息都添加到locs列表中
    """
    因为经过cv2.boundingRect() 外接矩形框后的数字顺序是乱的
    通过每个数字外接矩形框的左上角顶点的x坐标的大小进行重新排序
    恢复到原身份证号码的数字顺序
    """
    locs = sorted(locs, key=lambda x: x[0])
    
    # 将身份证号码数字进行与模板数字相同的操作
    output = []
    for (i, (gX, gY, gW, gH)) in enumerate(locs):
        group = gray[gY - 2:gY + gH + 2, gX - 2:gX + gW + 2]
        cv_show('group', group)
        # 预处理
        group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
        cv_show('group', group)
        # 将每个数字都设置成与数字模板中每个数字的大小相同
        roi = cv2.resize(group, (57, 88))
        cv_show('roi', roi)
    
  • 结果如下
    在这里插入图片描述

    • 身份证号码每一个数字处理后的效果如下:
      在这里插入图片描述

4.使用模板匹配,计算匹配得分,找到正确结果

  • 代码如下:

    # 定义scores空列表用于存放所有的匹配得分
        scores = []
        # 定义groupOutput空列表用于存放匹配后的每一个正确的号码数字
        groupOutput = []
        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空列表中添加正确的身份证号码
        output.extend(groupOutput)
    
    # 打印出身份证号码
    print("Credit Card #:{}".format("".join(output)))
    # 显示身份证图片匹配后的结果图
    cv_show("Image", image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  • 结果如下
    在这里插入图片描述
    在这里插入图片描述

  • 完整代码如下:

    import numpy as np
    import cv2
    
    
    def cv_show(name, img):
        cv2.imshow(name, img)
        cv2.waitKey(0)
    
    
    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]
        (cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
                                            key=lambda b: b[1][i], reverse=reverse))
        # zip(*...)使用星号操作符解包排序后的元组列表,并将其重新组合成两个列表:一个包含所有轮廓,另一个包含所有边界框。
        return cnts, boundingBoxes
    
    
    """------模板图像中数字的定位处理------"""
    # 读取模板图片
    img = cv2.imread('template.png')
    cv_show('img', img)
    # 转换为灰度图
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转换为灰度图
    cv_show('gray', gray)
    # 转换为二值化图
    ref = cv2.threshold(gray, 155, 255, cv2.THRESH_BINARY_INV)[1]  # 再转换为二值图像
    cv_show('ref', ref)
    
    # 计算轮廓: cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图)
    # cv2.RETR_EXTERNAL 只检测外轮廓,cv2.CHAIN_APPROX_SIMPLE 只保留终点坐标
    _, refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    # 画出轮廓
    cv2.drawContours(img, refCnts, -1, (0, 0, 255), 2)
    cv_show('img', img)
    
    refCnts = sort_contours(refCnts, method="left-to-right")[0]  # 排序 ,从左到右,从上到下
    digits = {}  # 保存模板中每个数字对应的像素值
    for (i, c) in enumerate(refCnts):  # 遍历每一个轮廓
        # 计算外接矩形并且resize成合适大小
        (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))  # 缩放到指定的大小
        # cv2.bitwise_not() 位非操作:反转图像中每个像素的位值,即将白色变为黑色,黑色变为白色,
        # 对于灰度图像,较亮的像素会变暗,较暗的像素会变亮。
        roi = cv2.bitwise_not(roi)
        cv_show('roi', roi)
        digits[i] = roi  # 每一个数字对应每一个模板
    # cv2.destroyAllWindows()
    
    """ 身份证号码数字的定位处理 """
    # 读取身份证照片
    image = cv2.imread('sfz.jpg')
    cv_show('image', image)
    # 转换为灰度图
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    cv_show('gray', gray)
    # 转换为二值图
    ref = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY_INV)[1]
    cv_show('ref', ref)
    
    # 计算轮廓
    t_, threshCnts, h = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = threshCnts
    cur_img = image.copy()
    # 画出轮廓
    cv2.drawContours(cur_img, cnts, -1, (0, 0, 255), 2)
    cv_show('img', cur_img)
    
    # 遍历轮廓,找到数字部分像素区域
    locs = []
    for (i, c) in enumerate(cnts):
        # 算出所有轮廓的外接矩形
        (x, y, w, h) = cv2.boundingRect(c)
        # 通过每个号码数字外接矩形的y轴坐标的大小,和x轴坐标的大小来确定号码数字的区域
        if (y > 330 and y < 360) and x > 220:
            locs.append((x, y, w, h))   # 将符合的数字轮廓信息都添加到locs列表中
    """
    因为经过cv2.boundingRect() 外接矩形框后的数字顺序是乱的
    通过每个数字外接矩形框的左上角顶点的x坐标的大小进行重新排序
    恢复到原身份证号码的数字顺序
    """
    locs = sorted(locs, key=lambda x: x[0])
    
    # 将身份证号码数字进行与模板数字相同的操作
    output = []
    for (i, (gX, gY, gW, gH)) in enumerate(locs):
        group = gray[gY - 2:gY + gH + 2, gX - 2:gX + gW + 2]
        cv_show('group', group)
        # 预处理
        group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
        cv_show('group', group)
        # 将每个数字都设置成与数字模板中每个数字的大小相同
        roi = cv2.resize(group, (57, 88))
        cv_show('roi', roi)
    
        ''' 使用模板匹配,计算匹配得分,找到正确结果 '''
        # 定义scores空列表用于存放所有的匹配得分
        scores = []
        # 定义groupOutput空列表用于存放匹配后的每一个正确的号码数字
        groupOutput = []
        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空列表中添加正确的身份证号码
        output.extend(groupOutput)
    
    # 打印出身份证号码
    print("Credit Card #:{}".format("".join(output)))
    # 显示身份证图片匹配后的结果图
    cv_show("Image", image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

标签:数字,号码,show,img,cv2,OpenCV,身份证,cv,模板
From: https://blog.csdn.net/weixin_73504499/article/details/142141035

相关文章

  • java+opencv4来获取图像中轮廓的最小外接矩形
     举例:获取以下图片中的火车的最小外接矩形完成钱确认opencv的环境配置完整。要想查找图片中的轮廓信息,首先要获取图片的二制图,因为二制图的查找效率更高,具体原因自行百度。为了提高转换二制图的效率可以现将图片转换为灰度图。示例代码如下://将彩色图像转换为灰度图像M......
  • OpenCV(cv::drawContours())
    目录1.函数原型2.示例1.函数原型cv::drawContours()用于在图像上绘制轮廓。函数原型:voidcv::drawContours(cv::InputOutputArrayimage,conststd::vector<std::vector<cv::Point>>&contours,intcontourIdx,constc......
  • OpenCV(cv::getStructuringElement())
    目录1.函数原型2.示例3.使用场景1.函数原型cv::getStructuringElement()是OpenCV中一个用于生成结构元素的函数,结构元素在形态学操作(如膨胀、腐蚀等)中扮演了重要角色。cv::Matcv::getStructuringElement(intshape,cv::Sizeksize,cv::Pointanchor=cv::Point(-1......
  • python+opencv图片文字旋转矫正
    最近在使用实在RPA做机器人自动化,功能是受理单核对,即对核对业务受理人是否上传受理单承诺书方法很简单,由于系统中图片位置不固定,所以需要将所有附件进行下载,并进行图像文字识别,但是实在RPA中的OCR识别无法识别颠倒倾斜的图片,所以有两种方法,一种是使用其他OCR模型,一种是将图片旋转......
  • 实战OpenCV之像素操作
    基础入门        在OpenCV中,像素是最基本的操作单位。图像可以视为一个三维数组,其中第三维表示颜色通道。图像数据在内存中以连续或几乎连续的方式存储,对于多通道图像(比如:BGR图像),每个像素的各通道值紧密排列。OpenCV主要使用BGR色彩空间,与常用的RGB顺序不同。因此,在进......
  • 机器学习:opencv--图像金字塔
    目录一、图像金字塔1.图像金字塔是什么?2.有哪些常见类型?3.金字塔的构建过程4.图像金字塔的作用二、图像金字塔中的操作1.向下采样2.向上采样3.注意--无法复原三、代码实现1.高斯金字塔向下采样2.高斯金字塔向上采样3.无法复原4.拉普拉斯金字塔一、图像金字塔......
  • python身份证二要素、三要素实名认证接口调用示例
    身份证二要素、三要素实名认证接口,实时快速核实身份信息是否真实有效,根据姓名、身份证号码核对身份信息是否一致,核验结果实时返回,不限性别、不限年龄,各类网站程序和APP均可接入。接口介绍:实时快速核实身份信息是否真实有效,不限性别、不限年龄。更新时间:实时文件......
  • p'ython语言调用身份证实名认证接口示例
    身份证实名认证接口一般可用于金融、保险、在线教育、直播、新零售、网络游戏、电商、租赁、物流、旅游等需要实名注册、实名认证的场景。身份证实名认证接口核验姓名、身份证号、证件人像等一系列要素信息与权威数据是否匹配一致,从而验证身份证的真实性,可与翔云设法嫩正......
  • OpenCV结构分析与形状描述符(19)查找二维点集的最小面积外接旋转矩形函数minAreaRect()
    操作系统:ubuntu22.04OpenCV版本:OpenCV4.9IDE:VisualStudioCode编程语言:C++11算法描述找到一个包围输入的二维点集的最小面积旋转矩形。该函数计算并返回指定点集的最小面积边界矩形(可能是旋转的)。开发者需要注意的是,当数据接近包含的Mat元素边界时,返回的Rotated......
  • OpenCV结构分析与形状描述符(20)计算一个包围给定点集的最小外接圆函数minEnclosingCirc
    操作系统:ubuntu22.04OpenCV版本:OpenCV4.9IDE:VisualStudioCode编程语言:C++11算法描述找到一个包围二维点集的最小面积的圆。该函数使用迭代算法来寻找一个二维点集的最小外接圆。这意味着函数将会通过反复逼近的过程来计算出能够包围所有给定点且面积最小的圆。mi......