opencv实现银行卡号识别

前言

近期在学习opencv的用法,跟着b站的教学视频做了一下,特来记录一下。

思路

大致可以分为以下几个步骤

  • 制作数字的模板,方便后面进行比对
  • 将银行卡的无关信息进行隐藏
  • 提取银行卡号

第一步相对简单一些,就是在下图中把所有的数字单独提取出来
在这里插入图片描述
总体的过程就是将图片转换为二值图像,然后直接把所有的外轮廓都提取出来,然后根据这些轮廓的左上角坐标来进行排序,在排序之后我们就获得了单独的数字图像,如下图
在这里插入图片描述

第二步就需要开始处理银行卡片上的多余信息了,以b站课程的图片为例
在这里插入图片描述
可以看出,卡面上有许多的多余信息,我们要做的就是尽量只留下银行卡号信息。
观察图片,我们可以发现大部分多余信息都是有着很粗的线条,如下图所示。
在这里插入图片描述
为了了解接下来的处理过程需要先了解几个概念

腐蚀操作:通俗来说,就是将图案中原来的粗的部分变细,让细的直接消失
在这里插入图片描述
如上图,当进行腐蚀操作之后就会变成下图
在这里插入图片描述
膨胀操作:就是把细的变粗,粗的更粗
上图在进行膨胀之后就变成了这样
在这里插入图片描述
开运算:先腐蚀在膨胀
礼帽:原图-开运算结果
在这里插入图片描述

现在我们就可以继续往下讲了,在银行卡图中,银行卡号明显比较细,这样我们就可以先腐蚀,在腐蚀之后,银行卡号数字就消失了,此时我们在膨胀,除了银行卡号的其他内容又几乎回到了腐蚀前,这时再用原图减去现在的图(即礼帽),就去掉了很多的多余信息。
看下对比图
仅仅是灰度图
在这里插入图片描述

礼帽之后
在这里插入图片描述
很明显,多余信息少了很多。

这时在使用sobel算子,搞出边界信息,在进行归一化之后,就变成下面这个样子(为啥进行归一化,不晓得。。。。)
在这里插入图片描述
为了方便处理,我们将图片转换为二值图像
在这里插入图片描述

这时就已经很明确了,我们需要的部分已经很清楚了,但是这四个框中有一些空缺,处理起来仍不是很方便,因此我们在对图像进行一次闭运算,得到图像如下图所示

在这里插入图片描述
这时就可以进行轮廓提取了,看一下效果,呈现在原图上是这样
在这里插入图片描述
这时再将数字的部分进行提取,然后逐个与我们之前提取的模板一一比对即可。
接下来开始搞代码

实现

制作模板

img = cv2.imread('template.jpg')
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值处理
ref = cv2.threshold(img2, 10, 255, cv2.THRESH_BINARY_INV)[1]
# 找轮廓                               二值图    只检测外轮廓    只保留终点坐标
contours, hiearchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 必须画在原图上,不能是灰度图
cv2.drawContours(img, contours, -1, (0, 0, 255), 3)
#  以左上角坐标为标准,升序排列
cnts, boundingBoxs = myutils.sort(contours)

digits = {
    
    }

# 制作模板
for (i, c) in enumerate(cnts):
    (x, y, w, h) = cv2.boundingRect(c)
    roi = ref[y:y + h, x:x + w]
    roi = cv2.resize(roi, (57, 88))
    digits[i] = roi

提取银行卡号

# 设置两个核 为了礼帽运算和闭运算时使用
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqlKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))


# 使得图片等比例放大    即长为100
card = myutils.changeImg('card.jpg', 300)

gray_card = cv2.cvtColor(card, cv2.COLOR_BGR2GRAY)

#  礼帽操作
tophat = cv2.morphologyEx(gray_card, cv2.MORPH_TOPHAT, rectKernel)

# -1代表使用的是3*3的算子
gradx = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)
picutils.show(gradx)
gradx = np.absolute(gradx)
picutils.show(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)

# 二值处理    自适应阈值
thresh = cv2.threshold(gradx, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

picutils.show(thresh)
sqlKernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 8))
# 这里进行的闭运算         先膨胀在腐蚀
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqlKernel2)

# 取轮廓
cnts, hiearchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

cur_img = card.copy()

cv2.drawContours(cur_img, cnts, -1, (0, 0, 255), 3)

locs = []

for (i, v) in enumerate(cnts):
    (x, y, w, h) = cv2.boundingRect(v)
    ar = w / float(h)
    cur_img = card.copy()
    if ar >= 3 and ar < 4 and w > 50 and w < 60:
        locs.append((x, y, w, h))
        # cv2.drawContours(cur_img, [v], -1, (0, 0, 255), 3)
        # picutils.show(cur_img)
        # print(ar)
        # print(w)

# 依次排序
locs = myutils.sort2(locs)
# 获取四个数字串

pics = []
for i, v in enumerate(locs):
    grayone = gray_card[v[1] - 5:v[1] + v[3] + 5, v[0] - 5:v[0] + v[2] + 5]
    # picutils.show(grayone)
    # 二值处理
    pics.append(grayone)

数字比对

nums = []
for i in pics:
    tempnums = []
    # picutils.show(i)
    t = cv2.threshold(i, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
    cnts, hiearchy = cv2.findContours(t.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    for c in cnts:
        (x, y, w, h) = cv2.boundingRect(c)
        # 取出所有的数字图片   每个数字单独一张
        tempnums.append((x, y, w, h))

    tempnums = myutils.sort2(tempnums)
    for tn in tempnums:
        nums.append(t[tn[1]:tn[1] + tn[3], tn[0]:tn[0] + tn[2]])

fresult=[]
# 进行数字比对
for num in nums:
    scores = []
    num = cv2.resize(num, (57, 88))
    # picutils.show(num)
    for (digit, digitRoi) in digits.items():
        # 与模板进行比对,选出最合适的
        result = cv2.matchTemplate(num, digitRoi, cv2.TM_CCOEFF)
        _, score, _, _ = cv2.minMaxLoc(result)
        scores.append(score)
    # print(np.argmax(scores))
    fresult.append(str(np.argmax(scores)))

# 得出结果
print(fresult)

结果

在这里插入图片描述
刚开始学,作为笔记记录一下

猜你喜欢

转载自blog.csdn.net/qq_43627076/article/details/125872729