多模板匹配是单模板匹配的扩展,可在图像中定位多个相同模板目标,核心解决 "单目标匹配→多目标筛选→重复框过滤" 问题。本文修复原代码核心问题,演示完整的多模板匹配实现,新手可直接复用。
核心代码实现
python
import cv2 as cv
import numpy as np
# 1. 读取模板和待匹配图像(增加空值校验)
tpl = cv.imread('.\image\19.bmp') # 模板图像
target = cv.imread('.\image\20.bmp') # 待匹配图像
if tpl is None or target is None:
print('图像读取失败,请检查路径!')
exit()
# 显示模板和待匹配图像
cv.namedWindow('tpl', cv.WINDOW_NORMAL)
cv.resizeWindow('tpl', 600, 600)
cv.imshow('tpl', tpl)
cv.imshow('target', target)
# 2. 定义匹配算法(优先归一化相关系数,鲁棒性最强)
TM_SQDIFF = cv.TM_SQDIFF # 平方差:0最好
TM_SQDIFF_NORMED = cv.TM_SQDIFF_NORMED
TM_CCORR = cv.TM_CCORR # 相关性:1最好
TM_CCORR_NORMED = cv.TM_CCORR_NORMED
TM_CCOEFF = cv.TM_CCOEFF # 相关系数:1最好
TM_CCOEFF_NORMED = cv.TM_CCOEFF_NORMED # 推荐使用
# 3. 执行多模板匹配(关键:待匹配图在前,模板在后!)
result = cv.matchTemplate(target, tpl, TM_CCOEFF_NORMED)
# 归一化匹配结果,统一数值范围[0,1]
cv.normalize(result, result, 0, 1, cv.NORM_MINMAX, -1)
# 4. 筛选高匹配度位置(阈值0.8,可根据实际场景调整)
threshold = 0.8
# 获取所有匹配度≥阈值的坐标(np.where返回(行,列),需反转为(列,行))
locs = np.where(result >= threshold)
# 转换坐标格式:(x,y)
match_locs = list(zip(*locs[::-1]))
# 5. 重复框过滤(避免相邻重复标注)
filtered_locs = []
temp_loc = None
tpl_h, tpl_w = tpl.shape[:2] # 模板的宽高(匹配框尺寸)
gap_threshold = 110 # 重复框判定阈值(像素)
for loc in match_locs:
# 首次匹配或当前框与上一个框间距≥阈值,保留
if temp_loc is None or (abs(loc[0] - temp_loc[0]) > gap_threshold) or (abs(loc[1] - temp_loc[1]) > gap_threshold):
filtered_locs.append(loc)
temp_loc = loc
# 6. 绘制所有匹配框(在待匹配图像上标注)
draw_img = target.copy()
for (x, y) in filtered_locs:
# 匹配框右下角坐标:x+tpl_w, y+tpl_h
cv.rectangle(draw_img, (x, y), (x + tpl_w, y + tpl_h), (0, 0, 255), 3)
# 7. 显示匹配结果
cv.namedWindow('img', cv.WINDOW_NORMAL)
cv.resizeWindow('img', 600, 600)
cv.imshow('img', draw_img)
cv.waitKeyEx(0)
cv.destroyAllWindows()
关键知识点解析
1. 多模板匹配核心流程
| 步骤 | 核心操作 | 作用说明 |
|---|---|---|
| 匹配计算 | cv.matchTemplate(target, tpl, 算法) |
生成全图匹配度矩阵 |
| 阈值筛选 | np.where(result >= 0.8) |
过滤低匹配度位置,只保留高置信度目标 |
| 坐标转换 | zip(*locs[::-1]) |
将 np.where 返回的 (行,列) 转为图像 (列,行) 坐标 |
| 重复过滤 | 间距判断abs(x1-x2) > 110 |
避免同一目标被多次标注 |
| 绘制标注 | cv.rectangle() |
可视化所有匹配目标 |
2. 核心避坑点
- 参数顺序 :原代码
matchTemplate(tpl, target)顺序颠倒,正确应为(待匹配图, 模板),否则匹配结果完全错误; - 匹配框尺寸 :框的宽高需取模板图像 的
tpl_w/tpl_h,而非 target,原代码用 target 尺寸会导致框大小错误; - 重复过滤逻辑 :原代码
temp_loc[0]+110 < loc[0]仅判断单方向,改为abs()绝对值判断,避免漏过滤; - 绘图载体 :在
target.copy()上绘图,避免修改原始图像。
3. 优化技巧
- 阈值调优:阈值 0.8 是经验值,目标清晰时可提高(如 0.9),目标模糊时可降低(如 0.7);
- 批量模板:若需匹配多个不同模板,可循环加载模板并执行匹配,最后合并结果;
- 效率提升:对大图像,可先缩放后匹配,再还原坐标,减少计算量;
- 更优去重:复杂场景可改用非极大值抑制(NMS)算法,替代简单间距判断。
总结
- 多模板匹配核心是阈值筛选 + 重复框过滤,区别于单模板匹配的 "找极值";
matchTemplate参数顺序必须是(待匹配图, 模板),匹配框尺寸取模板宽高;- 重复框过滤需用绝对值间距判断,避免单方向判断导致漏过滤。