Opencv实战案例——模板匹配实现银行卡号识别(附详细介绍及完整代码下载地址)

news2024/11/20 6:24:07

在这里插入图片描述

Opencv目录

  • 1.项目意义
  • 2.模板匹配
  • 3.图像二值化
    • 3.1全局阈值
    • 3.2全局阈值代码即效果展示
    • 3.3 自适应阈值
    • 3.4自适应阈值代码即效果展示
  • 4.轮廓筛选
    • 4.1轮廓检测
    • 4.2绘制轮廓
    • 4.3轮廓筛选代码及效果展示
  • 5.形态学变化
    • 5.1腐蚀
    • 5.2膨胀
    • 5.3开运算和闭运算、礼帽和黑帽
  • 6.项目实战
    • 6.1读取图片转化为灰度图
    • 6.2自适应阈值处理
  • 6.3第一次寻找合适的轮廓
    • 6.4黑帽+腐蚀操作
    • 6.5再次寻找轮廓+膨胀操作
    • 6.6 模板匹配
  • 7.完整代码
  • 8.总结

1.项目意义

在日常生活中,常常需要输入自己的银行卡号。银行为保证卡号的唯一性和账号的安全性,会将卡号设计偏长,对于视力不好的人群以及老人不是很友好。传统银行卡服务时的人工识别银行卡号码太过费时费力。所以银行卡号的自动识别变得越来越重要。银行卡卡号的自动识别对实现银行卡的有效管理和进行银行卡的相关服务具有重要的理论意义和实际应用价值。本项目设计了一个银行卡卡号自动识别程序,传入银行卡照片即可获取银行卡号,为生活提供了遍历。。程序效果如下:

2.模板匹配

本项目中主要使用的方法为模板匹配,在opencv中也提供了API,为cv2.matchTemplate函数,现在我们一起来看一下这个函数的用法。
cv2.matchTemplate(img,img_Temp,method)

  • img:传入的需要匹配的图片

  • img_Temp:匹配的模板

  • method:模板匹配的算法,主要有:

    • 平方差匹配(cv.TM_SQDIFF):利用模板与图像之间的平方差进行匹配,最好的匹配是0,匹配越差,匹配的值越大。
    • 相关匹配(cv.TM_CCORR):利用模板与图像间的乘法进行匹配,数值越大表示匹配程度较高,越小表示匹配效果差。
    • 利用相关系数匹配(cv.TM_CCOEFF):利用模板与图像间的相关系数匹配,1表示完美的匹配,-1表示最差的匹配。
    • cv.TM_SQDIFF_NORMED
    • cv.TM_CCORR_NORMED
    • cv.TM_CCOEFF_NORMED

在这里详细介绍一下我这个项目所使用的cv.TM_SQDIFF算法,这也是在模板匹配中最常用的算法,算法的公式为:
∑ x , y [ Img ⁡ ( x , y ) −  imgTemp  ( x , y ) ] 2 \sum_{x, y}[\operatorname{Img}(x, y)-\text { imgTemp }(x, y)]^2 x,y[Img(x,y) imgTemp (x,y)]2
这与我们在机器学习中所提到的最小二乘法非常相似,都是先求对应位置的差,再平方变成正数,最后求和。从这个算法我们不能发现模板匹配也有一定的局限性:只能对特定的图片进行模板匹配,如果匹配目标发生旋转或大小变化那么算法失效!

本次项目所用的模板如下:[Alt]

3.图像二值化

二值化是图像分割的一种最简单的方法。二值化可以把灰度图像转换成二值图像。把大于某个临界灰度值(阈值)的像素灰度设为灰度极大值(255),把小于这个值的像素灰度设为灰度极小值(0),从而实现二值化,简单来说:设定一个阈值valve,对于视频信号矩阵中的每一行,从左至右比较各像素值和阈值的大小,若图像灰度值大于或等于阈值,则判定该像素对应的255;反之,小于阈值的灰度值则为0。就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的黑白效果。

作用:去除干扰信息,方便后期的处理

图像的二值化分为全局阈值以及自适应阈值,下面我们分别来进行讲解。

3.1全局阈值

