Advertisement

基于OpenCV和数字图像处理的图像识别项目——信用卡卡号识别

阅读量:

信用卡卡号识别:

该项目旨在实现信用卡数字识别功能;即对于任意一张信用卡图片而言,系统将准确识别并提取卡片上的号码信息,并标注出卡片号码在图像中的具体位置

在这里插入图片描述

核心上来说,则是一个标准模式的核心问题。针对该信用卡而言,在识别出其数字范围的基础上展开下一步骤的大体思路如下:

首先采用基于边缘检测的方法来实现目标物体边界信息提取功能,并利用该技术实现目标物体的大致位置识别过程。
接着根据各物体尺寸特征计算出中间数值串区域,并确定位于物体中心位置的一串连续数字区域。
随后通过形态学处理增强该区域的边缘特性。
接下来再次利用边缘检测方法识别出四个独立的矩形框,并分别对这四个矩形框进行边缘定位。
最后针对每一个单独识别出来的数字字符应用字符匹配算法来确定其具体数值。

使用的模板张下面这样:

在这里插入图片描述

在模板方面,同样需要实现相应的处理步骤:首先将模板转换为灰度图像,并对其进行二值化处理以便后续识别。接着通过轮廓检测技术将其分割为10个独立的小区域。从而使得其中的每一个数字与这些小区域进行匹配操作。最终将得到所需的匹配结果。

复制代码
    import os
    import numpy as np
    from imutils import contours
    import cv2
    import pickle
    
    
      
      
      
      
      
    
    代码解读
复制代码
    def cv_show(title, img):
    cv2.imshow(title, img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    
      
      
      
      
    
    代码解读
复制代码
    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
    
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解读
复制代码
    # 读取图像
    base_path = 'images'
    file_name = 'credit_card_01.png'
    credit_card = cv2.imread(os.path.join(base_path, file_name))
    credit_card = resize(credit_card, width=300)
    credit_gray = cv2.cvtColor(credit_card, cv2.COLOR_BGR2GRAY)
    cv_show('img', credit_gray)
    
    # 顶帽操作,突出更明亮的区域
    
    # 初始化卷积核
    rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))  # 自定义卷积核的大小了
    sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
    
    tophat = cv2.morphologyEx(credit_gray, cv2.MORPH_TOPHAT, rectKernel)
    # cv_show('tophat', tophat)
    
    cv_show('tophat', tophat)
    
    # 水平边缘检测  
    gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)  # 水平边缘检测
    # gradX = cv2.convertScaleAbs(gradX)    这个操作会把一些背景边缘也给检测出来,加了一些噪声
    
    # 所以下面手动归一化操作
    gradX = np.absolute(gradX)
    (minVal, maxVal) = (np.min(gradX), np.max(gradX))
    gradX = (255 * ((gradX-minVal) / (maxVal-minVal)))
    gradX = gradX.astype('uint8')
    
    # 这里也可以按照之前的常规, 先水平,后垂直,然后合并,但是效果可能不如单独x的效果好
    
    cv_show('gradX', gradX)
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解读

接下来的一个问题是,在当前情况下确实能够找到边缘的位置;然而为了将相邻位置上的数字统一处理成块状结构,则需要运用形态学相关的处理手段来实现这一目标

复制代码
    # 闭操作: 先膨胀, 后腐蚀  膨胀就能连成一块了
    gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
    cv_show('gradX', gradX)
    
    
      
      
      
    
    代码解读

然后会发现,数字虽然大部分连成一块一块的了,但是有些地方有些黑洞,下面尝试把这些黑洞用白色填充起来

复制代码
    #THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0  让opencv自动的去做判断,找合适的阈值,这样就能自动找出哪些有用,哪些没用
    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 = credit_card.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])
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解读

得到每个轮廓中的每个数字,然后匹配

复制代码
    # 下面将轮廓进行排序,这是因为必须保证轮廓的顺序是0-9的顺序排列着
    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
    
    # 根据每个轮廓左上角的点进行排序, 这样能保证轮廓的顺序就是0-9的数字排列顺序
    (cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes), key=lambda x:x[1][i], reverse=reverse))
    
    return cnts, boundingBoxes 
    digits2Cnt = pickle.load(open('digits2Cnt.pkl', 'rb'))
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解读
复制代码
    outputs = []
    
    # 遍历每一个轮廓中的的数字
    for (i, (gX, gY, gW, gH)) in enumerate(locs):
    # 初始化组
    groupOutput = []
    
    # 根据坐标提取每一组
    group = credit_gray[gY-5:gY+gH+5, gX-5:gX+gW+5]  # 有5的一个容错长度
    
    # 对于这每一组,先预处理  
    # 二值化,自动寻找合适阈值,增强对比,更突出有用的部分,即数字
    group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
    
    # 计算每一组的轮廓
    digitCnts, hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    digitCnts = 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))
        
        # 模板匹配
        scores = []
        for (digit, digitROI) in digits2Cnt.items():
            result = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF)
            (_, score, _, _) = cv2.minMaxLoc(result)
            scores.append(score)
        
        # 得到合适的数字
        # 这是个列表,存储的每个小组里面的数字识别结果
        groupOutput.append(str(np.argmax(scores)))
    
    # 画出来
    cv2.rectangle(credit_card, (gX - 5, gY - 5), (gX + gW + 5, gY + gH + 5), (0, 0, 255), 1)
    cv2.putText(credit_card, "".join(groupOutput), (gX, gY - 15), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
    
    # 合并到最后的结果里面
    outputs.extend(groupOutput)
    
    # 指定信用卡类型
    FIRST_NUMBER = {
    	"3": "American Express",
    	"4": "Visa",
    	"5": "MasterCard",
    	"6": "Discover Card"
    }
    
    # 打印结果
    print("Credit Card Type: {}".format(FIRST_NUMBER[outputs[0]]))
    print("Credit Card #: {}".format("".join(outputs)))
    cv_imshow("Image", credit_card)
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解读

Credit Card Type: Visa
Credit Card #: 4000123456789010
32

全部评论 (0)

还没有任何评论哟~