OpenCV模板匹配提取tif图像地图时出现重复检测如何解决
模板匹配重复检测问题修复方案
核心问题原因
- 现有去重逻辑错误:仅对匹配点的x/y坐标做±9像素的过滤,远小于模板尺寸,且判断逻辑存在漏洞(把x、y坐标存入同一个列表,
lspoint2全程未使用,坐标判断条件完全不符合预期) - 阈值设置过低:
TM_CCOEFF_NORMED取值范围为[-1,1],越接近1匹配度越高,当前0.15的阈值会产生大量误检 - 无跨模板去重逻辑:多模板匹配时,同一个目标被多个模板命中会重复输出结果
- 无重叠框过滤逻辑:模板匹配在目标区域附近会产生连续的高响应值,相邻偏移的匹配点都会命中阈值,产生大量重复框
修复方案
采用**非极大值抑制(NMS)**作为标准去重方案,步骤如下:
- 收集所有高于阈值的匹配框、对应置信度
- 按置信度从高到低排序,删除和当前最高置信度框重叠度高于阈值的其他框
- 多模板场景下,统一收集所有模板的匹配结果后做全局NMS,避免跨模板重复
修复后代码
1. 新增NMS工具函数
def non_max_suppression(boxes, scores, iou_threshold=0.3): # boxes格式:[[x1,y1,x2,y2], ...] x1y1左上角,x2y2右下角 # scores格式:[score1, score2, ...] x1 = boxes[:, 0] y1 = boxes[:, 1] x2 = boxes[:, 2] y2 = boxes[:, 3] areas = (x2 - x1 + 1) * (y2 - y1 + 1) order = scores.argsort()[::-1] # 按置信度从高到低排序 keep = [] while order.size > 0: i = order[0] keep.append(i) # 计算重叠区域 xx1 = np.maximum(x1[i], x1[order[1:]]) yy1 = np.maximum(y1[i], y1[order[1:]]) xx2 = np.minimum(x2[i], x2[order[1:]]) yy2 = np.minimum(y2[i], y2[order[1:]]) w = np.maximum(0.0, xx2 - xx1 + 1) h = np.maximum(0.0, yy2 - yy1 + 1) inter = w * h iou = inter / (areas[i] + areas[order[1:]] - inter) # 保留重叠度低于阈值的框 inds = np.where(iou <= iou_threshold)[0] order = order[inds + 1] return keep
2. 改造模板匹配函数
import numpy as np import PIL import cv2 import csv import os import time import glob # 全局变量定义表头,避免原代码未定义报错 fields = ["文件路径", "模板宽", "模板高", "检测y2", "检测x2", "面积", "阈值", "耗时"] def matchtemplatetiff(img, tmp, threshold): h, w, c = tmp.shape # 注意numpy数组是高、宽、通道,原代码写反了w和h会导致框尺寸错误 res = cv2.matchTemplate(img, tmp, cv2.TM_CCOEFF_NORMED) loc = np.where(res >= threshold) boxes = [] scores = [] for pt in zip(*loc[::-1]): x1, y1 = pt x2 = x1 + w y2 = y1 + h boxes.append([x1, y1, x2, y2]) scores.append(res[y1, x1]) return np.array(boxes), np.array(scores)
3. 改造批量处理逻辑
if __name__ == "__main__": # 可调参数 threshold = 0.7 # 匹配度阈值,建议从0.6~0.8之间调整 nms_threshold = 0.3 # 重叠度阈值,超过这个值的重复框会被过滤 tiff_dir = "/content/drive/MyDrive/testpakistan/*.tif" template_dir = "/content/drive/MyDrive/templatepakistan/*.tif" out_dir = "/content/drive/MyDrive/testpakistan/" csv_path = "/content/drive/My Drive/Colab Notebooks/records_first.csv" # 预加载所有模板避免重复读取 templates = [] for tmp_path in glob.glob(template_dir): tmp = np.array(PIL.Image.open(tmp_path)) templates.append(tmp) # 逐图处理 for tifffile in glob.glob(tiff_dir): start_time = time.time() img = np.array(PIL.Image.open(tifffile)) imgc = img.copy() all_boxes = [] all_scores = [] # 多模板匹配,收集所有结果 for tmp in templates: boxes, scores = matchtemplatetiff(img, tmp, threshold) if len(boxes) > 0: all_boxes.extend(boxes) all_scores.extend(scores) if len(all_boxes) == 0: print(f"{tifffile} 未检测到目标") continue all_boxes = np.array(all_boxes) all_scores = np.array(all_scores) # 全局NMS去重 keep_idx = non_max_suppression(all_boxes, all_scores, nms_threshold) final_boxes = all_boxes[keep_idx] # 输出结果 n = 0 for box in final_boxes: x1, y1, x2, y2 = box w = x2 - x1 h = y2 - y1 size = w * h * (2.54/400) * (2.54/400) # 保存裁剪图 crop_img = imgc[y1:y2, x1:x2, :] cv2.imwrite(f"{tifffile}_map_{n}.tif", crop_img) # 写csv rows = [[tifffile, w, h, y2, x2, size, threshold, (time.time() - start_time)]] with open(csv_path, 'a', newline='') as csvfile: csvwriter = csv.writer(csvfile) if n == 0 and not os.path.exists(csv_path): # 仅首次写入表头 csvwriter.writerow(fields) csvwriter.writerows(rows) n += 1 # 保存标注后的原图 PIL.Image.fromarray(img, 'RGB').save(os.path.join(out_dir, os.path.basename(tifffile))) print(f"{tifffile} 处理完成,检测到{n}个目标,耗时{time.time()-start_time}秒")
额外调整说明
- 修正了原代码中模板宽高写反的问题(numpy数组读取顺序为
高、宽、通道,原代码w, h, c = tmp.shape会导致框尺寸错误) - 预加载所有模板避免重复读取文件,提升处理速度
- 新增csv表头判断,避免重复写入表头
- 调整匹配度阈值默认值为0.7,可根据实际匹配效果调整
内容的提问来源于stack exchange,提问作者Test Program
相关产品推荐
相关产品推荐