全局阈值即规定一个数字,将大于这个数字的像素全部变为指定的一个数,小于这个值的变为另一个数。我们首先看API函数,然后以具体以的例子来做讲解。对应全局阈值,opencv提供的API为:
cv2.threshold(src,thresh,maxVal,tpye)

  • src:输入的图片对象

  • thresh:阈值

  • maxVal:归成的最大值,归成的最小值为0,也就是黑色

  • tpye:cv2.THRESH_BINARY和cv2.THRESH_BINARY_INV

    • cv2.THRESH_BINARY:
      在这里插入图片描述
    • cv2.THRESH_BINARY_INV
      在这里插入图片描述

3.2全局阈值代码即效果展示

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :OpenCV识别银行卡数字 
@File    :test.py
@IDE     :PyCharm 
@Author  :咋
@Date    :2023/1/14 17:17 
"""
import cv2
import numpy as np
# 读取图片
image = cv2.imread("Handsome.jpg")
# 转化为灰度图
image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
# 调整图片大小
image = cv2.resize(image,(640,480))
# 全局阈值处理
thresh,threshold_img = cv2.threshold(image,120,255,cv2.THRESH_BINARY)
# 展示图片
cv2.imshow("image",np.hstack((image,threshold_img)))
# cv2.imshow("image",image)
cv2.waitKey(0)
# 销毁窗口
cv2.destroyAllWindows()

在这里插入图片描述

3.3 自适应阈值

自适应阈值是根据周围的像素来确定当前的像素,同样我们结合AIP来讲解其中的原理。在opencv中为我们提供了自适应阈值的函数,为cv2.adaptiveThreshold()
cv2.adapptiveThreshold(src,maxVal,adaptiveMethod,type,blockSize,C)

  • src:输入的图片对象

  • maxVal:与全局阈值一样,指规定的最大值

  • adaptiveMethod:着重讲解一下我们项目中所用到的cv2.ADAPTIVE_THRESH_MEAN_C
    在这里插入图片描述

    • cv2.ADAPTIVE_THRESH_MEAN_C取周围的像素作为判断当前像素值的依据,当blockSize为3时,即取3*3的卷积核,thresh = 9个像素平均灰度-C,那么图上的就等于90-10=80。这里的C即为一个常数,可以理解为偏置。此次项目中选择的tpye就是cv2.THRESH_BINARY,它的函数我们之前也提到过:
      在这里插入图片描述
  • tpye:即为:cv2.THRESH_BINARY和cv2.THRESH_BINARY_INV

  • blockSize:领域的大小,和卷积核非常相似,对此不熟悉的可以看我之前的CNN卷积神经网络的猫狗识别这篇文章:CNN卷积神经网络的猫狗识别

3.4自适应阈值代码即效果展示

在这里插入图片描述

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :OpenCV识别银行卡数字 
@File    :全局阈值.py
@IDE     :PyCharm 
@Author  :咋
@Date    :2023/1/14 17:17 
"""
import cv2
import numpy as np
# 读取图片
image = cv2.imread("cat_dog.jpg")
# 转化为灰度图
image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
# 调整图片大小
image = cv2.resize(image,(640,480))
# 全局阈值处理,注意有两个返回值,所以需要两个变量来接受
thresh,threshold_img = cv2.threshold(image,120,255,cv2.THRESH_BINARY)
# 展示图片
cv2.imshow("image",np.hstack((image,threshold_img)))
# cv2.imshow("image",image)
cv2.waitKey(0)
# 销毁窗口
cv2.destroyAllWindows()

4.轮廓筛选

4.1轮廓检测

我直接来看API,在opencv中也给我们提供了轮廓检测的函数,在这里多说一句,由于opencv底层是C++写的,所以很多代码命名规范与Python不同,所以在python中调用API的时候,一定要分清楚大小写。轮廓检测的函数是cv2.findContours()
cv2.findContours(image,mode,method)

  • image:传入的需要进行处理的图片
  • mode:轮廓检索的方式:

在这里插入图片描述

  • method:轮廓近似方法:
    在这里插入图片描述

在此次项目中,我们轮廓检索模式为cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE。值得注意的是,返回值有两个,我们需要的是第一个contours,第二个hierarchy对我们没什么价值,我们用一个参数接收就可以了,重要的是contours,我们后面的很多操作都要用到检测出来的轮廓列表。

4.2绘制轮廓

