银行卡号识别

1 图像基本操作

在我的仓库中,有相关的代码操作。仓库:https://github.com/Guoxn1/ai。

我也是从bilibili学了一点,关于cv2的操作。链接:https://www.bilibili.com/video/BV1PV411774y/。

2 银行卡号识别简介

image-20231016161723638

数据如上,其中有5个银行卡照片,和一个标准数字集图片。

最后做到的效果如下:

image-20231016161917248

可运行的代码和数据集存在我的仓库中。

基本的实现思路是模板匹配,应当分以下几步进行实施:

1.读取模板图像,提取每个数字的轮廓,作为轮廓要resize大小,然后和每个数字进行对应。

2.读取银行卡图像,先找到大的轮廓,定位到卡号的位置,再进行轮廓检测得到每个数字,和模板数字特征匹配,得到最大的那个。

使用到的技术:

图像处理需要转换为灰度图像,并且需要开闭运算得到数字区域、sobel找轮廓,模板匹配等。

3 预定义数据和函数

全局变量,展示图片的函数等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np
# 可以用命令行来运行
import argparse
import cv2

# 也可以顺带识别信用卡类型,根据第一个银行卡号第一个数字识别
FIRST_NUM = {
"3":"American Express",
"4":"Visa",
"5":"Mastercard",
"6":"Discover Card"
}

# 定义一个画图的函数
def cv_show(img,name="img"):
cv2.imshow(name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()


4 处理模板图像

4.1 处理成二值图像

二值图像具有更好的边界识别。

1
2
3
4
5
6
7
8
9
img = cv2.imread("img/ocr_a_reference.png")
cv_show(img)
# 灰度图
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv_show(ref)
# 二值图像
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]
cv_show(ref)

image-20231016163315548

4.2 识别边界,建立图像和数字的对应关系

先定义两个函数,sort_contours和myresize,分别对应区域排序画框和调整图片大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 找到10个数字 从左到右找
def sort_contours(refCnts, method):
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 = [cv2.boundingRect(c) for c in refCnts]
#key参数指定了排序的关键字,即根据元组中的第二个元素 (b[1]) 的第i个索引位置的值进行排序。
# 此处按照boundingBoxes的最小locx值进行排序
(refCnts,boundingBoxes) = zip(*sorted(zip(refCnts,boundingBoxes),
key=lambda b: b[1][i],reverse=reverse
))
return refCnts, boundingBoxes
# 需要调整图像大小
def myresize(image, width=None, height=None, inter=cv2.INTER_AREA):
dim = None
(h, w) = image.shape[:2]
if width is None and height is None:
return image
if width is None:
r = height / float(h)
dim = (int(w * r), height)
else:
r = width / float(w)
dim = (width, int(h * r))
resized = cv2.resize(image, dim, interpolation=inter)
return resized
image-20231016163350898

提取每一个模板数字,建立对应关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

cv2.drawContours(img,refCnts,-1,(0,0,255),3)
cv_show(img)

refCnts = sort_contours(refCnts,method="left-to-right")[0]
digits = {}
# 建立对应关系
for (i,c) in enumerate(refCnts):
(x,y,w,h) = cv2.boundingRect(c)
roi = ref[y:y + h, x:x + w]
roi = cv2.resize(roi, (57, 88))
# 模板和数字映射
digits[i] = roi

5 处理银行卡图像

5.1 图像处理和图像增强

转换为二值图像是必要的,可以再考虑图像增强,比如顶帽操作,均衡操作。

1
2
3
4
5
6
7
8
9
10
11
# 对输入图像进行处理
# 初始化卷积核
# 九列三行
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT,(6,6))
image = cv2.imread("img/credit_card_01.png")
cv_show(image)
image = myresize(image,width=300)
gray = cv2.cvtColor(image,cv2.COLOR_RGB2GRAY)
cv_show(gray)

1
2
3
4
5
# 数字区域位于明亮区,是否还记得顶帽操作
# 顶帽操作可以放大细节,可以用于图像增强,凸显更明亮的区域
tophat = cv2.morphologyEx(gray,cv2.MORPH_TOPHAT,rectKernel)
cv_show(tophat)

