NMS удаляет второй объект: как выбирать порог перекрытия

Камера видит два соседних товара. Детектор выдаёт две рамки, но после постобработки на экране остаётся одна. Если смотреть только на итог, кажется, что модель не заметила второй товар. На самом деле его мог удалить Non-Maximum Suppression — NMS, шаг, который убирает похожие предсказания одного класса.

Что решает порог

NMS берёт рамку с большим score и сравнивает с ней следующие. Если IoU двух рамок выше заданного порога, рамка с меньшим score подавляется. Так удаляются дубли одного объекта. Когда два настоящих объекта стоят близко, их рамки тоже могут сильно пересекаться. Тогда тот же механизм удаляет полезную детекцию. У torchvision.ops.nms порог сравнения задаётся параметром iou_threshold.

Код ниже показывает только логику двух рамок одинакового класса: первая имеет score 0,9, вторая — 0,8. Площади совпадают, сдвиг по x равен двум пикселям. Для двух объектов этого достаточно, чтобы увидеть эффект изменения порога.

def iou(a, b):
    x1, y1 = max(a[0], b[0]), max(a[1], b[1])
    x2, y2 = min(a[2], b[2]), min(a[3], b[3])
    intersection = max(0, x2 - x1) * max(0, y2 - y1)
    area_a = (a[2] - a[0]) * (a[3] - a[1])
    area_b = (b[2] - b[0]) * (b[3] - b[1])
    return intersection / (area_a + area_b - intersection)

boxes = [(0, 0, 10, 10), (2, 0, 12, 10)]
scores = [0.9, 0.8]
overlap = iou(*boxes)
for threshold in (0.5, 0.7):
    kept = [0] + ([1] if overlap <= threshold else [])
    print("порог", threshold, "IoU", round(overlap, 3), "оставлены", kept)
assert scores[0] > scores[1]
assert 0.5 < overlap < 0.7

IoU получается около 0,667. При пороге 0,5 остаётся только рамка 0, при 0,7 сохраняются обе. Поднять порог полезно, если система теряет соседние объекты, но одновременно может увеличить число дублей одного объекта. Поэтому настройку выбирают по размеченному набору, где есть и тесные пары, и одиночные объекты.

Как разобрать реальный пропуск

Сохраните предсказания до NMS с координатами, score и классом. Если нужной рамки там нет, порог постобработки не виноват — ищите ошибку детектора, порога уверенности или подготовки изображения. Если рамка была и исчезла, найдите подавившую её рамку и посчитайте IoU. Наконец, проверьте истинную разметку: две рамки могут относиться к одному объекту, а не к двум.

NMS обычно применяют внутри класса. Если классы разные, политика может отличаться; проверьте конкретную реализацию пайплайна. Меняя порог, фиксируйте тот же набор изображений и измеряйте precision/recall по нужным классам и сценам. Одной картинки недостаточно: «вернули второй товар» может означать «прибавили сто дублей» на другой линии.

Если после NMS рамки остались, но геометрия неточна, смотрите разбор mAP50 и строгих порогов IoU. NMS и метрика IoU используют одно отношение площадей, но отвечают на разные вопросы: первое решает, какие предсказания показать, второе сравнивает показанное с эталоном.

На курсе «Computer Vision Engineer: от детекции и Doc-AI до продакшна» вы обучаете детекторы, проверяете mAP/IoU и собираете сервисный слой с правилами постобработки. Это помогает отличать ошибку модели от ошибки настройки вывода. Откройте программу курса.

Продолжить на практике

  • Computer Vision Engineer: от детекции и Doc-AI до продакшна ↗

    Практический курс по Computer Vision. Соберёте продакшн-сервис: датасет → обучение (YOLO/RT-DETR, SAM), трекинг, OCR/Doc-AI, веб-приложение (UI+API) и мониторинг. Экспорт в ONNX/TensorRT/OpenVINO, батчинг/квантование под CPU/GPU. Плюс профессиональные блоки: mAP/IoU/Dice и калибровка доверия, QA разметки и аугментации, p95/стоимость, дрейф данных и алерты, guardrails (NMS/правила), A/B-тесты, Triton/gRPC/FastAPI.