在上面检测完轮廓之后返回了一个轮廓列表,在opencv中也给我们提供了绘制轮廓的API:cv2.drawContours(image,contours,i,color,thickness)

  • image:需要绘制的图片
  • contours:上述检测出来的轮廓列表
  • i:列表中第i个轮廓,如果选-1的话即所有轮廓
  • color:绘制的颜色
  • thickness:线宽,-1即填充

4.3轮廓筛选代码及效果展示

在这里插入图片描述

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :OpenCV识别银行卡数字 
@File    :轮廓检测.py
@IDE     :PyCharm 
@Author  :咋
@Date    :2023/1/14 18:30 
"""
import cv2
import numpy as np
# 读取图片
image = cv2.imread("img.png")
# 转化为灰度图
image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
# 调整图片大小
image = cv2.resize(image,(640,480))
true_img = image.copy()
# 自适应阈值处理
threshold_img = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,21,-10)
cv2.imshow("threshold_img",threshold_img)
# 轮廓检测
contours,hierarchy = cv2.findContours(threshold_img,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
print(contours)
image_draw = cv2.drawContours(image,contours,-1,(0,255,255),5)
# 展示图片
cv2.imshow("image",np.hstack((true_img,image_draw)))
# cv2.imshow("image",image_draw)
cv2.waitKey(0)
# 销毁窗口
cv2.destroyAllWindows()

5.形态学变化

5.1腐蚀

cv2.erode(image,kernel,iterations)

  • image:需要进行操作的图片对象
  • kernel:领域的大小
  • iterations:迭代的次数

腐蚀的原理很简单,就是用规定领域内像素的最小值代替当前的像素值,迭代次数就是腐蚀一边之后,是否还要腐蚀多次,腐蚀越多,图片的像素越小,图片也会越黑。

5.2膨胀

cv2.dilate(img,kernel,iterations)

  • image:需要进行操作的图片对象
  • kernel:领域的大小
  • iterations:迭代的次数

膨胀的原理和腐蚀非常相似,只不过膨胀是用邻域内像素的最大值代替当前的像素值,其他的参数意义一样。

5.3开运算和闭运算、礼帽和黑帽

开运算与闭运算以及下面所要介绍的礼帽黑帽所用的API的是一样的,只不是是op参数不同。
cv2.morghologyEx(image,op,kernel)

  • cv2.MORPH_OPEN:开运算,腐蚀—>膨胀
  • cv2.MORPH_CLOSE:闭运算,膨胀—>腐蚀
  • cv2.MORPH_TOPHAT:礼帽,开运算减原图 得到噪声图像
  • cv2.MORPH_BLACKHAT:黑帽 闭运算减原图 得到图像内的孔洞
    开运算
    含义:先腐蚀后膨胀,去除背景(黑)中的噪声
    闭运算
    含义:先膨胀后腐蚀,去除背景(白)中的黑色孔洞
    在这里插入图片描述
    礼帽
    cv2.
    含义:开运算与原图做差
    黑帽
    cv2.
    含义:闭运算与原图做差
    在这里插入图片描述

6.项目实战

6.1读取图片转化为灰度图

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :OpenCV识别银行卡数字 
@File    :6.1导入包读取图片.py
@IDE     :PyCharm 
@Author  :咋
@Date    :2023/1/14 19:54 
"""
import cv2
import numpy as np
# 读取图片
image = cv2.imread("bank_card41.jpg")
# 转化为灰度图
image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
# 调整图片大小
image = cv2.resize(image,(4*image.shape[1],4*image.shape[0]))
cv2.imshow("image",image)
cv2.waitKey(0)
# 销毁窗口
cv2.destroyAllWindows()

在这里插入图片描述

