银行卡卡号识别

E:\python\opencv\bankCard OCR.py

对模版图片的处理

1,转为灰度图

复制代码
tem_gray=cv.cvtColor(digitalTemplate,cv.COLOR_BGR2GRAY)

2,转为二值图,方便查找轮廓,要求前景为白色,背景为黑色的

复制代码
tem_binary=cv.threshold(tem_gray,10,255,cv.THRESH_BINARY_INV)[1]

3,查找轮廓

复制代码
contours,_=cv.findContours(tem_binary.copy(),cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)

4,将轮廓的外接矩阵进行排序

根据外接矩阵的x坐标将矩阵进行从左到右的排序

复制代码
def sort_contours(contours, 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=[cv.boundingRect(c) for c in contours]
    (contours,boundingBoxes)=zip(*sorted(zip(contours,boundingBoxes),
                                         key=lambda b:b[1][i],reverse=reverse))
    return contours,boundingBoxes


contours,boundingBoxes=sort_contours(contours,method='Left-to-right')

5,构建字典用于存储数字与各个数字图像的对应关系

复制代码
#构建字典用于存储数字与图像的对应
digits={}

#遍历每一个轮廓
for (i,b) in enumerate(boundingBoxes):
    #获取外接矩形并且resize成合适大小
    (x,y,w,h)=b
    roi=tem_binary[y:y+h,x:x+w]
    roi=cv.resize(roi,(57,88))

    digits[i]=roi

对银行卡的处理

1,转为灰度图

复制代码
bankCard_gray=cv.cvtColor(bankCard,cv.COLOR_BGR2GRAY)

2,礼帽操作,突出明亮的部分

复制代码
bankCard_tophat=cv.morphologyEx(bankCard_gray,cv.MORPH_TOPHAT,rectKernel)

3,通过sobel算子找到图像的边缘,分别在x轴和y轴进行梯度计算,最后系数加和

复制代码
#sobel算子,找到边缘
gradX=cv.Sobel(bankCard_tophat,ddepth=cv.CV_32F,dx=1,dy=0,ksize=-1)

#对比一下直接缩放再绝对值的操作
# gradX1=cv.convertScaleAbs(gradX)
# cv_show(gradX1)

gradX=np.absolute(gradX)
(minmal,maxmal)=(np.min(gradX),np.max(gradX))
gradX=(255*((gradX-minmal)/(maxmal-minmal)))
gradX=gradX.astype("uint8")
# print(np.array(gradX).shape)
# cv_show(gradX)

gradY=cv.Sobel(bankCard_tophat,ddepth=cv.CV_32F,dx=0,dy=1,ksize=-1)

gradY=np.absolute(gradY)
(minmal,maxmal)=(np.min(gradY),np.max(gradY))
gradY=(255*((gradY-minmal)/(maxmal-minmal)))
gradY=gradY.astype("uint8")
# print(np.array(gradY).shape)
# cv_show(gradY)

gradXY=cv.addWeighted(gradX,0.5,gradY,0.5,0)

4,通过膨胀和闭操作(先膨胀,再腐蚀)将数字粘连到一起

复制代码
gradXY=cv.dilate(gradXY,rectKernel)
复制代码
gradXY=cv.morphologyEx(gradXY,cv.MORPH_CLOSE,rectKernel)

5,将图像转为二值图,方便寻找轮廓

复制代码
thresh=cv.threshold(gradXY,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)[1]

6,寻找轮廓

复制代码
threshcontours,_=cv.findContours(thresh.copy(),cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
contours=threshcontours

7,挑选出银行卡所在位置的轮廓的外接矩阵

复制代码
locs=[]

for (i,c) in enumerate(contours):
    (x,y,w,h)=cv.boundingRect(c)
    ar=w/float(h)

    if y/bankCard.shape[1]>0.32 and y/bankCard.shape[1]<0.35:
        locs.append((x,y,w,h))
locs=sorted(locs,key=lambda x:x[0])

8,对每个数字块进行处理:

1.将数字块区域转为二值图

2.进行一个小小的膨胀,确保每个数字都是一个整体,核也不能太大,防止粘连到其他数字

3.寻找数字块中每个数字的轮廓并排序

4.获取每个数字轮廓的外接矩阵并排序

5.将数字矩阵与0--9数字模板进行匹配,得到数字矩阵与每个数字的相似得分,根据得分输出对应的数字

复制代码
output=[]

for (i,(gx,gy,gw,gh)) in enumerate(locs):
    groupOutput=[]
    group1=bankCard_gray[gy-5:gy+gh+5,gx-5:gx+gw+5]
    cv_show(group1)
    # print(gy/bankCard.shape[1])
    group=cv.threshold(group1,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)[1]
    cv_show(group)
    group_dilate=cv.dilate(group,kernel=np.ones((3,3),np.uint8))
    # cv_show(group_dilate)
    digitscnts,_=cv.findContours(group_dilate,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
    # imgcnts=cv.drawContours(group1,digitscnts,-1,(0,0,0),1)
    # cv_show(imgcnts)
    # print(len(digitscnts))
    digitscnts=sort_contours(digitscnts,method="Left-to-right")[0]

    for c in digitscnts:
        (x,y,w,h)=cv.boundingRect(c)
        # cv.rectangle(group,(x,y),(x+w,y+h),(255,255,255),1)
        # cv_show(group)
        roi=group[y:y+h,x:x+w]
        roi=cv.resize(roi,(57,88))
        # cv_show(roi)

        scores=[]

        for (digit,digitROi) in digits.items():
            result=cv.matchTemplate(roi,digitROi,cv.TM_CCOEFF_NORMED)
            (_,score,_,_)=cv.minMaxLoc(result)
            scores.append(score)

        groupOutput.append(str(np.argmax(scores)))
    cv.rectangle(bankCard,(gx-5,gy-5),(gx+gw+5,gy+gh+5),(0,0,255),1)
    cv.putText(bankCard,"".join(groupOutput),(gx,gy-15),cv.FONT_HERSHEY_SIMPLEX,0.65,(0,0,255),2)

    output.extend(groupOutput)

cv_show(bankCard)

完整代码

复制代码
import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.pyplot import gray


def cv_show(img,imgname='res'):
    cv.imshow(imgname,img)
    cv.waitKey(0)
    cv.destroyAllWindows()


templatesrc='./data/digital template.png'
datasrc='./data/bankCard/bankCard2.png'

digitalTemplate=cv.imread(templatesrc)
bankCard=cv.imread(datasrc)

# cv_show('digitaltemplate',digitalTemplate)
# cv_show('bankCard',bankCard)

#灰度图
tem_gray=cv.cvtColor(digitalTemplate,cv.COLOR_BGR2GRAY)
# cv_show(tem_gray)

#二值图像
tem_binary=cv.threshold(tem_gray,10,255,cv.THRESH_BINARY_INV)[1]
# cv_show(tem_binary)

#计算轮廓
contours,_=cv.findContours(tem_binary.copy(),cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)

# cv.drawContours(digitalTemplate,contours,-1,(0,0,255),3)
# cv_show(digitalTemplate)
# print(len(contours))
#排序,从左到右,从上到下
def sort_contours(contours, 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=[cv.boundingRect(c) for c in contours]
    (contours,boundingBoxes)=zip(*sorted(zip(contours,boundingBoxes),
                                         key=lambda b:b[1][i],reverse=reverse))
    return contours,boundingBoxes


contours,boundingBoxes=sort_contours(contours,method='Left-to-right')
#验证
# for b in boundingBoxes:
#     cv.rectangle(digitalTemplate,(b[0],b[1]),(b[0]+b[2],b[1]+b[3]),(0, 0, 255), 3)
#     cv_show(digitalTemplate)
#构建字典用于存储数字与图像的对应
digits={}

#遍历每一个轮廓
for (i,b) in enumerate(boundingBoxes):
    #获取外接矩形并且resize成合适大小
    (x,y,w,h)=b
    roi=tem_binary[y:y+h,x:x+w]
    roi=cv.resize(roi,(57,88))

    digits[i]=roi

#查看分隔后的图片

# for i in range(len(boundingBoxes)):
#     plt.subplot(2,5,i+1),plt.imshow(digits[i])
#     plt.title(f'{i}'),plt.xticks([]),plt.yticks([])
# plt.show()
#     cv_show(digits[i])

#初始化卷积核
rectKernel=cv.getStructuringElement(cv.MORPH_RECT,(9,3))
sqKernel=cv.getStructuringElement(cv.MORPH_RECT,(5,5))

#获取输入图像,预处理
bankCard=cv.resize(bankCard,dsize=None,fx=0.8,fy=0.8)
# cv_show(bankCard)
#灰度图
bankCard_gray=cv.cvtColor(bankCard,cv.COLOR_BGR2GRAY)
# cv_show(bankCard_gray)

#礼帽操作,突出更明亮的区域
bankCard_tophat=cv.morphologyEx(bankCard_gray,cv.MORPH_TOPHAT,rectKernel)
# cv_show(bankCard_tophat)

#sobel算子,找到边缘
gradX=cv.Sobel(bankCard_tophat,ddepth=cv.CV_32F,dx=1,dy=0,ksize=-1)

#对比一下直接缩放再绝对值的操作
# gradX1=cv.convertScaleAbs(gradX)
# cv_show(gradX1)

gradX=np.absolute(gradX)
(minmal,maxmal)=(np.min(gradX),np.max(gradX))
gradX=(255*((gradX-minmal)/(maxmal-minmal)))
gradX=gradX.astype("uint8")
# print(np.array(gradX).shape)
# cv_show(gradX)

gradY=cv.Sobel(bankCard_tophat,ddepth=cv.CV_32F,dx=0,dy=1,ksize=-1)

gradY=np.absolute(gradY)
(minmal,maxmal)=(np.min(gradY),np.max(gradY))
gradY=(255*((gradY-minmal)/(maxmal-minmal)))
gradY=gradY.astype("uint8")
# print(np.array(gradY).shape)
# cv_show(gradY)

gradXY=cv.addWeighted(gradX,0.5,gradY,0.5,0)
# cv_show(gradXY)


#通过闭操作(先膨胀,再腐蚀)将数字连在一起
gradXY=cv.dilate(gradXY,rectKernel)
# cv_show(gradXY)

gradXY=cv.morphologyEx(gradXY,cv.MORPH_CLOSE,rectKernel)
# cv_show(gradXY)

thresh=cv.threshold(gradXY,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)[1]
# cv_show(thresh)

# thresh=cv.morphologyEx(thresh,cv.MORPH_CLOSE,sqKernel)
# cv_show(thresh)

threshcontours,_=cv.findContours(thresh.copy(),cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
contours=threshcontours
# cur_img=bankCard.copy()
# cv.drawContours(cur_img,contours,-1,(0,0,255),3)
# cv_show(cur_img)

locs=[]

# cur_img=bankCard.copy()
for (i,c) in enumerate(contours):
    (x,y,w,h)=cv.boundingRect(c)

    if y/bankCard.shape[1]>0.32 and y/bankCard.shape[1]<0.35:
        locs.append((x,y,w,h))

#         cv.rectangle(cur_img,(x,y),(x+w,y+h),(0,0,255),2)
# cv_show(cur_img)
locs=sorted(locs,key=lambda x:x[0])


output=[]

for (i,(gx,gy,gw,gh)) in enumerate(locs):
    groupOutput=[]
    group1=bankCard_gray[gy-5:gy+gh+5,gx-5:gx+gw+5]
    cv_show(group1)
    # print(gy/bankCard.shape[1])
    group=cv.threshold(group1,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)[1]
    cv_show(group)
    group_dilate=cv.dilate(group,kernel=np.ones((3,3),np.uint8))
    # cv_show(group_dilate)
    digitscnts,_=cv.findContours(group_dilate,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
    # imgcnts=cv.drawContours(group1,digitscnts,-1,(0,0,0),1)
    # cv_show(imgcnts)
    # print(len(digitscnts))
    digitscnts=sort_contours(digitscnts,method="Left-to-right")[0]

    for c in digitscnts:
        (x,y,w,h)=cv.boundingRect(c)
        # cv.rectangle(group,(x,y),(x+w,y+h),(255,255,255),1)
        # cv_show(group)
        roi=group[y:y+h,x:x+w]
        roi=cv.resize(roi,(57,88))
        # cv_show(roi)

        scores=[]

        for (digit,digitROi) in digits.items():
            result=cv.matchTemplate(roi,digitROi,cv.TM_CCOEFF_NORMED)
            (_,score,_,_)=cv.minMaxLoc(result)
            scores.append(score)

        groupOutput.append(str(np.argmax(scores)))
    cv.rectangle(bankCard,(gx-5,gy-5),(gx+gw+5,gy+gh+5),(0,0,255),1)
    cv.putText(bankCard,"".join(groupOutput),(gx,gy-15),cv.FONT_HERSHEY_SIMPLEX,0.65,(0,0,255),2)

    output.extend(groupOutput)

cv_show(bankCard)
相关推荐
海南java第二人1 小时前
Cursor 高级实战:从 Spring Boot 到微服务,AI 驱动的全流程开发指南
人工智能·spring boot·微服务
码码哈哈0.01 小时前
开源项目Heygem本地运行 AI 数字人模型
人工智能·ai
CertiK1 小时前
CertiK实测:Skill扫描并非安全边界
人工智能·安全·openclaw
大傻^1 小时前
Spring AI Alibaba 项目初始化:Maven依赖与YAML配置全解析
人工智能·spring·maven·springai·springaialibaba·评估框架
OpenCSG2 小时前
GLM-OCR:轻量级多模态OCR的技术突破
人工智能
ofoxcoding2 小时前
Qwen3.5 API 接入实测:和 GPT-4o 比到底差多少
人工智能·qwen3.5
摄影图2 小时前
智能汽车领域应用图素材 汽车AI研发转型
人工智能·科技·aigc
一只落魄的蜂鸟2 小时前
【2026年-11期】Where lies the future of humanity in the age of AI?
人工智能
IT阳晨。2 小时前
PyTorch深度学习实践
人工智能·pytorch·深度学习