1.首先下载opencv-python库,这个库的版本一定要固定
pip install opencv-python==4.1.2.30
2.准备下载模板图片,如下,在代码中命名为1.jpg
3.准备一个银行卡图片,在代码中命名为3.jpg
4.处理模板代码如下,该代码是为了匹配银行卡数字模板
import cv2
import numpy as np
import imutils
from imutils import contours
# filepath = 'D:\\openCVImage\\bankCard'
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 处理模板
def temp_deal():
# 读取模板图像
tempImage = cv2.imread("1.jpg")
# cv_show("temp", tempImage)
# 转换灰度图,颜色改变函数
tempGray = cv2.cvtColor(tempImage, cv2.COLOR_BGR2GRAY)
# 二值化处理,图像阈值函数,像素值超过127变成0,否则变成255 反二值化,高亮突出数字
ret, tempBinary = cv2.threshold(tempGray, 127, 255, cv2.THRESH_BINARY_INV)
# image_Binary = cv2.threshold(image_Gray, 177, 255, cv2.THRESH_BINARY_INV)[1] # 转换为二值化图像,[1]表示返回二值化图像,[0]表示返回阈值177
# cv_show("tempBinary", tempBinary)
# 返回轮廓信息和轮廓层数
contours, hierarchy = cv2.findContours(tempBinary.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE) # 轮廓检测。第1个参数是二值图。第2个参数检测最外层轮廓,第3个参数保留轮廓终点坐标
# 绘制轮廓
drawTemp = tempImage.copy() # 复制一份原图像作为画板,不能在原图上画,不然原图会改变
res = cv2.drawContours(drawTemp, contours, -1, (0, 0, 255), 2) # "-1"是指在画板上画出所有轮廓信息,红色,线宽为2
# cv_show("drawTempImage", res)
# 模板轮廓排序
# 详细解释参考:使用Python和OpenCV对轮廓进行排序(从左到右,自上而下)
# 原理求每一个轮廓的外接矩形也就是轮廓拟合,根据返回的左上坐标点,就能判断出轮廓的位置,再排序
# boxing中存放每次计算轮廓外接矩形得到的x、y、w、h,它的shape为(10,4)。cnt存放每一个轮廓
# 轮廓排序核心思想为先求出每个轮廓的外接矩形框,然后通过对外接框按照x或y坐标排序进⽽来实现对轮廓的排序.
refCnts = imutils.contours.sort_contours(contours, method="left-to-right")[0] # 排序,从左到右,从上到下
digits = {}
# 遍历每一个轮廓
for (i, c) in enumerate(refCnts): # 返回轮廓下标和对应的轮廓值
# 计算外接矩形并且resize成合适大小
(x, y, w, h) = cv2.boundingRect(c) # 轮廓拟合,中存放的是每个轮廓1 2 3 。。。。的最小矩形信息
roi = tempBinary[y:y + h, x:x + w] # tempBinary中依次保存的是高和宽,即(y,x)
# 将每个区域对应一个数字
roi = cv2.resize(roi, (55, 85)) # roi区域统一大小,根据自己需求来定
digits[i] = roi
# print(digits)
return digits
5.然后处理待识别银行卡图片的代码如下
def bankCard_deal(digits):
# 初始化卷积核
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
myKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
img = cv2.imread("3.jpg")
# cv_show("img", img)
img = imutils.resize(img, width=300)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 礼帽操作,突出更明亮的区域
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
# cv_show("tophat", tophat)
# 利用Sobel算子进行边缘检测
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)
gradY = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=0, dy=1, ksize=-1)
gradX = np.absolute(gradX)
minVal = np.min(gradX)
maxVal = np.max(gradX)
gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
gradX = gradX.astype("uint8")
print(np.array(gradX).shape)
# cv_show("gradX", gradX)
# 通过闭操作,先膨胀后腐蚀,将数字连接在一块
gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
# cv_show("gradXclose", gradX)
thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# cv_show('threshImg', thresh)
# 再来一个闭操作
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, myKernel)
# cv_show('gradXclose2', thresh)
# 计算轮廓
threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = threshCnts
cur_img = img.copy()
cv2.drawContours(cur_img, cnts, -1, (0, 0, 255), 2)
# cv_show('imgCopy', cur_img)
locs = []
# 遍历轮廓,提取轮廓,需要尝试
for (i, c) in enumerate(cnts):
# 计算矩形
(x, y, w, h) = cv2.boundingRect(c)
ar = w / float(h)
# 选择合适的区域,根据实际任务来,这里的基本都是四个数字一组
# [(454, 213, 110, 29), (308, 211, 109, 31), (171, 211, 98, 31), (18, 211, 107, 31)]
if 2.5 < ar < 5.0:
if (40 < w < 65) and (10 < h < 20):
# 符合的留下来
locs.append((x, y, w+2, h+2))
res = cv2.rectangle(img.copy(), (x, y), (x + w+2, y + h+2), (0, 0, 255), 2) # "-1"是指在画板上画出所有轮廓信息,红色,线宽为2
cv_show("imgRect", res)
# 将符合的轮廓从左到右排序
locs = sorted(locs, key=lambda o: o[0])
output = []
# 四组轮廓
for (i, (gx, gy, gw, gh)) in enumerate(locs):
groupOutput = []
group = gray[gy - 5:gy + gh + 5, gx - 5:gx + gw + 5]
group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show("groupThreshold", group) # 1234;5678;
digitCnts, his = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 提取轮廓内数字1,2,3,4
digitCnts = imutils.contours.sort_contours(digitCnts, method="left-to-right")[0] # 对找到的轮廓进行排序 按照X轴坐标
# 计算每一组中的每一个数值
for c in digitCnts: # 每组数字1,2,3,4
# 找到当前数值的轮廓,resize成合适的大小
(x, y, w, h) = cv2.boundingRect(c) # 具体数字
roi = group[y:y + h, x:x + w] # 数字在每组group中的坐标
# 将每个区域对应一个数字
roi = cv2.resize(roi, (65, 99)) # roi区域统一大小,根据自己需求来定
# cv_show('roi',roi)
scores = []
for (digitKey, digitROIValue) in digits.items(): # 识别图的roi数字与模板里每个数字matchTemplate
result = cv2.matchTemplate(roi, digitROIValue, cv2.TM_CCOEFF_NORMED)
# 返回最值及最值位置,在这里我们需要的是最小值的得分,不同的匹配度计算方法的选择不同
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
scores.append(max_val) # 最大值
score = np.abs(scores) # 有负数出现,统一成正数,相关系数都变成正数
flag = False
for val in score:
if val > 0.9:
flag = True
break
if not flag:
best_index = np.argmax(score) # score最大值的下标,匹配度最高
best_value = str(best_index) # 下标就是对应的数字,在字典中,key是0对应的是值为0的图片
groupOutput.append(best_value)
cv2.rectangle(img, (gx - 5, gy - 5), (gx + gw + 5, gy + gh + 5), (0, 0, 255), 1)
# 在矩形框上绘图
cv2.putText(img, ''.join(groupOutput), (gx, gy - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
output.append(groupOutput)
cv_show('img', img)
# print('数字为:', output)
for i in output:
for j in i:
print(j,end="")
5.如果我们更换图片时,我们需要修改的代码地方有两处,第一处是选择的银行卡的数字区域,自己换图片时需要修改如下参数
if 2.5 < ar < 5.0:
if (40 < w < 65) and (10 < h < 20):
# 符合的留下来
locs.append((x, y, w+2, h+2))
res = cv2.rectangle(img.copy(), (x, y), (x + w+2, y + h+2), (0, 0, 255), 2) # "-1"是指在画板上画出所有轮廓信息,红色,线宽为2
cv_show("imgRect", res)
6.第二个要修改的地方是匹配数字模板上的银行卡数字图片的宽度和高度
roi = cv2.resize(roi, (65, 99))
import cv2
import numpy as np
import imutils
from imutils import contours
# filepath = 'D:\\openCVImage\\bankCard'
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 处理模板
def temp_deal():
# 读取模板图像
tempImage = cv2.imread("1.jpg")
# cv_show("temp", tempImage)
# 转换灰度图,颜色改变函数
tempGray = cv2.cvtColor(tempImage, cv2.COLOR_BGR2GRAY)
# 二值化处理,图像阈值函数,像素值超过127变成0,否则变成255 反二值化,高亮突出数字
ret, tempBinary = cv2.threshold(tempGray, 127, 255, cv2.THRESH_BINARY_INV)
# image_Binary = cv2.threshold(image_Gray, 177, 255, cv2.THRESH_BINARY_INV)[1] # 转换为二值化图像,[1]表示返回二值化图像,[0]表示返回阈值177
# cv_show("tempBinary", tempBinary)
# 返回轮廓信息和轮廓层数
contours, hierarchy = cv2.findContours(tempBinary.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE) # 轮廓检测。第1个参数是二值图。第2个参数检测最外层轮廓,第3个参数保留轮廓终点坐标
# 绘制轮廓
drawTemp = tempImage.copy() # 复制一份原图像作为画板,不能在原图上画,不然原图会改变
res = cv2.drawContours(drawTemp, contours, -1, (0, 0, 255), 2) # "-1"是指在画板上画出所有轮廓信息,红色,线宽为2
# cv_show("drawTempImage", res)
# 模板轮廓排序
# 详细解释参考:使用Python和OpenCV对轮廓进行排序(从左到右,自上而下)
# 原理求每一个轮廓的外接矩形也就是轮廓拟合,根据返回的左上坐标点,就能判断出轮廓的位置,再排序
# boxing中存放每次计算轮廓外接矩形得到的x、y、w、h,它的shape为(10,4)。cnt存放每一个轮廓
# 轮廓排序核心思想为先求出每个轮廓的外接矩形框,然后通过对外接框按照x或y坐标排序进⽽来实现对轮廓的排序.
refCnts = imutils.contours.sort_contours(contours, method="left-to-right")[0] # 排序,从左到右,从上到下
digits = {}
# 遍历每一个轮廓
for (i, c) in enumerate(refCnts): # 返回轮廓下标和对应的轮廓值
# 计算外接矩形并且resize成合适大小
(x, y, w, h) = cv2.boundingRect(c) # 轮廓拟合,中存放的是每个轮廓1 2 3 。。。。的最小矩形信息
roi = tempBinary[y:y + h, x:x + w] # tempBinary中依次保存的是高和宽,即(y,x)
# 将每个区域对应一个数字
roi = cv2.resize(roi, (55, 85)) # roi区域统一大小,根据自己需求来定
digits[i] = roi
# print(digits)
return digits
# 处理待识别银行卡图片
def bankCard_deal(digits):
# 初始化卷积核
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
myKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
img = cv2.imread("3.jpg")
# cv_show("img", img)
img = imutils.resize(img, width=300)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 礼帽操作,突出更明亮的区域
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
# cv_show("tophat", tophat)
# 利用Sobel算子进行边缘检测
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)
gradY = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=0, dy=1, ksize=-1)
gradX = np.absolute(gradX)
minVal = np.min(gradX)
maxVal = np.max(gradX)
gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
gradX = gradX.astype("uint8")
# print(np.array(gradX).shape)
# cv_show("gradX", gradX)
# 通过闭操作,先膨胀后腐蚀,将数字连接在一块
gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
# cv_show("gradXclose", gradX)
thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# cv_show('threshImg', thresh)
# 再来一个闭操作
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, myKernel)
# cv_show('gradXclose2', thresh)
# 计算轮廓
threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = threshCnts
cur_img = img.copy()
cv2.drawContours(cur_img, cnts, -1, (0, 0, 255), 2)
# cv_show('imgCopy', cur_img)
locs = []
# 遍历轮廓,提取轮廓,需要尝试
for (i, c) in enumerate(cnts):
# 计算矩形
(x, y, w, h) = cv2.boundingRect(c)
ar = w / float(h)
# 选择合适的区域,根据实际任务来,这里的基本都是四个数字一组
# [(454, 213, 110, 29), (308, 211, 109, 31), (171, 211, 98, 31), (18, 211, 107, 31)]
if 2.5 < ar < 5.0:
if (40 < w < 65) and (10 < h < 20):
# 符合的留下来
locs.append((x, y, w+2, h+2))
# res = cv2.rectangle(img.copy(), (x, y), (x + w+2, y + h+2), (0, 0, 255), 2) # "-1"是指在画板上画出所有轮廓信息,红色,线宽为2
# cv_show("imgRect", res)
# 将符合的轮廓从左到右排序
locs = sorted(locs, key=lambda o: o[0])
output = []
# 四组轮廓
for (i, (gx, gy, gw, gh)) in enumerate(locs):
groupOutput = []
group = gray[gy - 5:gy + gh + 5, gx - 5:gx + gw + 5]
group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# cv_show("groupThreshold", group) # 1234;5678;
digitCnts, his = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 提取轮廓内数字1,2,3,4
digitCnts = imutils.contours.sort_contours(digitCnts, method="left-to-right")[0] # 对找到的轮廓进行排序 按照X轴坐标
# 计算每一组中的每一个数值
for c in digitCnts: # 每组数字1,2,3,4
# 找到当前数值的轮廓,resize成合适的大小
(x, y, w, h) = cv2.boundingRect(c) # 具体数字
roi = group[y:y + h, x:x + w] # 数字在每组group中的坐标
# 将每个区域对应一个数字
roi = cv2.resize(roi, (65, 99)) # roi区域统一大小,根据自己需求来定
# cv_show('roi',roi)
scores = []
for (digitKey, digitROIValue) in digits.items(): # 识别图的roi数字与模板里每个数字matchTemplate
result = cv2.matchTemplate(roi, digitROIValue, cv2.TM_CCOEFF_NORMED)
# 返回最值及最值位置,在这里我们需要的是最小值的得分,不同的匹配度计算方法的选择不同
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
scores.append(max_val) # 最大值
score = np.abs(scores) # 有负数出现,统一成正数,相关系数都变成正数
flag = False
for val in score:
if val > 0.9:
flag = True
break
if not flag:
best_index = np.argmax(score) # score最大值的下标,匹配度最高
best_value = str(best_index) # 下标就是对应的数字,在字典中,key是0对应的是值为0的图片
groupOutput.append(best_value)
cv2.rectangle(img, (gx - 5, gy - 5), (gx + gw + 5, gy + gh + 5), (0, 0, 255), 1)
# 在矩形框上绘图
cv2.putText(img, ''.join(groupOutput), (gx, gy - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
output.append(groupOutput)
cv_show('img', img)
# print('数字为:', output)
print("银行卡号如下:")
for i in output:
for j in i:
print(j,end="")
digits=temp_deal()
bankCard_deal(digits)