6.2自适应阈值处理

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :OpenCV识别银行卡数字 
@File    :6.2自适应阈值处理.py
@IDE     :PyCharm 
@Author  :咋
@Date    :2023/1/14 19:57 
"""
import cv2
import numpy as np
# 读取图片
image = cv2.imread("bank_card4.jpg")
# 转化为灰度图
image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
# 调整图片大小
image = cv2.resize(image,(640,480))
# 自适应阈值处理
threshold_img = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,21,3)
# 取反操作
thresh = cv2.bitwise_not(threshold_img)
cv2.imshow("image",np.hstack((image,thresh)))
cv2.waitKey(0)
# 销毁窗口
cv2.destroyAllWindows()

在这里插入图片描述

6.3第一次寻找合适的轮廓

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :OpenCV识别银行卡数字 
@File    :6.3寻找合适的轮廓.py
@IDE     :PyCharm 
@Author  :咋
@Date    :2023/1/14 19:59 
"""
import cv2
import numpy as np
# 读取图片
image = cv2.imread("bank_card41.jpg")
# 转化为灰度图
image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
# 调整图片大小
image = cv2.resize(image,(4*image.shape[1],4*image.shape[0]))
# 自适应阈值处理
threshold_img = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,21,3)
# 取反操作
thresh = cv2.bitwise_not(threshold_img)
# 寻找合适的轮廓
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
print(contours)
# 判断合适的轮廓
for i in range(len(contours)):
    # 根据轮廓的面积
    if cv2.contourArea(contours[i]) < 60:
        thresh = cv2.drawContours(thresh, contours, i, (0,0,0), -1)

cv2.imshow("image",np.vstack((image,thresh)))
cv2.waitKey(0)
# 销毁窗口
cv2.destroyAllWindows()

6.4黑帽+腐蚀操作

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :OpenCV识别银行卡数字 
@File    :6.4黑帽+腐蚀py
@IDE     :PyCharm 
@Author  :咋
@Date    :2023/1/14 20:12 
"""
import cv2
import numpy as np
# 读取图片
image = cv2.imread("bank_card41.jpg")
# 转化为灰度图
image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
# 调整图片大小
image = cv2.resize(image,(4*image.shape[1],4*image.shape[0]))
# 自适应阈值处理
threshold_img = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,21,3)
# 取反操作
thresh = cv2.bitwise_not(threshold_img)
# 寻找合适的轮廓
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
print(contours)
# 判断合适的轮廓
for i in range(len(contours)):
    # 根据轮廓的面积
    if cv2.contourArea(contours[i]) < 60:
        thresh = cv2.drawContours(thresh, contours, i, (0,0,0), -1)
# 黑帽操作
kernel = np.ones((15,15),dtype=np.uint8)
blackhat = cv2.morphologyEx(thresh, cv2.MORPH_BLACKHAT, kernel)
# 腐蚀操作
kernel = np.ones((3,3),dtype=np.uint8)
erosion = cv2.erode(blackhat,kernel,iterations = 1)
cv2.imshow("image",np.vstack((image,erosion)))
cv2.waitKey(0)
# 销毁窗口
cv2.destroyAllWindows()

在这里插入图片描述

6.5再次寻找轮廓+膨胀操作

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :OpenCV识别银行卡数字 
@File    :再次寻找轮廓+膨胀操作.py
@IDE     :PyCharm 
@Author  :咋
@Date    :2023/1/14 20:21 
"""
import cv2
import numpy as np
# 读取图片
image = cv2.imread("bank_card41.jpg")
# 转化为灰度图
image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
# 调整图片大小
image = cv2.resize(image,(4*image.shape[1],4*image.shape[0]))
# 自适应阈值处理
threshold_img = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,9,3)
# 取反操作
thresh = cv2.bitwise_not(threshold_img)
# 寻找合适的轮廓
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
print(contours)
# 判断合适的轮廓
for i in range(len(contours)):
    # 根据轮廓的面积
    if cv2.contourArea(contours[i]) < 60:
        thresh = cv2.drawContours(thresh, contours, i, (0,0,0), -1)
# 黑帽操作
kernel = np.ones((15,15),dtype=np.uint8)
blackhat = cv2.morphologyEx(thresh, cv2.MORPH_BLACKHAT, kernel)
# 腐蚀操作
kernel = np.ones((3,3),dtype=np.uint8)
erosion = cv2.erode(blackhat,kernel,iterations = 1)

