OpenCV学习(一)银行卡号识别

  • B站教学链接: 

银行卡号识别(OpenCV-Python)

代码:Card_Number_Recognition

1.方法

使用模板匹配的方法来识别银行卡号数字。具体来说,先通过图像预处理将图像中的银行卡号分割出来,再与提供的模板进行对比,从而完成银行卡号的识别。

2.代码

##############################
###   Bank card number identification
##############################

import cv2
import numpy as np

card_GRAY=cv2.imread('bank_card41.jpg',0)
card_GRAY4=cv2.resize(card_GRAY,(4*card_GRAY.shape[1],4*card_GRAY.shape[0]))
#cv2.imshow('card_GRAY4',card_GRAY4)

adaptiveThresh = cv2.adaptiveThreshold(card_GRAY4, 255, cv2.ADAPTIVE_THRESH_MEAN_C, 
                                       cv2.THRESH_BINARY_INV, 13, 3)
#cv2.imshow('adaptiveThresh',adaptiveThresh)

contours, hierarchy = cv2.findContours(adaptiveThresh,cv2.RETR_TREE,
                                       cv2.CHAIN_APPROX_SIMPLE)
for i in range(len(contours)):
    if cv2.contourArea(contours[i])<160:
        adaptiveThresh = cv2.drawContours(adaptiveThresh, contours, i, (0,0,0), -1)
#cv2.imshow('adaptiveThresh2',adaptiveThresh)

kernel = np.ones((15,15),dtype=np.uint8)
blackhat = cv2.morphologyEx(adaptiveThresh, cv2.MORPH_BLACKHAT, kernel)
#cv2.imshow('blackhat',blackhat)

kernel = np.ones((3,3),dtype=np.uint8)
opening = cv2.morphologyEx(blackhat, cv2.MORPH_OPEN, kernel)
#cv2.imshow('opening',opening)

contours, hierarchy = cv2.findContours(opening,cv2.RETR_TREE,
                                       cv2.CHAIN_APPROX_SIMPLE)
for i in range(len(contours)):
    x,y,w,h = cv2.boundingRect(contours[i])
    aspect_ratio = float(w)/h
    Area = w * h
    if Area<1800 or Area>6000:
        opening = cv2.drawContours(opening, contours, i, (0,0,0), -1)
    else:
        if aspect_ratio>0.7 or aspect_ratio<0.5:
            opening = cv2.drawContours(opening, contours, i, (0,0,0), -1)
cv2.imshow('opening2',opening)

kernel = np.ones((5,5),np.uint8)
dilation = cv2.dilate(opening,kernel,iterations = 1)
cv2.imshow('dilation',dilation)

numTemplate=cv2.imread('bankCardNumTemplate.jpg')
numTemplate_GRAY=cv2.cvtColor(numTemplate,cv2.COLOR_BGR2GRAY)
ret,numTemplate_GRAY = cv2.threshold(numTemplate_GRAY,200,255,cv2.THRESH_BINARY)
cv2.imshow('numTemplate_GRAY',numTemplate_GRAY)

def sequence_contours(image, width, height):
    contours, hierarchy = cv2.findContours(image,cv2.RETR_EXTERNAL,
                                           cv2.CHAIN_APPROX_SIMPLE)
    n = len(contours)
    RectBoxes0 = np.ones((n,4),dtype=int)
    for i in range(n):
        RectBoxes0[i] = cv2.boundingRect(contours[i])
    
    RectBoxes = np.ones((n,4),dtype=int)
    for i in range(n):
        sequence = 0
        for j in range(n):
            if RectBoxes0[i][0]>RectBoxes0[j][0]:
                sequence = sequence + 1
        RectBoxes[sequence] = RectBoxes0[i]
        
    ImgBoxes = [[] for i in range(n)]
    for i in range(n):
        x,y,w,h = RectBoxes[i]
        ROI = image[y:y+h,x:x+w]
        ROI = cv2.resize(ROI, (width, height))
        thresh_val, ROI = cv2.threshold(ROI, 200, 255, cv2.THRESH_BINARY)
        ImgBoxes[i] = ROI
        
    return RectBoxes, ImgBoxes
    
