You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于模板匹配的目标检测代码无检测框输出问题求助

模板匹配目标检测无检测框问题求助

问题概述

从技术博客复制了基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 00:18:00