contours, hierarchy = cv2.findContours(erosion,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
for i in range(len(contours)):
    x,y,w,h = cv2.boundingRect(contours[i])
    aspect_ratio = float(w)/h
    A = float(w)*h
    if A < 1800:
        erosion = cv2.drawContours(erosion, contours, i, (0,0,0), -1)
    else:
        if aspect_ratio > 0.6 or aspect_ratio < 0.57:
            erosion = cv2.drawContours(erosion, contours, i, (0,0,0), -1)
cv2.imshow('erosion2',erosion)

kernel = np.ones((7,7),dtype=np.uint8)
dilation = cv2.dilate(erosion,kernel,iterations = 1)
# cv2.imshow('dilation',)
cv2.imshow("image",np.vstack((image,dilation)))
cv2.waitKey(0)
# 销毁窗口
cv2.destroyAllWindows()

在这里插入图片描述

6.6 模板匹配

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('thresh',thresh)
# cv2.imshow('numTemplate_GRAY', numTemplate_GRAY)
contours, hierarchy = cv2.findContours(numTemplate_GRAY, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# print(np.size(contours))


def sequence_contours(dst_Binary, method, Rect_width, Rect_height):
    if method == "Left_to_Right" or method == 2:
        contours, hierarchy = cv2.findContours(dst_Binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        n = np.size(contours)
        RectBoxes0 = np.ones((n, 4), dtype=int)
        # print(n)
        for i in range(0, n):
            RectBoxes0[i] = cv2.boundingRect(contours[i])
            # print(RectBoxes0)

        RectBoxes = np.ones((n, 4), dtype=int)

        for i in range(0, n):
            sequence = 0
            for j in range(0, n):
                if RectBoxes0[i][0] > RectBoxes0[j][0]:
                    sequence = sequence + 1
            RectBoxes[sequence] = RectBoxes0[i]

        RectImgBoxes = [[] for i in range(n)]
        for i in range(0, n):
            img = dst_Binary[RectBoxes[i, 1]:(RectBoxes[i, 1] + RectBoxes[i, 3]),
                  RectBoxes[i, 0]:(RectBoxes[i, 0] + RectBoxes[i, 2])]
            # cv2.imshow('number'+ str(i), img)
            img = cv2.resize(img, (Rect_width, Rect_height))
            ret, img = cv2.threshold(img, 200, 255, cv2.THRESH_BINARY)
            RectImgBoxes[i] = img
        # print(RectBoxes)
        # print(np.size(RectImgBoxes))

    return RectBoxes, RectImgBoxes


RectBoxes_Temp, RectImgBoxes_Temp = sequence_contours(numTemplate_GRAY, method="Left_to_Right", Rect_width=50,
                                                      Rect_height=80)
# print(RectBoxes)
# cv2.imshow('numberTemp', RectImgBoxes_Temp[3])

RectBoxes, RectImgBoxes = sequence_contours(dilation, method="Left_to_Right", Rect_width=50, Rect_height=80)
# cv2.imshow('numberfin', RectImgBoxes[3])

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

# 定义添加数字到图片的函数
def add_num(image,num_list):
    text = ""
    for i in num_list:
        text += str(i)
    font = ImageFont.truetype("msyh.ttc",50)
    # 创建一个pillow的图片
    pil_img = Image.fromarray(image)
    # 绘制图片
    draw = ImageDraw.Draw(pil_img)
    # 利用draw去绘制中文
    draw.text((0, 0), text , font=font, fill=0)  # 后面的fill即颜色,RGBA
    # 重新变为ndarray
    image = np.array(pil_img)
    return image
print(result)
image = add_num(image,result)
cv2.imshow("image",image)
# cv2.imshow("image",np.vstack((image,dilation)))
cv2.waitKey(0)

在这里插入图片描述

7.完整代码

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project :OpenCV识别银行卡数字 
@File    :模板匹配.py
@IDE     :PyCharm
@Author  :咋
@Date    :2023/1/14 20:29
"""
import cv2
import numpy as np
from PIL import ImageFont,ImageDraw,Image
# 读取图片
image = cv2.imread("bank_card41.jpg")
true_img = cv2.imread("bank_card4.jpg")
# 转化为灰度图
image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
# 调整图片大小
image = cv2.resize(image,(4*image.shape[1],4*image.shape[0]))
# 自适应阈值处理
threshold_img = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,9,3)
# 取反操作
thresh = cv2.bitwise_not(threshold_img)
# 寻找合适的轮廓
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
# print(contours)
# 判断合适的轮廓
for i in range(len(contours)):
    # 根据轮廓的面积
    if cv2.contourArea(contours[i]) < 60:
        thresh = cv2.drawContours(thresh, contours, i, (0,0,0), -1)
# 黑帽操作
kernel = np.ones((15,15),dtype=np.uint8)
blackhat = cv2.morphologyEx(thresh, cv2.MORPH_BLACKHAT, kernel)
# 腐蚀操作
kernel = np.ones((3,3),dtype=np.uint8)
erosion = cv2.erode(blackhat,kernel,iterations = 1)

contours, hierarchy = cv2.findContours(erosion,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
for i in range(len(contours)):
    x,y,w,h = cv2.boundingRect(contours[i])
    aspect_ratio = float(w)/h
    A = float(w)*h
    if A < 1800:
        erosion = cv2.drawContours(erosion, contours, i, (0,0,0), -1)
    else:
        if aspect_ratio > 0.6 or aspect_ratio < 0.57:
            erosion = cv2.drawContours(erosion, contours, i, (0,0,0), -1)
# cv2.imshow('erosion2',erosion)

kernel = np.ones((7,7),dtype=np.uint8)
dilation = cv2.dilate(erosion,kernel,iterations = 1)
# 模板匹配
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('thresh',thresh)
# cv2.imshow('numTemplate_GRAY', numTemplate_GRAY)
contours, hierarchy = cv2.findContours(numTemplate_GRAY, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# print(np.size(contours))


def sequence_contours(dst_Binary, method, Rect_width, Rect_height):
    if method == "Left_to_Right" or method == 2:
        contours, hierarchy = cv2.findContours(dst_Binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        n = np.size(contours)
        RectBoxes0 = np.ones((n, 4), dtype=int)
        # print(n)
        for i in range(0, n):
            RectBoxes0[i] = cv2.boundingRect(contours[i])
            # print(RectBoxes0)

        RectBoxes = np.ones((n, 4), dtype=int)

        for i in range(0, n):
            sequence = 0
            for j in range(0, n):
                if RectBoxes0[i][0] > RectBoxes0[j][0]:
                    sequence = sequence + 1
            RectBoxes[sequence] = RectBoxes0[i]

        RectImgBoxes = [[] for i in range(n)]
        for i in range(0, n):
            img = dst_Binary[RectBoxes[i, 1]:(RectBoxes[i, 1] + RectBoxes[i, 3]),
                  RectBoxes[i, 0]:(RectBoxes[i, 0] + RectBoxes[i, 2])]
            # cv2.imshow('number'+ str(i), img)
            img = cv2.resize(img, (Rect_width, Rect_height))
            ret, img = cv2.threshold(img, 200, 255, cv2.THRESH_BINARY)
            RectImgBoxes[i] = img
        # print(RectBoxes)
        # print(np.size(RectImgBoxes))

    return RectBoxes, RectImgBoxes


RectBoxes_Temp, RectImgBoxes_Temp = sequence_contours(numTemplate_GRAY, method="Left_to_Right", Rect_width=50,
                                                      Rect_height=80)
# print(RectBoxes)
# cv2.imshow('numberTemp', RectImgBoxes_Temp[3])

RectBoxes, RectImgBoxes = sequence_contours(dilation, method="Left_to_Right", Rect_width=50, Rect_height=80)
# cv2.imshow('numberfin', RectImgBoxes[3])

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

# 定义添加数字到图片的函数
def add_num(image,num_list):
    text = ""
    for i in num_list:
        text += str(i)
    font = ImageFont.truetype("msyh.ttc",40)
    # 创建一个pillow的图片
    pil_img = Image.fromarray(image)
    # 绘制图片
    draw = ImageDraw.Draw(pil_img)
    # 利用draw去绘制中文
    draw.text((15, 80), text , font=font, fill=0)  # 后面的fill即颜色,RGBA
    # 重新变为ndarray
    image = np.array(pil_img)
    return image
print(result)
true_img = add_num(true_img,result)
cv2.imshow("image",true_img)
# cv2.imshow("image",np.vstack((image,dilation)))
cv2.waitKey(0)

附上图片和代码链接:欢迎需要的小伙伴自取链接https://pan.baidu.com/s/1UeXrFjLXn3LWiDoUudW13w?pwd=3hye
提取码:3hye

8.总结

识别银行卡可算是Opencv的经典项目了,尤其是模板匹配,算得上是计算机视觉的精髓所在了。通过这个案例对二值化、轮廓检测、形态学变化腐蚀膨胀、开运算闭运算、礼帽黑帽有所了解,并能够组合在一起运算,算得上有所进步。
以此类推,模板匹配可以应用在很多项目中,关键的难点就在于怎么把图片和模板调整好,通过上述的方法以及参数的调整,可以达到这样的效果,还是需要多练习,才能真正掌握Opencv’的精髓。

原创不易,还希望各位大佬支持一下 \textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下

👍 点赞,你的认可是我创作的动力! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!

⭐️ 收藏,你的青睐是我努力的方向! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!

✏️ 评论,你的意见是我进步的财富! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/163101.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

next_permutation函数讲解

目录 前言&#xff1a; 简要概述&#xff1a; 例题&#xff08;1&#xff09;&#xff1a; P1088 [NOIP2004 普及组] 火星人 题目描述 输入格式 输出格式 输入输出样例 说明/提示 代码&#xff08;1&#xff09;&#xff1a; 例题&#xff08;2&#xff09;&#xf…

如何开发 Vite 3 插件构建 Electron 开发环境?(文末附视频讲解)

开发新版本 Vue 项目推荐你使用 Vite 脚手架构建开发环境&#xff0c;然而 Vite 脚手架更倾向于构建纯 Web 页面&#xff0c;而不是桌面应用&#xff0c;因此开发者要做很多额外的配置和开发工作才能把 Electron 引入到 Vue 项目中&#xff0c;这也是很多开发者都基于开源工具来…

用户多兴趣建模MIND

1. 概述 在工业界&#xff0c;一个完整的推荐系统中通常包括两个阶段&#xff0c;分别为召回阶段和排序阶段。在召回阶段&#xff0c;根据用户的兴趣从海量的商品中去检索出用户&#xff08;User&#xff09;可能感兴趣的候选商品&#xff08; Item&#xff09;&#xff0c;满…

JavaEE进阶第二课:Spring创建与使用

上一篇我们介绍了Spring的概念&#xff0c;知道了Spring是众多工具方法的IoC容器。 但是纸上谈兵终觉浅&#xff0c;这一篇文章就来介绍Spring创建与使用&#xff0c; 注之后我们对对象的称呼就叫Bean 1.1Spring项目的创建与配置 1.创建maven项目&#xff0c;语言选java&…

【1819. 序列中不同最大公约数的数目】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你一个由正整数组成的数组 nums 。 数字序列的 最大公约数 定义为序列中所有整数的共有约数中的最大整数。 例如&#xff0c;序列 [4,6,16] 的最大公约数是 2 。 数组的一个 子序列 本质是一个序…

Python(16):Numpy之array数组的数值计算

目录 0. 相关文章链接 1. 创建Array数组 2. 基本数值计算 2.1. numpy中的函数 2.2. 数组中的函数 3. 指定维度进行计算 3.1. numpy中的函数 3.2. 数组中的函数 4. 复杂计算 4.1. 统计乘机 4.2. 获取对应值的索引位置 4.3. 求平均值 4.4. 求标准差 4.5. 求方差 4…

【MFEN:轻量级多尺度特征提取:SR网络】

MFEN: Lightweight multi-scale feature extraction super-resolution network in embedded system &#xff08;MFEN&#xff1a;嵌入式轻量级多尺度特征提取超分辨率网络&#xff09; 深度卷积神经网络&#xff08;CNN&#xff09;在超分辨率&#xff08;SR&#xff09;方面…

基于java springboot+mybatis爱游旅行平台前台+后台设计实现

基于java springbootmybatis爱游旅行平台前台后台设计实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获…

PCI、PCI-X、PCI-E、PCI-E Card、Mini PCI-E、M.2、Add-in Card 这些概念你搞清楚了吗

搞硬件或通信的“攻城狮”们&#xff0c;免不了要和各种通信协议及接口打交道。比如&#xff0c;我们经常接触PCI、PCI-X、PCI-E、PCI-E Card、Mini PCI-E、M.2(NGFF)、Add-in Card这些概念&#xff0c;作为“攻城狮”队伍中的一员&#xff0c;你搞清楚它们之间的关系了吗&…

Linux第一个小程序-进度条

目录 \r&&\n 行缓冲区概念 倒计时程序 进度条代码 \r&&\n 回车概念换行概念 \n[rootVM-12-17-centos lesson8]# touch test.c [rootVM-12-17-centos lesson8]# touoch Makefile bash: touoch: command not found [rootVM-12-17-centos lesson8]# touch Mak…

Python:每日一题之完全二叉树的权值

题目描述 给定一棵包含 N 个节点的完全二叉树&#xff0c;树上每个节点都有一个权值&#xff0c;按从 上到下、从左到右的顺序依次是 A1​,A2​,⋅⋅⋅AN​&#xff0c;如下图所示&#xff1a; 现在小张要把相同深度的节点的权值加在一起&#xff0c;他想知道哪个深度的节点 权…

【Linux操作系统】Linux进程状态和两个特殊进程

文章目录一.一套普适性的进程状态理论1.运行2.阻塞3.挂起二.一套具体的Linux进程状态1.R-运行2.S-睡眠3.T-暂停5.t-被追踪三.僵尸进程和孤儿进程1.僵尸进程2.孤儿进程一.一套普适性的进程状态理论 1.运行 由于CPU数量相对于进程数量来说少之又少,所以CPU维护了一个运行队列,方…

Synchronized底层原理系列之Synchronized的偏向锁实现原理

作者简介&#xff1a;专注于研究Linux内核、Hotspot虚拟机、汇编语言、JDK源码、各大中间件源码等等喜欢的话&#xff0c;可以三连关注~上篇文章已经对Synchronized关键字做了初步的介绍&#xff0c;从字节码层面介绍了Synchronized关键字&#xff0c;最终字节码层面就是monito…

【Linux】 iptables 入门简介

文章目录前言作用持久化和恢复执行的顺序前言 简单地说&#xff0c;iptables是Linux的防火墙程序。它将使用表监控进出服务器的流量。这些表包含称为链的规则集&#xff0c;这些规则将过滤传入和传出数据包。 作用 当数据包与规则匹配的时候&#xff0c;会为其指定一个目标&a…

基于幂等表思想的幂等实践

一、为什么需要幂等 分布式场景下&#xff0c;多个业务系统间实现强一致的协议是极其困难的。一个最简单和可实现的假设就是保证最终一致性&#xff0c;这要求服务端在处理一个重复的请求时需要给出相同的回应&#xff0c;同时不会对持久化数据产生副作用&#xff08;即多次操…

【Linux】Linux下调试器gdb的使用

&#x1f451;作者主页&#xff1a;安 度 因 &#x1f3e0;学习社区&#xff1a;StackFrame &#x1f4d6;专栏链接&#xff1a;Linux 文章目录一、前言二、铺垫三、指令集和使用1、指令集2、演示四、结语如果无聊的话&#xff0c;就来逛逛 我的博客栈 吧! &#x1f339; 一、前…

通信原理与MATLAB(十三):AMI的编解码

目录1.AMI的的编解码原理1.1 AMI编码原理1.2 AMI解码原理2.AMI编解码的代码3.AMI编解码结果图4.AMI的误码率曲线4.1 原理4.2 AMI的误码率曲线代码4.3 误码率曲线图1.AMI的的编解码原理 1.1 AMI编码原理 如下图所示&#xff0c;AMI的编码原理:将原始码元的1转换成1,0转换成-1。…

快过年了,用Python康康哪一家足浴店可以带朋友去玩.....

人生苦短&#xff0c;我用Python 首先肯定是去正经足浴店&#xff0c; 毕竟一年出差也不少&#xff0c; 大家都很辛苦&#xff0c; 好不容易放假了&#xff0c; 约上好兄弟一起去放松放松~ 所需环境 python 3.8 解释器pycharm 编辑器 所需模块 requests 数据来源分析 …

Silane-PEG-NH2 氨基聚乙二醇硅烷 NH2-PEG-Silane结构式

英文名称&#xff1a;Silane-PEG-NH2 Silane-PEG-Amine 中文名称&#xff1a;硅烷-聚乙二醇-氨基 分子量&#xff1a;1k&#xff0c;2k&#xff0c;3.4k&#xff0c;5k&#xff0c;10k&#xff0c;20k。。。 存储条件&#xff1a;-20C&#xff0c;避光&#xff0c;避湿 用 途…

2022年度总结,迎接2023

目录 我和CSDN的2022 初次见面&#xff1a; 你我的成长&#xff1a; 博客&#xff1a; 比赛&#xff1a; 我和CSDN的2023 我和CSDN的2022 初次见面&#xff1a; CSDN你好啊&#xff01;我跟你的初次见面在于2022年4月2日&#xff01;&#xff01;&#xff01; 这这半年内…