RectBoxes_Temp, ImgBoxes_Temp = sequence_contours(numTemplate_GRAY, 50, 80)
print(RectBoxes_Temp)
cv2.imshow('ImgBoxes_Temp[1]',ImgBoxes_Temp[1])
RectBoxes, ImgBoxes = sequence_contours(dilation, 50, 80)

result = []
for i in range(len(ImgBoxes)):
    score = np.zeros(len(ImgBoxes_Temp),dtype=int)
    for j in range(len(ImgBoxes_Temp)):
        score[j] = cv2.matchTemplate(ImgBoxes[i], ImgBoxes_Temp[j], cv2.TM_SQDIFF)
    min_val, max_val, min_indx, max_indx = cv2.minMaxLoc(score)
    result.append(min_indx[1])
print(result)

cv2.waitKey(0)
cv2.destroyAllWindows()




2.1 分割银行卡号码

目的:减少识别号码时的干扰。

方法:找到号码与其他区域的区别,通过这个区别将其他部分剪切掉。

以下为尝试,还没有真的实现,这张银行卡号码颜色和银行卡本身太接近了。

2.1.1 前景分割

一般而言,银行卡上有相应银行的名字,logo,和有标识度的建筑,银行卡号码一般在卡中间稍向下,凸出,且有一定反光的部分。因此,银行卡号与其他图像是一个前后景的关系,银行卡号是前景,其余图像是背景。所以通过前景分割来去除银行卡号无关的图像信息是可行的。

2.1.2 图像处理

通常情况下,银行卡号码的像素是有别于它周围的,使用灰度化和自适应阈值化后可以明显的观察到银行卡号和其他图像的,之后通过形态学变化来确定银行卡位置的形状,然后以银行卡号特有的长宽比例从银行卡上筛选出号码区域。

筛选出的号码区域:

2.2 图像预处理

因为识别的方法睡觉哦模板匹配,首先就需要尽可能地将图像处理得和模板相似,再通过模板清晰的图像来识别银行卡号码。

模板:

2.2.1 初步预处理

#读取图像

# 注意:这一步骤已经在读取图像时完成,因为使用了参数 0,将图像直接读取为灰度图像

card_GRAY=cv2.imread('bank_card41.jpg',0)

#将图像扩大四倍
card_GRAY4=cv2.resize(card_GRAY,(4*card_GRAY.shape[1],4*card_GRAY.shape[0]))
#自适应阈值化

adaptiveThresh = cv2.adaptiveThreshold(card_GRAY4, 255, cv2.ADAPTIVE_THRESH_MEAN_C, 
                                       cv2.THRESH_BINARY_INV, 13, 3)

  1. 灰度化:

    • 目的: 将彩色图像转换为灰度图像,即只包含灰度级别(0-255)的图像,而不包含颜色信息。
    • 原理: 灰度化过程会将彩色图像中的每个像素的红、绿、蓝(RGB)三个通道的值进行加权平均,从而得到一个单一的灰度值。常用的加权公式包括取平均值、加权平均值等。
    • 优势: 简化了图像,去除了颜色信息,使得图像处理更加高效和简单。在很多图像处理任务中,只需使用亮度信息而不需要颜色信息。
    • 应用: 在很多计算机视觉任务中广泛应用,如边缘检测、特征提取、图像分割等。

  1. 自适应阈值化:

    • 目的: 将图像分割成目标和背景,以便更容易地提取目标对象或执行其他后续处理任务。
    • 原理: 自适应阈值化根据图像的局部区域来确定每个像素的阈值,而不是使用全局固定的阈值。这样可以更好地处理图像中光照不均匀或对比度不一致的情况。
    • 优势: 自适应阈值化能够根据图像的局部特征动态调整阈值,从而更好地适应不同区域的光照和对比度变化,提高了图像处理的鲁棒性和准确性。
    • 应用: 在需要分割目标对象的图像中,如文本检测、目标检测、图像分割等任务中经常使用。

