【基础】模板匹配
1. 什么是模板匹配
模板匹配是指在图像A中寻找与图像B最相似的部分, 将图像A称为输入图像,将图像B称为模板图像。模板匹配的操作方法是将模板图像B在输入图像A上进行滑动,遍历所有像素以完成匹配。
比如在下图中,希望在图中的大图像lena.jpg内寻找左上角的"眼睛"图像。此时,大图像lena.jpg是输入图像,"眼睛"图像是模板图像。查找的方式是,将模板图像在输入图像内从左上角开始滑动,逐个像素遍历整幅输入图像,以查找与其最匹配的部分。

2. 模板匹配原理
openCV提供了函数cv2.matchTemplate(),用于模板匹配,该函数的语法格式如下:
result = matchTemplate(image, templ, method[, result[, mask]])
image:输入图像templ:模板图像,需要注意的是它的尺寸必须小于或等于输入图像,并且与输入图像具有相同的类型method:匹配方法,该参数通过TemplateMatchModes实现,有6种可能的值:
| 参数值 | 对应数值 | 说明 |
|---|---|---|
cv2.TM_SQDIFF |
0 | 以方差为依据进行匹配。若完全匹配,则结果为0;若不匹配,计算得到的方差会很大 |
cv2.TM_SQDIFF_NORMED |
1 | 标准平方差匹配 |
cv2.TM_CCORR |
2 | 相关匹配,将模板图像与输入图像相乘,乘积结果越大表示匹配程度较高, 0表示最坏的匹配效果 |
cv2.TM_CCORR_NORMED |
3 | 标准相关匹配 |
cv2.TM_CCOEFF |
4 | 相关系数匹配,该方法将模板图像与其均值的相对值和输入图像与其均值的相关值进行匹配。1表示完美匹配,-1表示差的匹配,0表示没有任何相关性 |
cv2.TM_CCOEFF_NORMED |
5 | 标准相关系数匹配 |
其分别对应的公式如下图所示,其中,I表示输入图像,T表示模板图像,R表示输出的结果图像,x和y表示位置信息:

函数cv2.matchTemplate()的返回值result是由每个位置的比较结果组合而成的一个结果集。如果输入图像尺寸是W*H,模板的尺寸是w*h,那么返回值的大小为(W-w+1)*(H-h+1),这是因为在进行模板匹配时,模板会在输入图像上进行遍历,
在水平方向上:
遍历的起始坐标为输入图像从左数第1个像素值
当模板图像遍历到最右边时,此时模板图像的左上角像素点所在的位置是
W-w+1
因此,返回值result在水平方向上的大小是W-w+1,也即是在水平方向上的比较次数。
在垂直方向上:
遍历的起始坐标为输入图像从顶端数第1个像素值
当模板图像遍历到最下端时,此时模板图像的左上角像素点所在的位置是
H-h+1
因此,返回值result在垂直方向上的大小是H-h+1,也即是在垂直方向上的比较次数。
综上所述,如果原始图像尺寸是W*H,模板的尺寸是w*h,则返回值的大小为(W-w+1)*(H-h+1)。也就是说,模板图像要在输入图像上共比较(W-w+1)*(H-h+1)次。
另外需要注意的是,函数cv2.matchTemplate()根据参数method来决定使用不同的查找方法。对于不同的查找方法,返回值result具有不同的含义。例如:
● method的值为cv2.TM_SQDIFF和cv2.TM_SQDIFF_NORMED时,result值为0表示匹配度最好,值越大,表示匹配度越差。
● method的值为cv2.TM_CCORR、cv2.TM_CCORR_NORMED、cv2.TM_CCOEFF和cv2.TM_CCOEFF_NORMED时,result的值越小表示匹配度越差,值越大表示匹配度越好。
由上述简单分析可知,查找方法不同,结果的判定方式也不同。在查找最佳匹配时,首先要确定使用的是何种method,然后再确定到底是查找最大值,还是查找最小值。
查找**最值(极值)与最值所在的位置,**可以使用cv2.minMaxLoc()函数实现。该函数语法格式如下:
minVal, maxVal, minLoc, maxLoc = minMaxLoc(src[, mask])
src:单通道数组minVal:从src找到的最小值maxVal:从src找到的最大值minLoc:从src找到的最小值的位置maxLoc:从src找到的最大值的位置
综上所述,函数cv2.matchTemplate()返回值中的最值位置就是模板匹配的位置。当然,选用上表中不同参数值,匹配位置可能位于最大值所在的位置也可能位于最小值所在的位置。通过函数cv2.minMaxLoc()来查找函数cv2.matchTemplate()返回值中的最值位置,就可以找到最佳模板匹配的位置。
例如,当method的值为cv2.TM_SQDIFF和cv2.TM_SQDIFF_NORMED时,0表示最佳匹配,值越大,则表示匹配效果越差。因此,在使用这两种方法时,要寻找最小值所在的位置作为最佳匹配。如下语句能够找到cv2.matchTemplate()函数返回值中最小值的位置:
minVal, maxVal, minLoc, maxLoc = minMaxLoc(result) # result为cv2.matchTemplate()返回值
topLeft = minLoc # 最小值所在位置
以topLeft点为模板匹配位置的左上角坐标,结合模板图像的宽度w和高度h可以确定匹配位置的右下角坐标,代码如下所示:
bottomRight = (topLeft[0] + w, topLeft[1] + h)
当method的值为cv2.TM_CCORR、cv2.TM_CCORR_NORMED、cv2.TM_CCOEFF和cv2.TM_CCOEFF_NORMED时,cv2.matchTemplate()函数的返回值越小,表示匹配度越差,而返回值越大则表示匹配度越好。此时,要寻找最大值所在的位置作为最佳匹配。如下语句能够找到模板匹配返回值中最大值的位置,并以该点为左上角,结合模板的宽度w和高度h确定匹配位置的右下角坐标。
minVal, maxVal, minLoc, maxLoc = minMaxLoc(result) # result为cv2.matchTemplate()返回值
topLeft = maxLoc # 最小值所在位置
bottomRight = (topLeft[0] + w, topLeft[1] + h)
确定了模板位置之后就可以利用cv2.rectangle()把矩形画出来,简单介绍cv2.rectangle():
rectangle(img, pt1, pt2, color[, thickness[, lineType[, shift]]])
img:要绘制的图像pt1:矩形顶点pt2:pt1的对角顶点color:画笔颜色thickness:画笔宽度
3. 程序演示
输入图像为下图,

模板图像为

"""
Author: Will Wang
Email: WillWang1998@163.com
"""
import cv2
color_yellow = (0, 255, 255) # B-G-R | G+R=YELLOW
image = cv2.imread('wallpaper.jpg', 1)
templ = cv2.imread('templ_80x80.jpg', 1)
h, w = templ.shape[0:2]
result = cv2.matchTemplate(image, templ, cv2.TM_SQDIFF) # 平方差匹配
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(result)
topLeft = minLoc # TM_SQDIFF
bottomRight = (topLeft[0] + w, topLeft[1] + h)
print(f'image.shape:{image.shape}, templ.shape:{templ.shape}, result.shape:{result.shape}')
print(f'模板匹配 左上角坐标:{topLeft} 右下角坐标:{bottomRight}')
cv2.rectangle(image, topLeft, bottomRight, color_yellow)
cv2.imshow('image', image)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行以上代码,结果如下图所示,

