基于模板匹配的目标检测代码无检测框输出问题求助
模板匹配目标检测无检测框问题求助
问题概述
从技术博客复制了基于Template Matching的目标检测代码,运行后仅显示参考图像,无任何目标检测框。仅能成功检测图像中的大型组件,小型目标无论是用原模板还是调整尺寸后的模板都无法检测到,预期输出应能标注出三个小型元件的位置。
相关图像说明
- 参考图像:包含多个电子元件的电路板
- 使用的模板图像:三个不同的小型电子元件
- 预期输出:带有三个彩色检测框及对应标签的电路板图像
- 当前仅能实现的结果:仅电路板上的大型组件被标注
- 尝试过的调整:使用缩放后的三个元件模板,仍无法检测目标
运行代码
import cv2 import numpy as np DEFAULT_TEMPLATE_MATCHING_THRESHOLD = 0.9 class Template: """ A class defining a template """ def __init__(self, image_path, label, color, matching_threshold=DEFAULT_TEMPLATE_MATCHING_THRESHOLD): """ Args: image_path (str): path of the template image path label (str): the label corresponding to the template color (List[int]): the color associated with the label (to plot detections) matching_threshold (float): the minimum similarity score to consider an object is detected by template matching """ self.image_path = image_path self.label = label self.color = color self.template = cv2.imread(image_path) self.template_height, self.template_width = self.template.shape[:2] self.matching_threshold = matching_threshold image = cv2.imread("reference.jpg") templates = [ Template(image_path="Component1.jpg", label="1", color=(0, 0, 255), matching_threshold=0.99), Template(image_path="Component2.jpg", label="2", color=(0, 255, 0,) , matching_threshold=0.91), Template(image_path="Component3.jpg", label="3", color=(0, 191, 255), matching_threshold=0.99), ] detections = [] for template in templates: template_matching = cv2.matchTemplate(template.template, image, cv2.TM_CCORR_NORMED) match_locations = np.where(template_matching >= template.matching_threshold) for (x, y) in zip(match_locations[1], match_locations[0]): match = { "TOP_LEFT_X": x, "TOP_LEFT_Y": y, "BOTTOM_RIGHT_X": x + template.template_width, "BOTTOM_RIGHT_Y": y + template.template_height, "MATCH_VALUE": template_matching[y, x], "LABEL": template.label, "COLOR": template.color } detections.append(match) def compute_iou(boxA, boxB): xA = max(boxA["TOP_LEFT_X"], boxB["TOP_LEFT_X"]) yA = max(boxA["TOP_LEFT_Y"], boxB["TOP_LEFT_Y"]) xB = min(boxA["BOTTOM_RIGHT_X"], boxB["BOTTOM_RIGHT_X"]) yB = min(boxA["BOTTOM_RIGHT_Y"], boxB["BOTTOM_RIGHT_Y"]) interArea = max(0, xB - xA + 1) * max(0, yB - yA + 1) boxAArea = (boxA["BOTTOM_RIGHT_X"] - boxA["TOP_LEFT_X"] + 1) * (boxA["BOTTOM_RIGHT_Y"] - boxA["TOP_LEFT_Y"] + 1) boxBArea = (boxB["BOTTOM_RIGHT_X"] - boxB["TOP_LEFT_X"] + 1) * (boxB["BOTTOM_RIGHT_Y"] - boxB["TOP_LEFT_Y"] + 1) iou = interArea / float(boxAArea + boxBArea - interArea) return iou def non_max_suppression(objects, non_max_suppression_threshold=0.5, score_key="MATCH_VALUE"): """ Filter objects overlapping with IoU over threshold by keeping only the one with maximum score. Args: objects (List[dict]): a list of objects dictionaries, with: {score_key} (float): the object score {top_left_x} (float): the top-left x-axis coordinate of the object bounding box {top_left_y} (float): the top-left y-axis coordinate of the object bounding box {bottom_right_x} (float): the bottom-right x-axis coordinate of the object bounding box {bottom_right_y} (float): the bottom-right y-axis coordinate of the object bounding box non_max_suppression_threshold (float): the minimum IoU value used to filter overlapping boxes when conducting non-max suppression. score_key (str): score key in objects dicts Returns: List[dict]: the filtered list of dictionaries. """ sorted_objects = sorted(objects, key=lambda obj: obj[score_key], reverse=True) filtered_objects = [] for object_ in sorted_objects: overlap_found = False for filtered_object in filtered_objects: iou = compute_iou(object_, filtered_object) if iou > non_max_suppression_threshold: overlap_found = True break if not overlap_found: filtered_objects.append(object_) return filtered_objects NMS_THRESHOLD = 0.2 detections = non_max_suppression(detections, non_max_suppression_threshold=NMS_THRESHOLD) image_with_detections = image.copy() for detection in detections: cv2.rectangle( image_with_detections, (detection["TOP_LEFT_X"], detection["TOP_LEFT_Y"]), (detection["BOTTOM_RIGHT_X"], detection["BOTTOM_RIGHT_Y"]), detection["COLOR"], 2, ) cv2.putText( image_with_detections, f"{detection['LABEL']} - {detection['MATCH_VALUE']}", (detection["TOP_LEFT_X"] + 2, detection["TOP_LEFT_Y"] + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, detection["COLOR"], 1, cv2.LINE_AA, ) # NMS_THRESHOLD = 0.2 # detection = non_max_suppression(detections, non_max_suppression_threshold=NMS_THRESHOLD) print("Image written to file-system: ", status) cv2.imshow("res", image_with_detections) cv2.waitKey(0)
问题排查与解决方案
1. 修正模板匹配参数顺序
cv2.matchTemplate的参数顺序应为输入图像(参考图)在前,模板图像在后,原代码中顺序写反了,导致匹配逻辑错误:
# 错误写法 template_matching = cv2.matchTemplate(template.template, image, cv2.TM_CCORR_NORMED) # 正确写法 template_matching = cv2.matchTemplate(image, template.template, cv2.TM_CCORR_NORMED)
2. 降低匹配阈值
原代码设置的阈值(如0.99)过高,小型元件的匹配相似度很难达到这个标准,建议降低阈值到0.8~0.9区间:
# 示例调整 Template(image_path="Component1.jpg", label="1", color=(0, 0, 255), matching_threshold=0.85), Template(image_path="Component2.jpg", label="2", color=(0, 255, 0,) , matching_threshold=0.8), Template(image_path="Component3.jpg", label="3", color=(0, 191, 255), matching_threshold=0.85),
3. 图像灰度化预处理
彩色图像的颜色差异会干扰匹配结果,将参考图和模板都转为灰度图进行匹配,能提升稳定性:
# 在Template类初始化中添加灰度化 self.template = cv2.cvtColor(cv2.imread(image_path), cv2.COLOR_BGR2GRAY) # 参考图也转为灰度图 image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 匹配时使用灰度图 template_matching = cv2.matchTemplate(image_gray, template.template, cv2.TM_CCORR_NORMED)
4. 验证图像读取有效性
添加图像读取检查,避免因路径错误或图像损坏导致匹配失败:
# 在Template类__init__中添加 self.template = cv2.imread(image_path) if self.template is None: raise ValueError(f"无法读取模板图像:{image_path}") # 参考图读取检查 image = cv2.imread("reference.jpg") if image is None: raise ValueError("无法读取参考图像:reference.jpg")
5. 实现多尺度匹配
针对尺寸差异问题,遍历不同缩放比例对模板进行缩放,覆盖更多可能的目标尺寸:
# 修改匹配循环,添加多尺度逻辑 scales = [0.8, 0.9, 1.0, 1.1, 1.2] # 根据实际情况调整 for template in templates: template_gray = cv2.cvtColor(template.template, cv2.COLOR_BGR2GRAY) h, w = template_gray.shape[:2] for scale in scales: # 缩放模板 resized_template = cv2.resize(template_gray, (int(w*scale), int(h*scale))) resized_h, resized_w = resized_template.shape[:2] if resized_h > image_gray.shape[0] or resized_w > image_gray.shape[1]: continue # 跳过过大的模板 # 匹配 result = cv2.matchTemplate(image_gray, resized_template, cv2.TM_CCORR_NORMED) locations = np.where(result >= template.matching_threshold) for (x, y) in zip(locations[1], locations[0]): match = { "TOP_LEFT_X": x, "TOP_LEFT_Y": y, "BOTTOM_RIGHT_X": x + resized_w, "BOTTOM_RIGHT_Y": y + resized_h, "MATCH_VALUE": result[y, x], "LABEL": template.label, "COLOR": template.color } detections.append(match)
6. 修复代码语法错误
原代码中templates列表定义缺少闭合的],这会导致代码运行报错,需补上:
templates = [ Template(image_path="Component1.jpg", label="1", color=(0, 0, 255), matching_threshold=0.99), Template(image_path="Component2.jpg", label="2", color=(0, 255, 0,) , matching_threshold=0.91), Template(image_path="Component3.jpg", label="3", color=(0, 191, 255), matching_threshold=0.99), ] # 补上这个闭合括号
7. 移除未定义变量的打印
原代码最后一行print("Image written to file-system: ", status)中的status变量未定义,会报错,建议删除或替换为实际的保存状态:
# 可以替换为实际保存代码 status = cv2.imwrite("detections_result.jpg", image_with_detections) print("Image written to file-system: ", status)
内容的提问来源于stack exchange,提问作者yessi
相关产品推荐
相关产品推荐