2.2.2 进一步处理 

#定义核大小

kernel = np.ones((15,15),dtype=np.uint8)

#黑帽
blackhat = cv2.morphologyEx(adaptiveThresh, cv2.MORPH_BLACKHAT, kernel)
#cv2.imshow('blackhat',blackhat)

#定义核大小

kernel = np.ones((3,3),dtype=np.uint8)

#顶帽
opening = cv2.morphologyEx(blackhat, cv2.MORPH_OPEN, kernel)
#cv2.imshow('opening',opening)

#搜集图像中形状的轮廓

contours, hierarchy = cv2.findContours(opening,cv2.RETR_TREE,
                                       cv2.CHAIN_APPROX_SIMPLE)

#过滤掉非目标形状轮廓
for i in range(len(contours)):
    x,y,w,h = cv2.boundingRect(contours[i])
    aspect_ratio = float(w)/h
    Area = w * h
    if Area<1800 or Area>6000:
        opening = cv2.drawContours(opening, contours, i, (0,0,0), -1)
    else:
        if aspect_ratio>0.7 or aspect_ratio<0.5:
            opening = cv2.drawContours(opening, contours, i, (0,0,0), -1)
cv2.imshow('opening2',opening)

kernel = np.ones((5,5),np.uint8)
dilation = cv2.dilate(opening,kernel,iterations = 1)
cv2.imshow('dilation',dilation)

最终的输出:

输入:

 

形态学变换是一组基于图像形状的操作,通常用于图像预处理、特征提取和图像分析等任务。以下是常见的形态学变换操作:

腐蚀(Erosion):

腐蚀操作通过沿着图像边界移动的结构元素(通常是一个小的二值化核)来“侵蚀”图像的边界。如果结构元素与图像中的像素完全匹配,则保留像素值,否则将像素设置为0。腐蚀操作可以用于去除图像中的噪声、分离连接的对象等。

膨胀(Dilation):

膨胀操作与腐蚀相反,它通过将结构元素与图像中的像素进行匹配来“膨胀”图像的边界。如果结构元素的至少一个像素与图像中的像素匹配,则将像素设置为1。膨胀操作可以用于填充图像中的空洞、连接接近的对象等。

开运算(Opening):

开运算是先进行腐蚀操作,然后进行膨胀操作的组合。它可以用于去除图像中的小的干扰对象、平滑对象的边界等。

闭运算(Closing):

闭运算是先进行膨胀操作,然后进行腐蚀操作的组合。它可以用于填充对象的孔洞、连接相邻的对象等。

顶帽(Top Hat)和黑帽(Black Hat)变换:

顶帽变换是原始图像与开运算结果之间的差异,它可以用于提取图像中的小的亮区域。

黑帽变换是原始图像与闭运算结果之间的差异,它可以用于提取图像中的小的暗区域。

这些形态学变换操作通常用于图像的预处理和分析,能够改善图像的质量、增强图像的特征,并有助于后续的图像处理任务。

2.3 处理模板

#读取模板图像

numTemplate=cv2.imread('bankCardNumTemplate.jpg')

#灰度化
numTemplate_GRAY=cv2.cvtColor(numTemplate,cv2.COLOR_BGR2GRAY)

#自适应阈值化
ret,numTemplate_GRAY = cv2.threshold(numTemplate_GRAY,200,255,cv2.THRESH_BINARY)
cv2.imshow('numTemplate_GRAY',numTemplate_GRAY)

#搜集模板图像中数字形状,并将每一个形状命名

def sequence_contours(image, width, height):
    contours, hierarchy = cv2.findContours(image,cv2.RETR_EXTERNAL,
                                           cv2.CHAIN_APPROX_SIMPLE)

    n = len(contours)
    RectBoxes0 = np.ones((n,4),dtype=int)
    for i in range(n):
        RectBoxes0[i] = cv2.boundingRect(contours[i])
