feat(detect): 约束预测框互不重叠
人工标注的真值里一处缺陷只有一个框:11211 张图、151 张多框图,重叠框对为 0。 而模型预测在验证集上有 6.9% 的图存在重叠框——NMS 只在同类内、按固定 IoU 阈值抑制, 压不掉两类情况:同一处缺陷被两个类别各框一次(实测 IoU 高达 0.99),以及 IoU 低于 NMS 阈值的同类重叠(grease 66 对、wrinkle 45 对)。 - engine/detection.suppress_overlaps:按置信度贪心保留互不重叠的框,可设允许的 最大 IoU;另可丢弃被大框完全包住的小框(面积悬殊时 IoU 很小,但显然是重复表达)。 - visualize_detections 默认 --max-overlap 0(与真值一致),并放出 --nms-iou 与 类别无关 NMS 开关;--max-overlap -1 可看模型原始输出。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
43c8c4a179
commit
eba533a5b2
@@ -102,6 +102,44 @@ def match_boxes(
|
||||
)
|
||||
|
||||
|
||||
def suppress_overlaps(
|
||||
boxes: Sequence[Box], max_iou: float = 0.0, contained: bool = True
|
||||
) -> list[int]:
|
||||
"""贪心地挑出互不重叠的框,返回保留下来的下标(按输入顺序)。
|
||||
|
||||
按置信度从高到低考察,与任一已保留框重叠超过 `max_iou` 就丢弃。
|
||||
|
||||
为什么需要这一步:NMS 只在**同类**内、且按固定 IoU 阈值抑制,所以同一处缺陷被两个
|
||||
类别各框一次(跨类重复),或两个框 IoU 低于 NMS 阈值时,都会留下重叠框。而人工标注的
|
||||
真值里一处缺陷只有一个框,零重叠——推理侧不加这层约束,指标和图上都会出现真值里
|
||||
不可能有的形态。
|
||||
|
||||
Args:
|
||||
max_iou: 允许的最大 IoU。0 表示碰到就算重叠,完全不允许。
|
||||
contained: 是否额外丢弃被已保留框完全包住的框。整图大框套小框时 IoU 可能很小
|
||||
(面积悬殊),但那显然是同一处目标的重复表达。
|
||||
"""
|
||||
order = sorted(range(len(boxes)), key=lambda i: -(boxes[i].score or 0.0))
|
||||
kept: list[int] = []
|
||||
for index in order:
|
||||
box = boxes[index]
|
||||
if any(
|
||||
iou(box, boxes[other]) > max_iou or (contained and _inside(box, boxes[other]))
|
||||
for other in kept
|
||||
):
|
||||
continue
|
||||
kept.append(index)
|
||||
return sorted(kept)
|
||||
|
||||
|
||||
def _inside(inner: Box, outer: Box) -> bool:
|
||||
"""inner 是否完全落在 outer 里(含边界)。"""
|
||||
return (
|
||||
outer.x1 <= inner.x1 and outer.y1 <= inner.y1
|
||||
and outer.x2 >= inner.x2 and outer.y2 >= inner.y2
|
||||
)
|
||||
|
||||
|
||||
def draw_boxes(image, ground_truth: Sequence[Box], predictions: Sequence[Box],
|
||||
classes: Sequence[str], match: Match, width: int = 2):
|
||||
"""把真值与预测画在图上(就地修改传入的 PIL 图像并返回)。
|
||||
|
||||
Reference in New Issue
Block a user