今天尝试一下用python+OpenCV,使用模板匹配的方式做个简单地识别银行卡卡号(大部分参考网上的,自己改了一部分,代码写的有点不太好,但是思路很清晰,过段时间可能会写一个C++版本的,在顺便把代码优化一下)。有问题欢迎提问,尽量回复
完整的代码百度云自行下载,代码里面有好多我调试的代码,已经注释了,有需要的可以打开看看每一步发生了什么。
链接:https://pan.baidu.com/s/1ZmRUIuN7N8EFXwXf9LcYaQ
提取码:86c2
先把原图和处理后的结果放出看看效果
接下来看看处理思路:
原图灰度处理,二值化,寻找轮廓,画出有效轮廓。这里轮廓有个排序,想想问什么要排序,解释在第二步里面
idCard = cv.imread("image/credit_card_01.png")
showImage("Ori", idCard)
idCard = myutils.resize(idCard, width=300)
gray = cv.cvtColor(idCard, cv.COLOR_BGR2GRAY)
showImage("Gray", gray)
retm, Binary = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
showImage("Binary", Binary)
threshCnts_, hierarchy = cv.findContours(Binary.copy(), cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
cnts = threshCnts_
curimg = idCard.copy()
locs = []# 存储有效轮廓(数字区域)
# 存放识别的数字
output =[]
for cnt in cnts:
x, y, w, h = cv.boundingRect(cnt)
# print(cnt.shape())
ar = w/float(h)
print("h为"+str(h)+ "w为"+str(w)+"比例为"+str(ar))
# 由于所有轮廓并不全是数字区域的,所以这里使用长宽以及比例选出数字区域
# 判断数字区域
if ar>3.0 and ar<4.0:
if (w>40 and w<55)and (h>10 and h<20):
locs.append((x, y, w, h))
# 通过对每隔轮廓的X值,对数字区域的4个轮廓排序,从左到右
locs = sorted(locs, key=lambda x:x[0])
读取模板图片,灰度处理,二值化,寻找轮廓,画出最小外接矩形,再将轮廓从左到右排序,将对应的轮廓区域和数字存入字典中(注意,这里一定要将这里的轮廓排序,按照每一个轮廓左上角的X轴大小排序,不然每个分割的数字图像和真实数据无法对上,大家可以试试不排序直接存入字典后看看运行后的结果是什么)
def refer(image):
ref = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
showImage("ref", ref)
ref = cv.threshold(ref, 10, 255, cv.THRESH_BINARY_INV)[1]
showImage("ref", ref)
refCnts, hierarchy = cv.findContours(ref, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
cv.drawContours(image, refCnts, -1, (0, 0, 255), 3)
showImage("img", image)
refCnts = myutils.sort_contours(refCnts, method="leleft-to-right")[0]
digits = {}
for (i, c) in enumerate(refCnts):
(x, y, w, h) = cv.boundingRect(c)
roi = ref[y:y+h, x:x+w]
roi = cv.resize(roi, (57, 88))
showImage("rio", roi)
digits[i] = roi
return digits
步骤一中,已经将银行卡的数字部分提取出来了,分成了4个部分,循环处理这四个部分。
先获取第一个部分,和步骤二一样,分割出四个单独的图片,再和步骤二返回的字典中的图像进行模板匹配,计算出最合适的图片,并返回字典中图片对应的数字,再接下来就是套娃一样的操作了。最后再将得到的结果写在原图中即可。(注意,第三步和第二步都有一个resize操作,这是让模板匹配的时候两个图片有相同的大小,要不然模板匹配是出现问题)
for m in range(len(imgROI)):
fourMumROI = imgROI[m]
# showImage("demo", imgsplit)
fourMumROI = cv.cvtColor(fourMumROI, cv.COLOR_BGR2GRAY)
# showImage("demo", imgsplit)
fourMumROI = cv.threshold(fourMumROI, 127
, 255, cv.THRESH_BINARY
)[1]
# showImage("demo", imgsplit)
fourNumCnts, imghierarchy = cv.findContours(fourMumROI, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
fourNumImg = imgROI[m]
# cv.drawContours(imgROI[i], fourNumCnts, -1, (0, 0, 255), 1)
showImage("demo", imgROI[m])
fourNumCnts = myutils.sort_contours(fourNumCnts, method="leleft-to-right")[0]
for (i, c) in enumerate(fourNumCnts):
(x, y, w, h) = cv.boundingRect(c)
roi = fourNumImg[y:y + h, x:x + w]
roi = cv.resize(roi, (57, 88))
roi = cv.cvtColor(roi, cv.COLOR_BGR2GRAY)
roi = cv.threshold(roi, 127, 255, cv.THRESH_BINARY)[1]
showImage("roi", roi)
# 模板匹配得分情况
scores = []
for(digit, digROI) in dig.items():
result = cv.matchTemplate(roi, digROI, cv.TM_CCOEFF)
(_, score, _,_) = cv.minMaxLoc(result)
scores.append(score)
groupOutput.append(str(np.argmax(scores)))
# cv.rectangle(idCard, (gX -5, gY -5), (gX + gW + 5, gY + gH + 5), (0, 0, 255), 1)
printX, printY, printW, printH = locs[0]
cv.putText(idCard, "".join(groupOutput), (printX, printY-15), cv.FONT_HERSHEY_SIMPLEX,
0.65, (0, 0, 255), 2)
print("".join(groupOutput))
output.extend(groupOutput)
#
cv.imshow("Image", idCard)
cv.waitKey(0)