#重新排序,从小到大    
    RectBoxes = np.ones((n,4),dtype=int)
    for i in range(n):
        sequence = 0
        for j in range(n):
            if RectBoxes0[i][0]>RectBoxes0[j][0]:
                sequence = sequence + 1
        RectBoxes[sequence] = RectBoxes0[i]
 #获取形状
    ImgBoxes = [[] for i in range(n)]
    for i in range(n):
        x,y,w,h = RectBoxes[i]
        ROI = image[y:y+h,x:x+w]
        ROI = cv2.resize(ROI, (width, height))
        thresh_val, ROI = cv2.threshold(ROI, 200, 255, cv2.THRESH_BINARY)
        ImgBoxes[i] = ROI
        
    return RectBoxes, ImgBoxes

     contours, hierarchy = cv2.findContours(image,cv2.RETR_EXTERNAL,
                                           cv2.CHAIN_APPROX_SIMPLE)

这行代码使用OpenCV中的findContours函数来查找图像中的轮廓。让我解释一下这段代码的参数:

  • image: 这是输入的二值化图像,通常是经过阈值化处理后的图像,用于查找轮廓。
  • cv2.RETR_EXTERNAL: 这个参数指定了轮廓的检索模式。RETR_EXTERNAL表示只检索最外层的轮廓,不检索内部的轮廓。
  • cv2.CHAIN_APPROX_SIMPLE: 这个参数指定了轮廓的近似方法。CHAIN_APPROX_SIMPLE表示对轮廓的边界点进行压缩,只保留终点坐标,例如一个矩形轮廓只需四个角点。

该函数会返回两个值:

  • contours: 这是一个Python列表,包含了检测到的轮廓。每个轮廓都是一个包含点坐标的numpy数组。
  • hierarchy: 这是一个包含轮廓层级信息的数组。它可以用来确定轮廓之间的关系,如父子关系、内部轮廓等。

在这段代码中,我们使用RETR_EXTERNAL模式,因此contours列表中的每个元素都是图像中最外部的一个轮廓。然后我们可以对这些轮廓进行操作,如绘制、计算轮廓的面积等。

    for i in range(n):
        RectBoxes0[i] = cv2.boundingRect(contours[i])

在OpenCV中,contours列表中轮廓的排列顺序与它们的层级关系有关,而层级关系由hierarchy数组提供。hierarchy数组中每个元素对应于contours列表中的一个轮廓,它包含了关于该轮廓的层级信息。

hierarchy数组的每个元素都是一个四元组 [Next, Previous, First_Child, Parent],其中:

  • Next 表示后一个轮廓的索引,如果没有则为-1;
  • Previous 表示前一个轮廓的索引,如果没有则为-1;
  • First_Child 表示第一个子轮廓的索引,如果没有子轮廓则为-1;
  • Parent 表示父轮廓的索引,如果没有父轮廓则为-1。

根据这些信息,轮廓的排列顺序可以理解为:

  1. Next 表示的是在同一层级上下一个轮廓的索引,因此轮廓按照这个顺序排列。
  2. First_Child 表示的是子轮廓的索引,如果存在子轮廓,那么这些子轮廓将在父轮廓之后排列。

这意味着轮廓的排列顺序在一定程度上是根据它们在图像中的位置和相对关系确定的,而层级关系则决定了轮廓的嵌套和父子关系。

截取模板图像的示例

2.4 模板匹配

result = []
for i in range(len(ImgBoxes)):
    score = np.zeros(len(ImgBoxes_Temp),dtype=int)
    for j in range(len(ImgBoxes_Temp)):
        score[j] = cv2.matchTemplate(ImgBoxes[i], ImgBoxes_Temp[j], cv2.TM_SQDIFF)
    min_val, max_val, min_indx, max_indx = cv2.minMaxLoc(score)
    result.append(min_indx[1])
print(result)

         score[j] = cv2.matchTemplate(ImgBoxes[i], ImgBoxes_Temp[j], cv2.TM_SQDIFF)

