核心函数
OpenCV 提供了 cv2.matchTemplate() 函数来实现模板匹配。

语法:
result = cv2.matchTemplate(image, templ, method, mask=None)
参数说明:
image: 源图像,通常是较大的图像。templ: 模板图像,要在源图像中查找的小图像。method: 匹配方法,这是最重要的参数,它决定了如何计算相似度,不同的方法适用于不同的场景。mask(可选): 模板的掩码,如果模板图像有透明背景或不规则形状,可以使用掩码来指定只匹配模板的特定部分,默认为None。
返回值:
result: 一个与源图像大小相同的图像,但类型为float32,这个图像的每个像素值表示模板在该位置匹配的“响应值”或“相似度分数”,响应值越高(或越低,取决于方法),表示匹配越好。
匹配方法
OpenCV 提供了多种匹配方法,它们的主要区别在于如何计算相似度,最常用的有 6 种:

| 方法 (Method) | 全称 | 描述 | 特点 |
|---|---|---|---|
TM_SQDIFF |
Square Difference | 平方差匹配 | 值越小,匹配越好,完全匹配时为 0。 |
TM_CCORR |
Cross Correlation | 相关性匹配 | 值越大,匹配越好,完全匹配时为最大值。 |
TM_CCOEFF |
Correlation Coefficient | 相关系数匹配 | 值越大,匹配越好,完全匹配时为 1。 |
TM_SQDIFF_NORMED |
Normalized Square Difference | 归一化平方差匹配 | 值越小,匹配越好,范围在 [0, 1],0 为完全匹配。 |
TM_CCORR_NORMED |
Normalized Cross Correlation | 归一化相关性匹配 | 值越大,匹配越好,范围在 [0, 1],1 为完全匹配。 |
TM_CCOEFF_NORMED |
Normalized Correlation Coefficient | 归一化相关系数匹配 | 值越大,匹配越好,范围在 [-1, 1],1 为完全匹配。 |
如何选择?
- 推荐使用归一化方法 (
_NORMED),因为它们对光照变化不敏感,结果更可靠。TM_SQDIFF_NORMED和TM_CCORR_NORMED是最常用的两个。
TM_SQDIFF和TM_CCORR是非归一化的,容易受到图像对比度的影响,一般不推荐。
如何定位匹配位置
cv2.matchTemplate() 返回的是一个结果矩阵,我们需要在这个矩阵中找到最大值或最小值的位置,这对应了源图像中模板的最佳匹配位置。
OpenCV 提供了 cv2.minMaxLoc() 函数来方便地找到矩阵中的最小值和最大值及其位置。
语法:

min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
返回值:
min_val: 结果矩阵中的最小值。max_val: 结果矩阵中的最大值。min_loc: 最小值的位置 (x, y)。max_loc: 最大值的位置 (x, y)。
如何使用 minMaxLoc?
- 如果使用
TM_SQDIFF或TM_SQDIFF_NORMED(值越小越好),min_loc就是最佳匹配位置。 - 如果使用其他所有方法(值越大越好),
max_loc就是最佳匹配位置。
完整代码示例
下面是一个完整的示例,演示如何使用模板匹配在一张大图中找到小图的位置。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 1. 读取源图像和模板图像
# 注意:OpenCV 默认以 BGR 格式读取图像
source_img = cv2.imread('source.jpg')
template_img = cv2.imread('template.jpg')
# 如果图像或模板未找到,则退出
if source_img is None or template_img is None:
print("Error: Could not read image(s). Check file paths.")
exit()
# 2. 获取模板图像的宽度和高度
# 这些值用于在源图像上绘制矩形框
h, w = template_img.shape[:2]
# 3. 执行模板匹配
# 使用归一化的平方差方法,值越小越好
method = cv2.TM_SQDIFF_NORMED
result = cv2.matchTemplate(source_img, template_img, method)
# 4. 在结果矩阵中找到最佳匹配位置
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
# 5. 根据匹配方法确定最佳匹配坐标
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
top_left = min_loc
else:
top_left = max_loc
# 计算矩形的右下角坐标
bottom_right = (top_left[0] + w, top_left[1] + h)
# 6. 在源图像上绘制矩形框
# 使用绿色 (0, 255, 0) 线条,线宽为 2
matched_img = source_img.copy()
cv2.rectangle(matched_img, top_left, bottom_right, (0, 255, 0), 2)
# 7. 显示结果
# 使用 matplotlib 显示图像,因为它能更好地处理 BGR 到 RGB 的转换
plt.figure(figsize=(15, 5))
# 显示源图像
plt.subplot(131), plt.imshow(cv2.cvtColor(source_img, cv2.COLOR_BGR2RGB))'Source Image'), plt.axis('off')
# 显示模板图像
plt.subplot(132), plt.imshow(cv2.cvtColor(template_img, cv2.COLOR_BGR2RGB))'Template Image'), plt.axis('off')
# 显示匹配结果
plt.subplot(133), plt.imshow(cv2.cvtColor(matched_img, cv2.COLOR_BGR2RGB))'Match Result'), plt.axis('off')
plt.show()
# 打印匹配结果信息
print(f"Best match top-left corner: {top_left}")
print(f"Match confidence (for TM_SQDIFF_NORMED, lower is better): {min_val:.4f}")
代码解释与进阶技巧
1 多个对象匹配
上面的代码只找到了一个最佳匹配,如果源图像中有多个与模板相似的物体怎么办?
我们可以设置一个阈值,然后找出所有响应值低于(或高于,取决于方法)该阈值的点。
# 假设我们已经执行了 matchTemplate 并得到了 result
# method = cv2.TM_CCOEFF_NORMED # 使用归一化相关系数,值越大越好
# 设定一个阈值
threshold = 0.8
# 找到所有大于阈值的匹配位置
loc = np.where(result >= threshold)
# 遍历所有找到的位置
for pt in zip(*loc[::-1]): # *loc[::-1] 是为了将坐标 (y,x) 转换为 (x,y)
# 绘制矩形框
cv2.rectangle(source_img, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
# 显示结果
plt.imshow(cv2.cvtColor(source_img, cv2.COLOR_BGR2RGB))'Multiple Objects Matched')
plt.axis('off')
plt.show()
2 模板匹配的局限性
模板匹配虽然简单,但也有明显的缺点:
- 旋转和缩放敏感:标准模板匹配无法处理模板在源图像中发生旋转或缩放的情况,如果物体旋转了,就匹配不到了。
- 光照敏感:虽然归一化方法有所改善,但剧烈的光照变化仍然会影响匹配效果。
- 背景干扰:如果模板的背景比较复杂,或者源图像中模板的背景发生了变化,匹配效果会很差。
3 替代方案
当模板匹配效果不佳时,可以考虑更强大的目标检测方法:
- 特征点检测 (如 SIFT, SURF, ORB):这些方法检测图像中的关键点和描述符,然后通过匹配描述符来找到物体,对旋转和缩放有较好的鲁棒性。
- 深度学习方法 (如 YOLO, SSD, Faster R-CNN):对于现代应用,尤其是复杂场景,基于深度学习的目标检测模型是更准确、更可靠的选择。
希望这个详细的解释能帮助你理解和使用 OpenCV 的模板匹配功能!
