模板匹配顾名思义就是给定一幅影像(模板)然后在另一幅 图像中寻找这个模板的操作。它是一种用来在一幅大图中 寻找模板图像位置的方法。在OpenCV中有cv2.matchTemplate() 函数供我们方便调用。它的工作原理与2D卷积函数一样, 将模板图像在输入图像(大图)上滑动,并且在每一个位置对 模板图像和与其对应的输入图像的子区域进行比较。返回 的结果是一个灰度图像,每一个像素值表示了此区域与模板 的匹配程度。
1.输入原图像(I)和模板图像(T)。在原图像中我们希望找到一块和模板匹配的区域
2.通过将模板在原图像上滑动来寻找最匹配的区域。 这里所谓的滑动是指模板图像块一次移动一个像素(从左往右,从上往下)。 在每一个位置,都进行一次度量计算,来判断该像素对应的原图像的特定区域 与模板图像的相似度。
3.对于模板T覆盖在I上的每个位置,把上一步计算的度量值保存在结果图像矩阵R中。 在R中每个位置都包含对应的匹配度量值。
4.在结果图像矩阵中寻找最值(最大或最小,根据算法不同而不同)。最值所对应的像素的位置即认为是最高的匹配。 以该点为顶点,长宽和模板大小图像一样的矩阵认为是匹配区域。在OpenCV中可以用cv2.minMaxLoc()函数获得最值坐标。
上面说了模板匹配的核心步骤。可以发现在步骤中有个核心问题。 那就是如何度量匹配程度,如何进行度量计算。在OpenCV中提供了 6中不同方法。这里就以这6种算法为基础介绍匹配算法。在下列公式中, T(x,y)表示模板,I(x,y)表示原图像,R(x,y)表示模板与原图相似度函数。 x、y表示某个像素,x’、y’表示循环变量,在模板那么大的范围内进行循环。
下面给出两个算法公式:
如果输入图像大小是(W × H),模板大小是(w × h),输出结果大小就是(W-w+1, H-h+1)。 当得到这幅图之后,就可以使用函数cv2.minMaxLoc()来找到其中最小值和最大值位置了。 第一个值为矩形左上角的点(位置),(w,h)为模板矩形的宽和高。这个矩形就是找到的模板区域了。 下面是实例代码:
import numpy as np
import cv2
from matplotlib import pyplot as plt
# 依次读取原图与模板
plt.rcParams["font.family"] = "SimHei"#直接修改配置字典,设置默认字体
img = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/Opencv3/data/apple.png")
template = img [100:200,0:100,:]
plt.subplot(131),plt.imshow(template[:,:,::-1])
plt.axis("off"),plt.title("模板图像")
#依次获取模板的宽高,用于后续绘制矩形
h = template.shape[0]
w = template.shape[1]
"#调用匹配函数"
# 第一个参数是原图
# 第二个参数是模板
# 第三个参数是匹配算法
# 返回的结果是一个二维的float类型的数组,大小为W-w+1 * H-h+1
res = cv2.matchTemplate(img, template, cv2.TM_CCOEFF)
# 获取返回结果中最值及其在res中的位置
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# 构造矩形并在原图上绘制
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
cv2.rectangle(img, top_left, bottom_right, (255, 255, 0), 5)
# 在使用Matplotlib显示之前,需要调整BGR的顺序
b = img[:, :, 0]
g = img[:, :, 1]
r = img[:, :, 2]
img = cv2.merge((r, g, b))
# 打印相关信息
"可以观察到图像,模板,R矩阵的大小符合公式"
print (img.shape,template.shape,res.shape,res.dtype,cv2.minMaxLoc(res))
# 利用Matplotlib绘图对比
plt.subplot(132), plt.imshow(img)
plt.axis("off"),plt.title("原图")
plt.subplot(133), plt.imshow(res, cmap='gray')
#匹配矩阵R的信息,最亮的点认为最匹配的点
plt.axis("off"),plt.title("R图像")
plt.show()
在上面的例子中,原图中只出现了一次模板中的对象,但有时更有可能一幅图像中出现多个模板对象,这时就需要多对象匹配了。 在OpenCV中cv2.minMaxLoc()只会返回最值的位置。所以显然现在不能使用这个函数了。我们需要设置一个阈值,当R值大于这个 阈值时,认为是模板对象,否则不是。所以这里显然用标准化的方法更加合适,否则我们无法确定阈值。代码如下:
import cv2
from matplotlib import pyplot as plt
import numpy as np
# 依次读取原图与模板
img = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/Opencv3/data/house.jpg")
template = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/Opencv3/data/house1.jpg")
# 依次获取模板的宽高,用于后续绘制矩形
h = template.shape[0]
w = template.shape[1]
"调用匹配函数"
# 第一个参数是原图
# 第二个参数是模板
# 第三个参数是匹配算法
# 返回的结果是一个二维的float类型的数组,大小为W-w+1 * H-h+1
res = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED)
# 设定阈值
threshold = 0.8
# 从res中提取大于阈值的像素的位置
loc = np.where(res >= threshold)
# 遍历不同位置,绘制矩形
# 在函数调用中使用*list/tuple,表示将list/tuple分开,作为位置参数传递给对应函数(前提是对应函数支持不定个数的位置参数)
# 切片[::-1]是将列表或字符倒过来
for pt in zip(*loc[::-1]):
cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
# 在使用Matplotlib显示之前,需要调整BGR的顺序
b = img[:, :, 0]
g = img[:, :, 1]
r = img[:, :, 2]
img = cv2.merge((r, g, b))
# 利用Matplotlib绘图对比
plt.subplot(121), plt.imshow(res, cmap='gray')
plt.subplot(122), plt.imshow(img, cmap='gray')
plt.show()