银行卡卡号识别

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)
相关推荐
昨夜见军贴0616几秒前
IA-Lab AI 检测报告生成助手:土壤重金属检测报告如何实现GB 15618标准自动解析,推动降本与合规双升级?
大数据·人工智能
OpenVINO 中文社区1 分钟前
4.13直播 | 端侧多模态模型应用开发Skill实战
人工智能
前端摸鱼匠1 分钟前
【AI大模型春招面试题17】 过拟合、欠拟合在大模型中的表现与解决策略?
人工智能·ai·语言模型·面试·大模型
Coovally AI模型快速验证2 分钟前
建筑外立面多类缺陷自动巡检系统:无人机采集+AI分割+自动报告,剥落检测Recall达98%
人工智能·无人机·机器视觉·工业检测·建筑检测
handsomestWei3 分钟前
RAG知识图谱简介
人工智能·知识图谱·rag·lightrag
IT_陈寒3 分钟前
Python的异步陷阱:我竟然被await坑了一整天
前端·人工智能·后端
中钧科技3 分钟前
数字化的本质、核心、重点各是什么?
大数据·人工智能
硅农深芯7 分钟前
解析RF信号的调制方式
人工智能·语音识别·信号处理·rf·射频·调制
连线Insight8 分钟前
林俊旸离开后,吴泳铭猛补AI课
人工智能
拓端研究室9 分钟前
2026中国医疗健康行业趋势报告:投融资回暖、AI渗透与产业链提价|附100+份报告PDF、数据、可视化模板汇总下载
人工智能·物联网·microsoft