PyCharm环境配置
添加将所需的两个图片的路径
PyCharm中添加OpenCV库
正式代码:
1.导入工具包
from imutils import contours
import numpy as np
import argparse
import cv2
import myutils
2.设置图片的路径
# 设置参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", default='./images/credit_card_01.png', help="path to input image")
ap.add_argument("-t", "--template", default='./ocr_a_reference.png', help="path to template OCR image")
args = vars(ap.parse_args())
# 指定信用卡类型
FIRST_NUMBER = {"3": "American Express", "4": "Visa", "5": "MasterCard", "6": "Discover Card"}
argparse.ArgumentParser()函数: argparse模块可以让人轻松编写用户友好的命令行接口
argparse.ArgumentParser()用法解析_quantLearner的博客-CSDN博客_argparse.argumentparser
3.设置一个显示函数,方便以后的调用
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(500)
cv2.destroyAllWindows()
4.将图像读进来,然后进行灰度处理和二值化处理
# 读取一个模板图像
img = cv2.imread(args["template"])
cv_show('template', img)
template = cv2.imread(args["image"])
cv_show('image', template)
# 灰度图
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv_show('template_gray', ref)
# 二值图像
ref = cv2.threshold(ref, 10, 255, cv2.THRESH_BINARY_INV)[1]
cv_show('template_bi', ref)
cv2.cvtColor()函数
cv2.cvtColor(p1,p2) 是颜色空间转换函数,p1是需要转换的图片,p2是转换成何种格式。
cv2.imread()和cv2.cvtColor() 的使用_静茹秋叶的博客-CSDN博客_cv2.color_bgr2gray
cv2.threshold()简单阀值函数
5.计算轮廓,然后遍历每一个轮廓,外接矩形,扣出模板,列出每一个数字对应的模板
# 计算轮廓
# cv2.findContours()函数接受的参数为二值图,即黑白的(不是灰度图),cv2.RETR_EXTERNAL只检测外轮廓,cv2.CHAIN_APPROX_SIMPLE只保留终点坐标
# 返回的list中每个元素都是图像中的一个轮廓
refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # cv版本大于3.8的,只有两个返回值
cv2.drawContours(img, refCnts, -1, (0, 0, 255), 3) # 轮廓在二值图上得到, 画要画在原图上
cv_show('template_Contours', img)
print(np.array(refCnts).shape)
refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0] # 排序,从左到右,从上到下
digits = {} # 存模板的单个数字
# 遍历每一个轮廓,外接矩形
for (i, c) in enumerate(refCnts): # c是每个轮廓的终点坐标
# 计算外接矩形并且resize成合适大小
(x, y, w, h) = cv2.boundingRect(c)
# 抠出模板
roi = ref[y:y + h, x:x + w] # 每个roi对应一个数字
roi = cv2.resize(roi, (57, 88)) # 太小,调大点
# 每一个数字对应每一个模板
digits[i] = roi
cv2.findContours()函数
cv2.drawContours()函数
opencv:图像轮廓检测 cv2.findContours() 与 cv2.drawContours()_宁静致远*的博客-CSDN博客_cv2.findcontours
cv2.boundingRect()函数:矩形边框
该段代码中还涉及一个排序函数(myutils),该函数创建在另一个PythonFile中
myutilsFile函数
# myutils.py
import cv2
def sort_contours(cnts, method="left-to-right"):
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 cnts] #用一个最小的矩形,把找到的形状包起来x,y,h,w
(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
key=lambda b: b[1][i], reverse=reverse))
return cnts, boundingBoxes
def resize(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
6.输入图像的处理
# 输入图像处理
# 1.初始化卷积核,根据实际任务指定大小,不一定非要3*3
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
# 2.读取输入图像,预处理
image = cv2.imread(args["image"])
cv_show("Input image", image)
image = myutils.resize(image, width=300)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv_show("Input_gray", gray)
# 3.礼帽:原始输入-开运算结果(只剩毛刺)
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
cv_show("Input tophat", tophat)
# 4.x方向上面的Sobel算子,实验表明只加x效果更好一些
gradx = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)
gradx = np.absolute(gradx)
(minVal, maxVal) = (np.min(gradx), np.max(gradx))
gradx = (255 * ((gradx - minVal) / (maxVal - minVal)))
gradx = gradx.astype("uint8")
print(np.array(gradx).shape)
cv_show("Input_Sobel_gradx", gradx)
# 5.通过闭操作将数字连在一起,将本是4个数字的4个框膨胀成1个框,就腐蚀不掉了
gradx = cv2.morphologyEx(gradx, cv2.MORPH_CLOSE, rectKernel)
cv_show("Input_close_gradx", gradx)
# 6.THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
thresh = cv2.threshold(gradx, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv_show('Input_thresh', thresh)
# 7.再来一个闭操作
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)
cv_show('Input_thresh_CLOSE', thresh)
# 8.计算轮廓
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, (200, 150, 190), 3)
cv_show('Input_Contours', cur_img)
cv2.morphologyEx()函数
opencv-python 之 cv2.morphologyEx()_一芷泡泡的博客-CSDN博客_morphologyex python
cv2.Sobel()函数
7.遍历轮廓
# 遍历轮廓
# 1.遍历轮廓
locs = [] # 存符合条件的轮廓
for i, c in enumerate(threshCnts):
# 计算矩形
x, y, w, h = cv2.boundingRect(c)
ar = w / float(h)
# 选择合适的区域,根据实际任务来,这里的基本都是四个数字一组
if 2.5 < ar < 4.0:
if (40 < w < 55) and (10 < h < 20):
# 符合的留下来
locs.append((x, y, w, h))
# append函数,给列表里面添加元素
# 将符合的轮廓从左到右排序
locs = sorted(locs, key=lambda x: x[0])
# 2.遍历每一个轮廓中的数字
output = [] # 存正确的数字
for (i, (gx, gy, gw, gh)) in enumerate(locs): # 遍历每一组大轮廓(包含4个数字)
# initialize the list of group digits
groupOutput = []
# 根据坐标提取每一个组(4个值)
group = gray[gy - 5:gy + gh + 5, gx - 5:gx + gw + 5] # 往外扩一点
cv_show('group_' + str(i), group)
# 2.1 预处理
group = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] # 二值化的group
# cv_show('group_'+str(i),group)
# 计算每一组的轮廓 这样就分成4个小轮廓了
digitCnts = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
# 排序
digitCnts = myutils.sort_contours(digitCnts, method="left-to-right")[0]
# 2.2 计算并匹配每一组中的每一个数值
for c in digitCnts: # c表示每个小轮廓的终点坐标
z = 0
# 找到当前数值的轮廓,resize成合适的的大小
(x, y, w, h) = cv2.boundingRect(c) # 外接矩形
roi = group[y:y + h, x:x + w] # 在原图中取出小轮廓覆盖区域,即数字
roi = cv2.resize(roi, (57, 88))
# cv_show("roi_"+str(z),roi)
# 计算匹配得分: 0得分多少,1得分多少...
scores = [] # 单次循环中,scores存的是一个数值 匹配 10个模板数值的最大得分
# 在模板中计算每一个得分
# digits的digit正好是数值0,1,...,9;digitROI是每个数值的特征表示
for (digit, digitROI) in digits.items():
# 进行模板匹配, res是结果矩阵
res = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF) # 此时roi是X digitROI是0 依次是1,2.. 匹配10次,看模板最高得分多少
Max_score = cv2.minMaxLoc(res)[1] # 返回4个,取第二个最大值Maxscore
scores.append(Max_score) # 10个最大值
print("scores:", scores)
# 得到最合适的数字
groupOutput.append(str(np.argmax(scores))) # 返回的是输入列表中最大值的位置
z = z + 1
# 2.3 画出来
cv2.rectangle(image, (gx - 5, gy - 5), (gx + gw + 5, gy + gh + 5), (0, 0, 255), 1) # 左上角,右下角
# 2.4 putText参数:图片,添加的文字,左上角坐标,字体,字体大小,颜色,字体粗细
cv2.putText(image, "".join(groupOutput), (gx, gy - 15),
cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
# 2.5 得到结果
output.extend(groupOutput)
print("groupOutput:", groupOutput)
cv2.matchTemplate()模板匹配函数
cv2.minMaxLoc():函数功能:假设有一个矩阵a,现在需要求这个矩阵的最小值,最大值,并得到最大值,最小值的索引。咋一看感觉很复杂,但使用这个cv2.minMaxLoc()函数就可全部解决。函数返回的四个值就是上述所要得到的。
cv2.matchTemplate模板匹配和cv2.minMaxLoc()函数_zhaojiafu666的博客-CSDN博客_cv2.matchtemplate函数
8.打印结果
# 打印结果
print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Output_image", image)
cv2.waitKey(0)