1
2
3
4
5
# 采用均衡操作。
clahe = cv2.createCLAHE(clipLimit=2.0,tileGridSize=(8,8))
res = clahe.apply(tophat)

cv_show(res)
image-20231016163427296

5.2 确定“四数字”轮廓

识别到银行卡号数字,由于数字间比较紧凑,所以尽量识别出整串数字或者按照四个数字为一小块,识别出来,然后再在这些小块中识别出每一个数字。要识别出银行卡“四数字”所在的位置,需要对其限制。

image-20231016163057905
1
2
3
4
5
6
# 图像预处理完后进行确定轮廓
# 常见的 scharr sobel lapupasi
# 且细腻度逐渐降低
# 也可以采用canny 是拉普拉斯的改良版
canny = cv2.Canny(res,150,250)
cv_show(canny)
image-20231016163447669

对内部进行填充,提高识别率。

image-20231016163531233

image-20231016163552866

1
2
3
4
5
6
7
8
9
10
11
12
# 先识别出四个数字块
# 通过闭操作(先膨胀,再腐蚀)将数字连在一起
canny = cv2.morphologyEx(canny,cv2.MORPH_CLOSE, rectKernel)
cv_show(canny)
# 进行过闭操作后,二值化处理, 可能存在不是0或255的值
thresh = cv2.threshold(canny,0,255,cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]
# 有很多空白,想办法填充,填充就用扩张操作

# 再来个闭操作扩充白色区域
thresh = cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,sqKernel)
cv_show(thresh)

1
2
3
4
5
6
7
# 计算新图像的轮廓 近似成长方形
threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = threshCnts
cur_img = image.copy()
cv2.drawContours(cur_img,cnts,-1,(0,0,255),2)
cv_show(cur_img)
image-20231016163614378

确定出“四数字”框:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 计算轮廓,寻找出我们希望找出的内容框
locs = []
for (i,c) in enumerate(cnts):
(x,y,w,h) = cv2.boundingRect(c)
ar = w/(float(h))
#根据宽高比来确定
if ar > 2.5 and ar < 4.0:
if (w > 40 and w < 55) and (h > 10 and h < 20):
#符合的留下来
locs.append((x, y, w, h))
locs = sorted(locs, key=lambda x:x[0])


image-20231016163650015

5.3 确定每个数字,并进行模板匹配

image-20231016163659296
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
output = []
# 遍历轮廓中的每一个数字
for (i,(gx,gy,gw,gh)) in enumerate(locs):
groupOutput = []
group = gray[gy-5:gy+gh+5,gx-5:gx+gw+5]
cv_show(group)
# 二值化处理
group = cv2.threshold(group, 0, 255,
cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show(group)
# 对于每一个数字块,有四个数字
# 分别计算每个数字块的数字轮廓,得到的数组再进行比较
dicConts,hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
dicConts = sort_contours(dicConts,method="left-to-right")[0]
for j in dicConts:
# 凹凸不平 换成矩形
(x,y,w,h) = cv2.boundingRect(j)
# 计算矩形区域
roi = group[y:y+h,x:x+w]
roi = cv2.resize(roi,(57,88))
cv_show(roi)
scores = []
# 开始匹配,计算匹配得分,输出得分最高的
for (digit,digroi) in digits.items():
result = cv2.matchTemplate(roi,digroi,method=cv2.TM_CCOEFF_NORMED)
(_, score, _, _) = cv2.minMaxLoc(result)

scores.append(score)
groupOutput.append(str(np.argmax(scores)))

cv2.rectangle(image, (gx - 5, gy - 5),
(gx + gw + 5, gy + gh + 5), (0, 0, 255), 1)
cv2.putText(image, "".join(groupOutput), (gx, gy - 15),
cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
output.extend(groupOutput)
print("Credit Card Type: {}".format(FIRST_NUM[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv_show(image)
image-20231016163715302

如果这篇博客给到您帮助,我希望您能给我的仓库点一个star,这将是我继续创作下去的动力。

我的仓库地址,https://github.com/Guoxn1?tab=repositories。

like

银行卡号识别
http://example.com/2023/10/16/银行卡号识别/
作者
Guoxin
发布于
2023年10月16日
许可协议