模板匹配是指在当前图像A内寻找与图像B最相似的部分,一般将图像A称为输入图像,将图像B称为模板图像。模板匹配的操作方法是将模板图像B在图像A上滑动,遍历所有像素以完成匹配。
**
**
cv2.matchTemplate(image,templ,method,[mask])
● image为原始图像,必须是8位或者32位的浮点型图像。
● templ为模板图像。它的尺寸必须小于或等于原始图像,并且与原始图像具有同样的类型。
● method为匹配方法。该参数通过TemplateMatchModes实现,有6种可能的值
对应公式:
● mask 为模板图像掩模。它必须和模板图像 templ 具有相同的类型和大小。通常情况下该值使用默认值即可。
函数 cv2.matchTemplate()的返回值 result 是由每个位置的比较结果组合所构成的一个结果集,类型是单通道32位浮点型。如果输入图像(原始图像)尺寸是WH,模板的尺寸是wh,则返回值的大小为(W-w+1)*(H-h+1)。
在进行模板匹配时,模板在原始图像内遍历。在水平方向上:
● 遍历的起始坐标是原始图像左数第1个像素值(序号从1开始)。
● 最后一次比较是当模板图像位于原始图像的最右侧时,此时其左上角像素点所在的位置是W-w+1。
因此,返回值result在水平方向上的大小是W-w+1(水平方向上的比较次数)。
在垂直方向上:
● 遍历的起始坐标从原始图像顶端的第1个像素开始。
● 最后一次比较是当模板图像位于原始图像的最下端时,此时其左上角像素点所在位置是H-h+1。
所以,返回值result在垂直方向上的大小是H-h+1(垂直方向上的比较次数)。
如果原始图像尺寸是WH,模板的尺寸是wh,则返回值的大小为(W-w+1)(H-h+1)。也就是说,模板图像要在输入图像内比较(W-w+1)(H-h+1)次。
返回值的组成是横向比较次数和纵向比较次数的数组。
注意,函数cv2.matchTemplate()通过参数method来决定使用不同的查找方法。对于不同的查找方法,返回值result具有不同的含义。
上述分析可以看出,查找方法不同,结果的判定方式也不同。在查找最佳匹配时,首先要确定使用的是何种method,然后再确定到底是查找最大值,还是查找最小值。
查找最值(极值)与最值所在的位置,可以使用 cv2.minMaxLoc()函数实现。该函数语法格式如下:
minVal,maxVal,minLoc,maxLoc=cv2.minMaxLoc(src[,mask])
其中:
● src为单通道数组。
● minVal为返回的最小值,如果没有最小值,则可以是NULL(空值)。
● maxVal为返回的最大值,如果没有最小值,则可以是NULL。
● minLoc为最大值的位置,如果没有最大值,则可以是NULL。
● maxLoc为最大值的位置,如果没有最大值,则可以是NULL。
● mask为用来选取掩模的子集,可选项。
函数cv2.matchTemplate()返回值中的最值位置就是模板匹配的位置
当然,选用不同参数值,匹配位置可能位于最大值所在的位置也可能位于最小值所在的位置。通过函数cv2.minMaxLoc()来查找函数cv2.matchTemplate()返回值中的最值位置,就可以找到最佳模板匹配的位置
借助函数cv2.rectangle()将该位置用白色标记出来。函数cv2.rectangle的语法格式为:
Img=cv.rectangle(img,pt1,pt2,color[,thickness])
式中各个参数的含义为:
● img表示要标记的目标图像。
● pt1是矩形的顶点。
● pt2是pt1的对角顶点。
● color是要绘制矩形的颜色或灰度级(灰度图像)。
● thickness是矩形边线的宽度。
使用的标记语句为:
cv2.rectangle(img,topLeft,bottomRight,255,2)
该语句表示,在img内标记一个矩形,矩形的两个对角顶点为topLeft和bottomRight,矩形颜色为白色(255),宽度为2。
例子1:使用函数 cv2.matchTemplate()进行模板匹配。要求参数 method 的值设置为cv2.TM_SQDIFF,显示函数的返回结果及匹配结果。
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
src = cv.imread("img.png",0)
template = cv.imread("core.png",0)
th,tw = template.shape[:2]
print(th,tw)
rv = cv.matchTemplate(src, template, cv.TM_SQDIFF)
minVal,maxVal,minLoc,maxLoc = cv.minMaxLoc(rv)
topLeft = minLoc
bottomRight = (topLeft[0]+tw,topLeft[1]+th)
cv.rectangle(src, topLeft, bottomRight, 255,2)
plt.subplot(121),plt.imshow(rv,cmap='gray')
plt.title('Matching Result'),plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(src,cmap='gray')
plt.title('Detected Point'),plt.xticks([]),plt.yticks([])
plt.show()
例子2:使用 cv2.matchTemplate()函数进行模板匹配。要求参数 method 的值设置为cv2.TM_CCOEFF,显示函数的返回结果及匹配结果。
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
src = cv.imread("moliniao.jpg",0)
template = cv.imread("molishou.jpg",0)
th,tw = template.shape[:2]
print(th,tw)
rv = cv.matchTemplate(src, template, cv.TM_CCOEFF)
minVal,maxVal,minLoc,maxLoc = cv.minMaxLoc(rv)
topLeft = maxLoc
bottomRight = (topLeft[0]+tw,topLeft[1]+th)
cv.rectangle(src, topLeft, bottomRight, 255,2)
plt.subplot(121),plt.imshow(rv,cmap='gray')
plt.title('Matching Result'),plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(src,cmap='gray')
plt.title('Detected Point'),plt.xticks([]),plt.yticks([])
plt.show()
**
**
有些情况下,要搜索的模板图像很可能在输入图像内出现了多次,这时就需要找出多个匹配结果。而函数 cv2.minMaxLoc()仅仅能够找出最值,无法给出所有匹配区域的位置信息。所以,要想匹配多个结果,使用函数 cv2.minMaxLoc()是无法实现的,需要利用阈值进行处理。
分步骤介绍如何获取多模板匹配的结果:
1.获取匹配位置的集合
函数where()能够获取模板匹配位置的集合。对于不同的输入,其返回的值是不同的。
● 当输入(参数)是一维数组时,返回值是一维索引,只有一组索引数组。
● 当输入是二维数组时,返回的是匹配值的位置索引,因此会有两组索引数组表示返回值的位置。
例子:import numpy as np
array = np.array([3,9,6,1,2,15])
b = np.where(array > 5)
print(b)
结果:该段代码返回的结果为:
(array([1,2,5],dtype=int64),)
说明索引值为1、2、5的数组元素,它们的值是大于5的。
函数 np.where()可以找出在函数 cv2.matchTemplate()的返回值中,哪些位置上的值是大于阈值threshold的。在具体实现时,可以采用的语句为:
loc=np.where(res >=threshold)
式中:
● res是函数cv2.matchTemplate()进行模板匹配后的返回值。
● threshold是预设的阈值
● loc是满足“res >=threshold”的像素点的索引集合。
2.循环
因此,在获取匹配值的索引集合后,可以采用如下语句遍历所有匹配的位置,对这些位置做标记:
for i in 匹配位置集合:
标记匹配位置。
3.在循环中使用函数zip()
函数zip()可用迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
4.调整坐标
函数 numpy.where()可以获取满足条件的模板匹配位置集合,然后可以使用函数cv2.rectangle()在上述匹配位置绘制矩形来标注匹配位置。
使用函数numpy.where()在函数cv2.matchTemplate()的输出值中查找指定值,得到的形式为“(行号,列号)”的位置索引。但是,函数cv2.rectangle()中用于指定顶点的参数所使用的是形式为“(列号,行号)”的位置索引。所以,在使用函数cv2.rectangle()绘制矩形前,要先将函数numpy.where()得到的位置索引做“行列互换”。可以使用如下语句实现loc内行列位置的互换:
loc[::-1]
5.标记匹配图像的位置
函数cv2.rectangle()可以标记匹配图像的具体位置,分别指定要标记的原始图像、对角顶点、颜色、矩形边线宽度即可。
关于矩形的对角顶点:
● 其中的一个对角顶点A可以通过for循环语句从确定的满足条件的“匹配位置集合”内获取。
● 另外一个对角顶点,可以通过顶点A的位置与模板的宽(w)和高(h)进行运算得到。
例子:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread("1.jpg",0)
print(img.shape)
dst = cv.imread("duo_templ.jpg",-1)
template = cv.cvtColor(dst, cv.COLOR_BGR2GRAY)
print(template.shape)
#获得图像的w,h
h,w = template.shape
res =cv.matchTemplate(img, template, cv.TM_CCOEFF_NORMED)
#设置阈值
threshold = 0.9
loc = np.where(res >=threshold)
for i in zip(*loc[::-1]):
cv.rectangle(img, i, ((i[0] + w,i[1] + h)), 255,1)
plt.imshow(img,camp = "gray")
plt.xticks([]),plt.yticks([])