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

使用Python的cv2库检测不同尺度模板的多个实例遇阻

多尺度模板匹配的多实例检测问题

我正在用OpenCV的cv2库编写Python模板匹配脚本,已经实现不同尺度下单个模板实例的检测,但无法识别不同尺度下的多个模板实例。我尝试找到第一个匹配实例后遮挡它,再重新扫描,但第二次扫描无法正确匹配到模板。

相关参考图:

  • 模板图
  • 待检测原图
  • 标注检测框后的图
  • 目标查找图

以下是我运行的代码:

import numpy as np
import imutils
import glob
import cv2
from imutils.object_detection import non_max_suppression


imagen_a_detectar = r"C:\Users\OMEN\OneDrive\Documentos\TEC\PROYECTO RESIDEO\REPO\SCRUMsinCUM\software\Deteccion Iconos\flecha50.jpg"
imagen_a_comparar = r"C:\Users\OMEN\OneDrive\Documentos\TEC\PROYECTO RESIDEO\REPO\SCRUMsinCUM\software\Deteccion Iconos\HomeAuto3.png"

template = cv2.imread(imagen_a_detectar)
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
template = cv2.Canny(template, 50, 200)
(tH, tW) = template.shape[:2]

for imagePath in glob.glob(imagen_a_comparar):
    image = cv2.imread(imagePath)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    found = None
    threshold = 0.9

    for scale in np.linspace(0.2, 1.0, 20)[::-1]:
        scalem = scale
        resized = imutils.resize(gray, width = int(gray.shape[1] * scale))
        r = gray.shape[1] / float(resized.shape[1])

        if resized.shape[0] < tH or resized.shape[1] < tW:
            break
        edged = cv2.Canny(resized, 50, 200)

        result = cv2.matchTemplate(edged, template, cv2.TM_CCOEFF_NORMED)
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)

        if found is None or max_val > found[0]:
            found = (max_val, max_loc, r)


    (_, max_loc, r) = found
    (startX, startY) = (int(max_loc[0] * r), int(max_loc[1] * r))
    (endX, endY) = (int((max_loc[0] + tW) * r), int((max_loc[1] + tH) * r))
    m1 = cv2.rectangle(image, (startX, startY), (endX, endY), (0, 0, 255), -1)

    gray2 = cv2.cvtColor(m1, cv2.COLOR_BGR2GRAY)
    resized2 = imutils.resize(gray2, width = int(gray.shape[1] * scalem))
    r2 = gray.shape[1] / float(resized.shape[1])
    edged2 = cv2.Canny(resized2, 50, 200)

    result2 = cv2.matchTemplate(edged2, template, cv2.TM_CCOEFF_NORMED)
    min_val2, max_val2, min_loc2, max_loc2 = cv2.minMaxLoc(result2)
    found2 = (max_val2, max_loc2, r2)
    
    (_, max_loc2, r2) = found2
    (startX2, startY2) = (int(max_loc2[0] * r2), int(max_loc2[1] * r2))
    (endX2, endY2) = (int((max_loc2[0] + tW) * r2), int((max_loc2[1] + tH) * r2))

    m2 = cv2.rectangle(m1, (startX2, startY2), (endX2, endY2), (0, 0, 255), -1)

    cv2.imshow("Image", m2)
    cv2.waitKey(0)
    print(str(endX) + ' ' +str(endY)+ ' ' +str(r))

解决思路与修正代码

你的问题核心在于仅用单一尺度重新检测,且遮挡重扫的逻辑存在漏洞。正确方案是在所有尺度下收集所有符合阈值的匹配结果,再用非极大值抑制(NMS)去除重叠/重复框,无需逐个遮挡扫描。

修正后的代码:

import numpy as np
import imutils
import cv2
from imutils.object_detection import non_max_suppression

# 替换为你的文件路径
template_path = r"C:\Users\OMEN\OneDrive\Documentos\TEC\PROYECTO RESIDEO\REPO\SCRUMsinCUM\software\Deteccion Iconos\flecha50.jpg"
image_path = r"C:\Users\OMEN\OneDrive\Documentos\TEC\PROYECTO RESIDEO\REPO\SCRUMsinCUM\software\Deteccion Iconos\HomeAuto3.png"

# 预处理模板
template = cv2.imread(template_path)
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
template = cv2.Canny(template, 50, 200)
(tH, tW) = template.shape[:2]

# 读取待检测图像
image = cv2.imread(image_path)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
found_boxes = []
threshold = 0.7  # 可根据匹配效果调整阈值

# 遍历所有尺度,收集所有符合阈值的匹配框
for scale in np.linspace(0.2, 1.0, 20)[::-1]:
    resized = imutils.resize(gray, width=int(gray.shape[1] * scale))
    r = gray.shape[1] / float(resized.shape[1])
    
    # 缩放后的图像小于模板时停止遍历
    if resized.shape[0] < tH or resized.shape[1] < tW:
        break
    
    edged = cv2.Canny(resized, 50, 200)
    result = cv2.matchTemplate(edged, template, cv2.TM_CCOEFF_NORMED)
    
    # 提取所有匹配度超过阈值的位置
    loc = np.where(result >= threshold)
    for (x, y) in zip(loc[1], loc[0]):
        # 转换为原图中的坐标
        startX = int(x * r)
        startY = int(y * r)
        endX = int((x + tW) * r)
        endY = int((y + tH) * r)
        found_boxes.append((startX, startY, endX, endY))

# 使用非极大值抑制去除重叠的重复检测框
boxes = np.array(found_boxes)
if len(boxes) > 0:
    boxes = non_max_suppression(boxes)
    
    # 绘制所有有效检测框
    for (startX, startY, endX, endY) in boxes:
        cv2.rectangle(image, (startX, startY), (endX, endY), (0, 0, 255), 2)

# 显示检测结果
cv2.imshow("检测结果", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

关键改进点:

  • 遍历所有尺度时,收集所有符合阈值的匹配框,而非仅保留最佳匹配
  • 用non_max_suppression过滤重叠框,避免同一目标被重复识别
  • 移除低效的遮挡重扫逻辑,改用批量检测+NMS的可靠方案

内容的提问来源于stack exchange,提问作者Jorge Garcia

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 20:14:55