OpenCV实践-银行卡号识别

OpenCV实践-银行卡号识别

利用opencv实现对图片上的银行卡上的卡号识别,并把识别出的数字标注在图片上。解决思路:

  1. 对数字模板进行轮廓识别,并将每个数字放到数组里
  2. 对银行卡上所有轮廓处理,根据长度选取每组卡号的轮廓(一组为4个数字)
  3. 将卡号轮廓外接矩形坐标保存,提取里面的每个数字并将它与数组的数字进行模板匹配
  4. 选取得分最高的数字,并将它们画在图片上

模板处理

根据这样的一个数字模板:
OpenCV实践-银行卡号识别_第1张图片

因为轮廓提取函数只接受二值图像,所以先灰度化,二值化:

ref = cv2.cvtCOLOR(img,cv2.COLOR_BGR2GRAY)
#二值化图像(将超过10的像素值取255,低于10的取0)
ref= cv2.threshold(ref,10,255,cv2.THRESH_BINARY_INV)[1]

src,dst = cv2.threshold(img,thresh,maxVal,type)
见:图像阈值计算

提取并绘制轮廓,:

# findContoursh只接受二值图像,黑白图像,不是灰度图
# cv2.RETR_EXTERNAL只检测外轮廓,
# cv2.CHAIN_APPROX_SIMPLE只保留终点坐标
#返回的list中每个元素都是图像中的一个轮廓
ref_,refCnts,hierarchy = cv2.findContours(ref.copy(),
                            cv2.RETR_EXTERNAL,
                            cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,refCnts,-1,(0,0,255),3)

OpenCV实践-银行卡号识别_第2张图片
将轮廓外接矩形存入digits数组

for (i, c) in enumerate(refCnts):
    (x,y,w,h) = cv2.boundingRect(c)
    roi = ref[y:y+h,x:x+w]
    roi = cv2.resize(roi,(57,88))

    digits[i] = roi

银行卡图片处理

读取后,灰度化。
礼帽操作,突出明亮区域

tophat = cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel)

OpenCV实践-银行卡号识别_第3张图片
morphologyEx形态学操作,见:图像形态学操作
用sobel因子计算梯度

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))
gradX = (255*((gradX-minVal)/(maxVal-minVal)))
#去除噪音
gradX = gradX.astype("uint8")       

图像梯度见: 图像梯度
闭操作连接数字的轮廓

#通过闭操作(先膨胀,再腐蚀)将数字连在一起
gradX = cv2.morphologyEx(gradX,cv2.MORPH_CLOSE,rectKernel)

OpenCV实践-银行卡号识别_第4张图片
提取轮廓

#thresh_otsu会自动寻找合适的阈值,适合双峰,需把阈值参数设为0
thresh = cv2.threshold(gradX,0,255,
                       cv2.THRESH_BINARY|cv2.THRESH)[1]

#计算轮廓,只检测外轮廓,只保留终点坐标
thresh_, threshCnts, hierarchy = cv2.findContours(thresh.copy(),
                                                  cv2.RETR_EXTERNAL,
                                                  cv2.CHAIN_APPROX_SIMPLE)
                   

OpenCV实践-银行卡号识别_第5张图片
OpenCV实践-银行卡号识别_第6张图片
筛选属于数值的矩形

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):
    gropOutPut = []
    # 根据坐标提取每组
    print(gX,gY,gW,gH)

    #128*800(w*h)图片在矩阵存储是800*128(h,w)
    #此处-5和+5都是位移,保证了一组数据不会被重复选取
    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]

    group_,digitCnts,hierarchy = cv2.findContours(group,cv2.RETR_EXTERNAL,
                                                  cv2.CHAIN_APPROX_SIMPLE)
    digitCnts = myutils.sort_contours(digitCnts,"left-to-right")[0]
    #每个数字组,4个数字
    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 =[]
        # digits是每个数字的模板,
        # 在所有的数字模板中进行匹配 TM_CCORR:计算相关性,计算出来的值越大,越相关
        for(digit,digitROI) in digits.items():
            result = cv2.matchTemplate(roi,digitROI,
                                       cv2.TM_CCOEFF)
            # min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
            (_,score,_,_) = cv2.minMaxLoc(result)
            scores.append(score)

        gropOutPut.append(str(np.argmax(scores)))
        print(gropOutPut)
     #确实是靠 确定对角线 来画矩形的。
    # cv2.rectangle(img, (bbox.left, bbox.top), (bbox.right, bbox.bottom), (0,0,255), 2)
    cv2.rectangle(image,(gX-5,gY-5),(gX+gW+5,gY+gH+5),(0,0,255),1)
    cv2.putText(image,"".join(gropOutPut),(gX,gY-15),
                cv2.FONT_HERSHEY_SIMPLEX,0.65,(0,0,255),2)
    output.extend(gropOutPut)

最后得到结果为:
OpenCV实践-银行卡号识别_第7张图片

你可能感兴趣的:(opencv,机器学习,图像处理)