Unit 08.02: Confidence, non-maximum suppression and double counting
A detector finds the same object several times. Suppression is what turns that back into a count.
Keep the most confident, drop its overlaps
Non-maximum suppression, sorted by confidence.
The code runs it over four detections of two objects.
def iou(a, b):
ix1, iy1 = max(a[0], b[0]), max(a[1], b[1])
ix2, iy2 = min(a[2], b[2]), min(a[3], b[3])
inter = max(0, ix2 - ix1) * max(0, iy2 - iy1)
union = ((a[2] - a[0]) * (a[3] - a[1]) + (b[2] - b[0]) * (b[3] - b[1]) - inter)
return inter / union
detections = [((10, 10, 50, 50), 0.92), ((12, 11, 52, 49), 0.88),
((11, 13, 49, 51), 0.75), ((80, 80, 120, 120), 0.81)]
kept = []
for box, score in sorted(detections, key=lambda d: -d[1]):
if all(iou(box, k) < 0.5 for k, _ in kept):
kept.append((box, score))
print(f"raw detections : {len(detections)}")
print(f"after NMS : {len(kept)}")
for box, score in kept:
print(f" {box} {score:.2f}")
# Three of the four boxes are the same object found three times. Without NMS a
# detector reports one carton as three, and both precision and any count you
# derive from it are wrong.
Three of the four boxes are one object found three times. Without suppression, one carton is reported as three - and every count, every precision figure and every downstream decision built on the count is wrong.
The suppression threshold is a second choice alongside the IoU threshold. Set it too high and duplicates survive; too low and genuinely adjacent objects get merged.
The mistake this prevents
The mistake is tuning the NMS threshold on images with well-separated objects. The setting only matters when objects are close together, so it has to be tuned on exactly the crowded images where it is hard.
Takeaway
Non-maximum suppression converts repeated detections into objects. Its threshold matters only on crowded images, so tune it there.