这行代码使用了OpenCV中的matchTemplate函数,该函数用于在输入图像中寻找模板图像的匹配位置。让我解释一下这行代码的参数:

  • ImgBoxes[i]: 这是输入图像,通常是被搜索的大图像。
  • ImgBoxes_Temp[j]: 这是模板图像,是被用来在输入图像中搜索匹配位置的小图像。
  • cv2.TM_SQDIFF: 这是匹配方法,它指定了模板匹配的度量方式。TM_SQDIFF表示使用平方差匹配法,即通过计算输入图像和模板图像之间的像素差的平方来度量相似度,得到的值越小表示匹配度越高。

matchTemplate函数会返回一个二维数组,表示在输入图像中找到的与模板图像的匹配程度。在使用了TM_SQDIFF方法后,数组中的每个元素表示对应位置的像素值的平方差。通常,我们会找到这个数组中的最小值,以确定最佳匹配位置。

在你提供的代码中,score[j]被赋值为模板匹配结果,即输入图像中与模板图像最匹配的位置的匹配分数。

    min_val, max_val, min_indx, max_indx = cv2.minMaxLoc(score) 

这行代码使用了OpenCV中的minMaxLoc函数来找到匹配结果数组中的最小值和最大值,以及它们对应的位置。让我解释一下这行代码的参数:

  • score: 这是模板匹配的结果数组,即调用matchTemplate函数后得到的匹配分数数组。

minMaxLoc函数会返回四个值:

  • min_val: 这是匹配结果数组中的最小值。
  • max_val: 这是匹配结果数组中的最大值。
  • min_loc: 这是最小值的位置,即最佳匹配的位置之一。
  • max_loc: 这是最大值的位置,通常不会被使用。

在这行代码中,你选择了只接收最小值及其位置,因此min_val保存了匹配结果数组中的最小值,而min_indx保存了最小值的位置。这样你就可以确定在输入图像中最佳匹配的位置了。

 

  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要实现银行卡识别,可以使用OpenCV和Python。以下是一个简单的银行卡识别程序的示例代码: ```python import cv2 import numpy as np # 读取图片 img = cv2.imread('bank_card.jpg') # 灰度化 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 边缘检测 edges = cv2.Canny(gray, 50, 150, apertureSize=3) # 膨胀操作 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (17, 5)) dilated = cv2.dilate(edges, kernel) # 查找轮廓 contours, hierarchy = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 提取数字区域 num_regions = [] for contour in contours: # 计算轮廓面积 area = cv2.contourArea(contour) if 1000 < area < 5000: # 获取数字区域的矩形坐标 x, y, w, h = cv2.boundingRect(contour) num_regions.append((x, y, w, h)) # 对数字区域进行排序,按照从左到右的顺序 num_regions = sorted(num_regions, key=lambda x: x[0]) # 识别银行卡 bank_card_number = '' for num_region in num_regions: x, y, w, h = num_region num_img = gray[y:y+h, x:x+w] # 对数字图片进行二值化处理 ret, thresh = cv2.threshold(num_img, 100, 255, cv2.THRESH_BINARY) # 将二值化后的数字图片转换为字符串 num_str = pytesseract.image_to_string(thresh, config='--psm 10 --oem 3 -c tessedit_char_whitelist=0123456789') bank_card_number += num_str print('银行卡:', bank_card_number) ``` 在这个示例代码中,我们首先读入一张银行卡的图片,然后对其进行灰度化、边缘检测、膨胀操作和轮廓查找等处理。接着,我们通过计算轮廓面积,筛选出数字区域,并按照从左到右的顺序对数字区域进行排序。最后,我们通过Tesseract OCR引擎对每个数字区域进行识别,并将识别结果拼接起来,得到银行卡。 请注意,这个示例代码并不完美,可能会在某些情况下出现识别错误。如果您需要更精确的银行卡识别效果,可以考虑使用更复杂的算法和模型。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值