• OpenCV图像处理——(实战)信用卡识别


    总目录

    图像处理总目录←点击这里

    十四、信用卡数字识别

    • 识别的图片
    • 模板图片

    14.1、模板图片处理

    在这里插入图片描述

    读入图片->灰度图->二值图->计算轮廓->存储每一个模板

    如果是所需模板匹配只有一个,课直接读入灰度图像即可
    这里有10个模板(0-9),所以需要切割存储为多个模板进行循环匹配

    # 预处理
    	#  读入图片->灰度图->二值图->计算轮廓->存储每一个模板
    
    # 遍历模板的每一个轮廓并且存储
    for (i, c) in enumerate(refCnts):
        # 计算外接矩形并且resize成合适大小
        (x, y, w, h) = cv2.boundingRect(c)
        roi = ref[y:y + h, x:x + w]
        roi = cv2.resize(roi, (57, 88))
    
        # 每一个数字对应每一个模板
        digits[i] = roi
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    14.2、识别图片处理

    读取图像->灰度->边缘检测->计算轮廓检测->遍历筛选(宽高比)存储->再次计算轮廓(每个小部分,如4000)

    边缘检测可以直接调用Canny方法实现,

    • cv2.Canny(gray, 10, 200)

    可以自己实现

    • 礼帽(去除背景)->梯度(边界)->闭操作(先膨胀,再腐蚀)-找阈值->闭操作

    这两种边缘检测的作用一样

    (1)灰度

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    • 1

    灰度图

    (2)边缘检测

    cv2.Canny(gray, 10, 200)
    
    • 1

    第(2)部分下面的代码和第一句等价

    礼帽

    rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
    tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
    
    • 1
    • 2

    tophat
    梯度

    sobel算子梯度

    gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)  # ksize=-1相当于用3*3的
    
    gradX = np.absolute(gradX)
    (minVal, maxVal) = (np.min(gradX), np.max(gradX))
    gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
    gradX = gradX.astype("uint8")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    grad
    闭操作

    gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
    
    • 1

    闭操作
    找阈值

    这里采用双峰自动找合适的阈值,以下为固定写法

    thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
    
    • 1

    阈值
    闭操作
    让目前轮廓白色填充满

    thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)
    
    • 1

    闭操作
    (3)轮廓检测

    计算轮廓在原图像中展示

    threshCnts, hierarchy =  cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    cnts = threshCnts
    cur_img = image.copy()
    cv2.drawContours(cur_img, cnts, -1, (0, 0, 255), 3)
    cv_show('img', cur_img)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    计算轮廓

    (4)筛选

    按符合条件的比例进行筛选,筛选出符合条件的

    for (i, c) in enumerate(cnts):
        # 计算矩形
        (x, y, w, h) = cv2.boundingRect(c)
        ar = w / float(h)
        # 选择合适的区域,根据实际任务来,这里的基本都是四个数字一组
        if ar > 2.5 and ar < 4.0:
            if (w > 40 and w < 55) and (h > 10 and h < 20):
                # 符合的留下来
                locs.append((x, y, w, h))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
    (5)再次计算轮廓并处理

    遍历上面每一个轮廓中的数字并且和模板匹配

    for (i, (gX, gY, gW, gH)) in enumerate(locs):
        # initialize the list of group digits
        groupOutput = []
    
        # 根据坐标提取每一个组
        group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5]
        cv_show('group', group)
        # 预处理
        group = cv2.threshold(group, 0, 255,
                              cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
        # cv_show('group', group)
        # 计算每一组的轮廓
        digitCnts, hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL,
                                                        cv2.CHAIN_APPROX_SIMPLE)
        digitCnts = contours.sort_contours(digitCnts,
                                           method="left-to-right")[0]
    
        # 计算每一组中的每一个数值
        for c in digitCnts:
            # 找到当前数值的轮廓,resize成合适的的大小
            (x, y, w, h) = cv2.boundingRect(c)
            roi = group[y:y + h, x:x + w]
            roi = cv2.resize(roi, (57, 88))
            cv_show('roi', 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(image, (gX - 5, gY - 5),
                      (gX + gW + 5, gY + gH + 5), (0, 0, 255), 1)
        cv2.putText(image, "".join(groupOutput), (gX, gY - 15),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
    
        # 得到结果
        output.extend(groupOutput)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

    最终效果

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    原图

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    源码

    主程序

    注意初始化参数的输入
    在这里插入图片描述
    --image ./images/credit_card_01.png --template ocr_a_reference.png

    # 导入工具包
    from imutils import contours
    import numpy as np
    import argparse
    import cv2
    import myutils
    
    # 设置参数
    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 OCR-A image")
    args = vars(ap.parse_args())
    
    # 指定信用卡类型
    FIRST_NUMBER = {
        "3": "American Express",
        "4": "Visa",
        "5": "MasterCard",
        "6": "Discover Card"
    }
    
    
    # 绘图展示
    def cv_show(name, img):
        cv2.imshow(name, img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
    
    
    # 读取一个模板图像
    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)
    
    # 计算轮廓
    # cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图),cv2.RETR_EXTERNAL只检测外轮廓,cv2.CHAIN_APPROX_SIMPLE只保留终点坐标
    # 返回的list中每个元素都是图像中的一个轮廓
    
    refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    cv2.drawContours(img, refCnts, -1, (0, 0, 255), 3)
    # cv_show('img', img)
    print(np.array(refCnts).shape)
    refCnts = myutils.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:y + h, x:x + w]
        roi = cv2.resize(roi, (57, 88))
    
        # 每一个数字对应每一个模板
        digits[i] = roi
    
    # 初始化卷积核
    rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
    sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
    
    # 读取输入图像,预处理
    image = cv2.imread(args["image"])
    # cv_show('image', image)
    image = myutils.resize(image, width=300)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # cv_show('gray', gray)
    
    # 礼帽操作,突出更明亮的区域
    tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
    # cv_show('tophat', tophat)
    # 
    gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)  # ksize=-1相当于用3*3的
    
    gradX = np.absolute(gradX)
    (minVal, maxVal) = (np.min(gradX), np.max(gradX))
    gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
    gradX = gradX.astype("uint8")
    
    print(np.array(gradX).shape)
    # cv_show('gradX', gradX)
    
    # 通过闭操作(先膨胀,再腐蚀)将数字连在一起
    gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
    # cv_show('gradX', gradX)
    # THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
    thresh = cv2.threshold(gradX, 0, 255,
                           cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
    # cv_show('thresh', thresh)
    
    # 再来一个闭操作
    
    thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)  # 再来一个闭操作
    # cv_show('thresh', thresh)
    
    # 计算轮廓
    
    threshCnts, hierarchy = \
        cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    cnts = threshCnts
    cur_img = image.copy()
    cv2.drawContours(cur_img, cnts, -1, (0, 0, 255), 3)
    # cv_show('img', cur_img)
    locs = []
    
    # 遍历轮廓
    for (i, c) in enumerate(cnts):
        # 计算矩形
        (x, y, w, h) = cv2.boundingRect(c)
        ar = w / float(h)
    
        # 选择合适的区域,根据实际任务来,这里的基本都是四个数字一组
        if ar > 2.5 and ar < 4.0:
    
            if (w > 40 and w < 55) and (h > 10 and h < 20):
                # 符合的留下来
                locs.append((x, y, w, h))
    
    # 将符合的轮廓从左到右排序
    locs = sorted(locs, key=lambda x: x[0])
    output = []
    
    # 遍历每一个轮廓中的数字
    for (i, (gX, gY, gW, gH)) in enumerate(locs):
        # initialize the list of group digits
        groupOutput = []
    
        # 根据坐标提取每一个组
        group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5]
        # cv_show('group', group)
        # 预处理
        group = cv2.threshold(group, 0, 255,
                              cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
        # cv_show('group', group)
        # 计算每一组的轮廓
        digitCnts, hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL,
                                                        cv2.CHAIN_APPROX_SIMPLE)
        digitCnts = contours.sort_contours(digitCnts,
                                           method="left-to-right")[0]
    
        # 计算每一组中的每一个数值
        for c in digitCnts:
            # 找到当前数值的轮廓,resize成合适的的大小
            (x, y, w, h) = cv2.boundingRect(c)
            roi = group[y:y + h, x:x + w]
            roi = cv2.resize(roi, (57, 88))
            # cv_show('roi', 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(image, (gX - 5, gY - 5),
                      (gX + gW + 5, gY + gH + 5), (0, 0, 255), 1)
        cv2.putText(image, "".join(groupOutput), (gX, gY - 15),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
    
        # 得到结果
        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)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182

    工具方法

    myutils.py

    import cv2
    
    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] #用一个最小的矩形,把找到的形状包起来x,y,h,w
        (cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
                                            key=lambda b: b[1][i], reverse=reverse))
    
        return cnts, boundingBoxes
    def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
        dim = None
        (h, w) = image.shape[:2]
        if width is None and height is None:
            return image
        if width is None:
            r = height / float(h)
            dim = (int(w * r), height)
        else:
            r = width / float(w)
            dim = (width, int(h * r))
        resized = cv2.resize(image, dim, interpolation=inter)
        return resized
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
  • 相关阅读:
    APS可以给制造型企业带来哪些效益?
    xss——pdfxss,mxss,uxss,flashxss
    Docker安装Redis并使用Another Redis Desktop Manager连接
    【C/C++】宏定义中的#和##
    python基础语法 - 正则表达式
    Jenkins+Docker+SpringCloud微服务持续集成(中)
    python中的NaN在质量控制中怎么处理?
    4种实用的制作URL 文件的方法
    【HCIE】15.OSPFV3
    DRF: 序列化器、View、APIView、GenericAPIView、Mixin、ViewSet、ModelViewSet的源码解析
  • 原文地址:https://blog.csdn.net/weixin_44635198/article/details/127950841