使用Python+OpenCV进行卡类型及16位卡号数字的OCR功

编辑: admin 分类: python 发布时间: 2021-12-03 来源:互联网
目录
  • 1. 效果图
  • 2. 原理
    • 2.1 OCR-A字体
    • 2.2 检测过程步骤
    • 2.3 优化
  • 3. 源代码

    这篇博客将介绍如何通过OpenCV和Python使用模板匹配执行光学字符识别(OCR)。具体来说,将使用Python+OpenCV实现模板匹配算法,以自动识别卡的类型和以及16位卡号数字。

    在比较数字时,模板匹配是一种非常快速的方法。

    为此将图像处理管道分为4个步骤:

    1. 通过各种图像处理技术检测信用卡上四组四个数字,包括形态学操作、阈值和轮廓提取。
    2. 从四个分组中提取每个单独的数字,得到16个需要分类的数字。
    3. 将模板匹配应用于每个数字,将其与OCR-A字体进行比较,以获得数字分类。
    4. 检查信用卡号的第一位数字以确定发卡公司。

    在对信用卡OCR系统进行评估后,发现如果发卡信用卡公司使用OCR-A字体作为数字,该系统的准确率为100%。 优化可以考虑在野外采集信用卡的真实图像,并训练机器学习模型(通过标准特征提取或训练或卷积神经网络),以进一步提高此系统的准确性。

    1. 效果图

    首先了解一下卡的组成:

    在这里插入图片描述

    OCR-A 参考字体识别如下:原始图 VS 灰度图 VS 阈值化图 VS 轮廓每个数字提取图:
    灰度图:忽略颜色对轮廓提取的影响
    阈值化图:使得轮廓在前景白色,背景黑色便于轮廓提取。
    轮廓提取图:提取每个数字ROI并记录,方便后续对比卡片中的区域以识别出对应的数字。

    在这里插入图片描述

    以下卡号均是演示卡,
    正确的识别卡的类型和卡号,效果图1:

    在这里插入图片描述

    识别过程1——原图 VS 灰度图 VS 白帽图 VS 梯度图如下:
    灰度图:忽略色彩影响
    白帽图:从较暗的背景中提取较亮的区域
    梯度图:计算Schaar梯度图,便于了解图像的色彩分配及提取;

    在这里插入图片描述

    识别过程2——形态学闭合图 VS 二值化图1 VS 阈值化图2 如下:
    形态学闭合图:矩形框形态学闭合操作,以帮助闭合信用卡数字之间的小的缝隙
    二值化图:以便于提取
    阈值化图:方形框形态学闭合操作,以二次帮助闭合信用卡数字区域之间的缝隙

    在这里插入图片描述

    识别过程3——轮廓过滤图 VS 提取最终效果图 如下:
    轮廓过滤图:根据面积及纵横比,只保留卡片中的卡号区
    最终效果图:提取4组4数字每一个组,然后对每一个组中的4个数字进行截取ROI并识别,并与之前存储的数字ROI进行模板匹配,选取匹配值最高的作为最终结果。

    在这里插入图片描述

    2. 原理

    2.1 OCR-A字体

    OCR-A字体,是一种专门用于辅助光学字符识别算法的字体。

    主要分为:

    检测图像中信用卡的位置;本地化信用卡上的四组四位数字;应用OCR识别信用卡上的16位数字;识别信用卡的类型。

    Tesseract库在某些情况无法正确识别数字(这可能是因为Tesseract未接受信用卡示例字体培训)。

    2.2 检测过程步骤

    在字典中存储卡类型映射关系(卡号的第一位数字代表卡类型)。获取参考图像并提取数字。将数字模板存储在字典中。本地化四个信用卡号组,每个组有四位数字(总共16位)。提取要“匹配”的数字。对每个数字执行模板匹配,将每个单独的ROI与每个数字模板0-9进行比较,同时存储每个尝试匹配的分数。查找每个候选数字的最高分数,并构建一个名为“输出”的列表。其中包含信用卡号。将信用卡号和信用卡类型输出到终端,并将输出图像显示到屏幕上。

    2.3 优化

    使用OpenCV和Python匹配OCR脚本的模板在100%的时间内正确识别了16位数字中的每一位。然而在将OCR图像应用于真实的信用卡图像时,考虑到照明条件、视角和其他一般噪音的变化,可能需要采取更面向机器学习的方法。

    3. 源代码

    # 信用卡类型及卡号OCR系统
    # USAGE
    # python ocr_template_match.py --reference images/ocr_a_reference.png --image images/credit_card_05.png
    
    import argparse
    
    import cv2
    import imutils
    import numpy as np
    # 导入必要的包
    from imutils import contours
    
    # 构建命令行参数及解析
    # --image 必须 要进行OCR的输入图像
    # --reference 必须 参考OCR-A图像
    ap = argparse.ArgumentParser()
    ap.add_argument("-i", "--image", required=True,
                    help="path to input image")
    ap.add_argument("-r", "--reference", required=True,
                    help="path to reference OCR-A image")
    args = vars(ap.parse_args())
    
    # 定义一个字典(映射信用卡第一位数字和信用卡类型的编号)
    FIRST_NUMBER = {
        "3": "American Express",
        "4": "Visa",
        "5": "MasterCard",
        "6": "Discover Card"
    }
    
    # 从磁盘加载参考OCR-A图像,转换为灰度图,阈值化图像以显示为白色前景和黑色背景
    # 并反转图像
    # and invert it, such that the digits appear as *white* on a *black*
    ref_origin = cv2.imread(args["reference"])
    cv2.imshow("ref_origin", ref_origin)
    ref = ref_origin.copy()
    ref = cv2.cvtColor(ref, cv2.COLOR_BGR2GRAY)
    cv2.imshow("ref_gray", ref)
    ref = cv2.threshold(ref, 180, 255, cv2.THRESH_BINARY)[1]
    cv2.imshow("ref_threshhold", ref)
    cv2.waitKey(0)
    
    # 寻找OCR-A图像中的轮廓(数字的外轮廓线)
    # 并从左到右排序轮廓,初始化一个字典来存储数字ROI
    refCnts = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,
                               cv2.CHAIN_APPROX_SIMPLE)
    print('findContours: ', len(refCnts))
    refCnts = imutils.grab_contours(refCnts)
    refCnts = contours.sort_contours(refCnts, method="left-to-right")[0]
    digits = {}
    
    # 遍历OCR-A轮廓
    for (i, c) in enumerate(refCnts):
        # 计算数字的边界框,提取它,缩放到固定的大小
        (x, y, w, h) = cv2.boundingRect(c)
        cv2.rectangle(ref_origin, (x, y), (x + w, y + h), (0, 255, 0), 2)
        roi = ref[y:y + h, x:x + w]
        roi = cv2.resize(roi, (57, 88))
    
        # 更新数字字典,数字匹配ROI
        digits[i] = roi
    cv2.imshow("ref and digits", ref_origin)
    cv2.waitKey(0)
    
    # 初始化矩形和方形结构内核
    # 在图像上滑动它来进行(卷积)操作,如模糊、锐化、边缘检测或其他图像处理操作。
    # 使用矩形函数作为Top-hat形态学运算符,使用方形函数作为闭合运算。
    rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
    sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
    
    # 准备进行OCR的输入图像
    # 加载输入图像,保持纵横比缩放图像宽度为300,转换为灰度图
    origin = cv2.imread(args["image"])
    origin = imutils.resize(origin, width=300)
    image = origin.copy()
    cv2.imshow("origin", origin)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    cv2.imshow("gray", gray)
    
    # 执行形态学操作
    # 应用tophat(白帽)形态学操作以在暗的背景中提取出亮的区域(信用卡上的数字卡号)
    # Top hat操作在深色背景(即信用卡号)下显示浅色区域
    tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
    cv2.imshow("tophat", tophat)
    
    # 计算Scharr梯度,计算梯度值
    # 在白色礼帽上,计算x方向的Scharr梯度,然后缩放到范围[0, 255]
    gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)
    gradX = np.absolute(gradX)
    (minVal, maxVal) = (np.min(gradX), np.max(gradX))
    # 最小/最大归一化, 由float转换gradX到uint8范围[0-255]
    gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
    gradX = gradX.astype("uint8")
    cv2.imshow("gradient", gradX)
    
    # 使用矩形框应用闭合操作以帮助闭合信用卡数字之间的小的缝隙
    # 应用Otsu's阈值方法二值化图像
    gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
    cv2.imshow("morphologyEx", gradX)
    thresh = cv2.threshold(gradX, 0, 255,
                           cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
    cv2.imshow("thresh1", thresh)
    
    # 在二值化图像上,应用二次闭合操作
    # 再一次方形框形态学操作,帮助闭合信用卡数字区域之间的缝隙
    thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)
    cv2.imshow("thresh2", thresh)
    
    # 阈值图像中查找轮廓,然后初始化数字位置列表
    cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
                            cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    locs = []
    
    # 遍历轮廓
    for (i, c) in enumerate(cnts):
        # 计算轮廓的边界框,并计算纵横比
        (x, y, w, h) = cv2.boundingRect(c)
        ar = w / float(h)
    
        # 由于信用卡有固定的4组4数字,可以根据纵横比来寻找潜在的轮廓
        if ar > 2.5 and ar < 4.0:
            # 轮廓可以在最小/最大宽度上进一步修剪
            if (w > 40 and w < 55) and (h > 10 and h < 20):
                # 添加数字组轮廓的编辑框轮廓到位置list
                locs.append((x, y, w, h))
                cv2.rectangle(origin, (x, y), (x + w, y + h), (255, 0, 0), -1)
    
    cv2.imshow("contours filter", origin)
    # 突出显示信用卡上四组四位数字(总共十六位)。
    # 从左到右排序轮廓,并初始化list来存储信用卡数字列表
    locs = sorted(locs, key=lambda x: x[0])
    output = []
    
    # 遍历四组四位数字
    for (i, (gX, gY, gW, gH)) in enumerate(locs):
        # 初始化存放每组数字的list
        groupOutput = []
    
        # 提取每组4位数字的灰度图ROI
        # 应用阈值方法从背景信用卡中分割数字
        group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5]
        group = cv2.threshold(group, 0, 255,
                              cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
    
        # 检测组中每个单独数字的轮廓
        # 从左到右排序轮廓
        digitCnts = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL,
                                     cv2.CHAIN_APPROX_SIMPLE)
        digitCnts = imutils.grab_contours(digitCnts)
        digitCnts = contours.sort_contours(digitCnts,
                                           method="left-to-right")[0]
    
        # 遍历数字轮廓
        for c in digitCnts:
            # 计算每个单独数字的边界框
            # 提取数字,缩放以拥有和参考OCR-A字体模板图像相同的大小
            (x, y, w, h) = cv2.boundingRect(c)
            roi = group[y:y + h, x:x + w]
            roi = cv2.resize(roi, (57, 88))
    
            # 初始化模板匹配分数list
            scores = []
    
            # 遍历参考数字名和数字ROI
            for (digit, digitROI) in digits.items():
                # 应用基于相关性的模板匹配,计算分数,更新分数list
                # apply correlation-based template matching, take the
                # score, and update the scores list
                result = cv2.matchTemplate(roi, digitROI,
                                           cv2.TM_CCOEFF)
                (_, score, _, _) = cv2.minMaxLoc(result)
                scores.append(score)
    
            # 数字ROI的分类将取 模板匹配分数中分数最大的参考数字
            # the classification for the digit ROI will be the reference
            # digit name with the *largest* template matching score
            groupOutput.append(str(np.argmax(scores)))
    
        # 围绕每组画一个矩形,并以红色文本标识图像上的信用卡号
        # 绘制每组的数字识别分类结果
        cv2.rectangle(image, (gX - 5, gY - 5),
                      (gX + gW + 5, gY + gH + 5), (0, 0, 255), 2)
        cv2.putText(image, "".join(groupOutput), (gX, gY - 15),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
    
        # 更新输出数字分组列表
        # Pythonic的方法是使用extend函数,它将iterable对象的每个元素(本例中为列表)追加到列表的末尾
        output.extend(groupOutput)
    
    # 显示检测到的信用卡类型和卡号到屏幕上
    print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
    print("Credit Card #: {}".format("".join(output)))
    cv2.imshow("Image", image)
    cv2.waitKey(0)

    参考 https://www.pyimagesearch.com/2017/07/17/credit-card-ocr-with-opencv-and-python/

    到此这篇关于使用Pyhton+OpenCV进行卡类型及16位卡号数字的OCR功能的文章就介绍到这了,更多相关Pyhton+OpenCV卡号数字识别内容请搜索hwidc以前的文章或继续浏览下面的相关文章希望大家以后多多支持hwidc!

    【自由互联:韩国服务器 转载请保留连接】