Modules/Computer Vision/ NMS Lab

IoU and Non-Max Suppression

A detector never proposes just one box per object — it proposes dozens. IoU measures how much two boxes overlap; NMS uses that number to collapse the pile back down to one box per object.

NMS Threshold

0.50

boxes A-D are four raw proposals from one detector pass, four confidence scores, two actual objects

Confidence

Proposals

—

Pairwise IoU

Result

KeptA, D
SuppressedB, C

 

Overview

A detector never proposes just one box per object — it proposes dozens.

In this guide
  1. The problem it solves
  2. Non-Max Suppression
  3. Measuring overlap with one count
  4. Non-max suppression, step by step
  5. Scoring box overlap, then removing the duplicates
  6. Try it yourself
  7. The short of it
  8. IoU as a loss function
  9. Where IoU appears beyond detection
  10. Common mistakes
  11. Questions people ask
  12. Recap in one screen

IoU and Non-Max Suppression: A Practical Guide

Turning a pile of overlapping boxes into one box per object.

The problem it solves

A detector's region proposal stage doesn't stop at one box per object — it scores hundreds of candidate boxes and keeps each one above a confidence floor. It for a single real object often means a cluster of overlapping, near-duplicate boxes. Intersection over Union (IoU) is the standard way to measure how much two boxes overlap: the area they share, divided by the total area either one covers. IoU = 1 means identical boxes. IoU = 0 means no overlap at all.

Non-Max Suppression (NMS) uses IoU to collapse each cluster into a single detection. Sort proposals by confidence, keep the most confident one, discard each left box whose IoU with it exceeds a threshold. And repeat with the next survivor. The threshold is the just knob: too low and nearby but distinct objects get merged. Too high and duplicate boxes on the same object all survive. An IoU threshold of 0.5 is the traditional default for test benchmarks like PASCAL VOC, while COCO uses multiple thresholds from 0.5 to 0.95 in steps of 0.05 to reward tighter localisation.

This page walks through the IoU formula with a worked numeric case, builds NMS step by step. And lets you drag boxes in the visualisation to watch IoU update in real time. The code blocks compute IoU from coordinates, run NMS on a set of proposals. And print which boxes survive at other thresholds. It also covers Soft-NMS, which decays confidence scores instead of hard-deleting boxes, reducing the risk of suppressing a valid detection in a crowded scene.

Non-Max Suppression

NMS turns that overlap score into a cleanup rule. Sort each proposal by confidence, descending. Take the top one, keep it. And discard each left box whose IoU with it exceeds a threshold — those are treated as duplicate detections of the same object. Move to the next surviving box by confidence and repeat, until nothing is left to process. The threshold is the just knob: too low and boxes on genuinely other but nearby objects get merged into one. Too high and duplicate boxes on the same object all survive.

Measuring overlap with one count

IoU checks two boxes by dividing the area they share by the area they cover together:

IoU = intersection / union

Worked through. Box A spans (0, 0) to (100, 100). Box B spans (50, 50) to (150, 150).

  • Intersection: from (50, 50) to (100, 100) — 50 × 50 = 2,500.
  • Union: 10,000 + 10,000 − 2,500 = 17,500.
  • IoU = 2,500 / 17,500 = 0.14.

Reading the scale:

IoUInterpretation
> 0.9Near-perfect box
0.7–0.9Good localisation
0.5–0.7Acceptable by the traditional threshold
< 0.5Usually counted as a miss
0No overlap at all

Two properties make it the standard measure. It is scale-invariant — a 20-pixel error on a small object scores the same as a proportionally close error on a large one — and it is bounded. So it can be thresholded and averaged.

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])
    inter = 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 inter / (area_a + area_b - inter)

The two max(0, ...) calls are what handle boxes that do not overlap. Without them a negative width times a negative height gives a positive area and a nonsensical result.

Non-max suppression, step by step

A detector fires at many nearby positions. So one object gives a cluster of boxes. NMS reduces each cluster to one.

  1. Discard each box below a confidence threshold.
  2. Sort the rest by confidence, highest first.
  3. Take the top box and keep it.
  4. Remove each left box whose IoU with it exceeds the NMS threshold.
  5. Repeat from step 3 with what is left.

Two thresholds control the outcome, and they do other jobs. The confidence threshold decides how sure the net must be before a box is considered at all — raise it for fewer false positives, less it for better recall. The NMS IoU threshold decides how much overlap counts as a duplicate. 0.45–0.5 is typical.

The failure mode is genuinely overlapping objects: two people standing close together give boxes with high IoU. And standard NMS deletes one of them. Soft-NMS addresses this by reducing the confidence of overlapping boxes rather than deleting them outright. So a strong second detection can still survive.

Note also that NMS is applied per class in most implementations — a dog box and a person box overlapping heavily are not duplicates of each other.

Scoring box overlap, then removing the duplicates

Intersection over union is four max/min steps. Non-max suppression is a sort and a loop. Both are written out here, along with the two cases where each one behaves badly.

example_01.pyNumPy
Output

Next, slide two boxes of equal size apart to see how much overlap the 0.5 threshold really needs.

example_02.pyNumPy
Output

With IoU set, set up a cluster of seven overlapping proposals to test non-max suppression.

example_03.pyNumPy
Output

Now apply the NMS algorithm at threshold 0.5 and trace which boxes survive.

example_04.pyNumPy
Output

Sweeping the threshold shows how strongly it controls the outcome.

example_05.pyNumPy
Output

NMS breaks down when objects genuinely overlap, and Soft-NMS offers a workaround.

example_06.pyNumPy
Output

Try it yourself

  1. Read the stage at the default threshold. Box A (confidence 0.95) is kept. Box D, a split object with no real overlap with A, is also kept. Boxes B and C overlap A too heavily and are suppressed as duplicates.
  2. Less the threshold toward 0.1. Nothing changes here — B and C now fail a much looser bar. So tightening it more has no effect on this particular layout.
  3. Raise the threshold past roughly 0.59. Box B's overlap with A no longer exceeds the (now looser) suppression bar. So B is kept as a second detection of what is really the same object.
  4. Raise it past roughly 0.81. Now C survives too — at this threshold NMS considers all four boxes other enough to keep, still though A, B and C clearly describe one dog.

The short of it

IoU is a pure geometry math — it knows nothing about confidence or class. NMS is the policy layer that uses it: keep the most confident box, discard anything that overlaps it past a threshold, repeat. Set the threshold too low and it merges distinct nearby objects into one detection. Set it too high and duplicate boxes on the same object all survive. Each object detector that outputs boxes runs some form of this after its raw proposals come out.

IoU as a loss function

Since IoU is what test measures, training right on it is appealing — and it took a few iterations to make it work.

Plain IoU has a fatal gradient problem: when two boxes do not overlap at all, IoU is 0 for each possible position. So there is no gradient telling the box which way to move. Three refinements fix it:

  • GIoU adds a cost based on the smallest box enclosing both. So non-overlapping boxes still get a useful gradient.
  • DIoU adds a term for the distance between the box centres, which converges faster.
  • CIoU adds an aspect-ratio term as well. And is the default regression loss in a few modern detectors.

The option, still widely used, is smooth L1 on the coordinates — simple and stable. But it optimises a proxy rather than the metric. And it treats a 10-pixel error the same on a small object as on a large one.

Where IoU appears beyond detection

  • Giving anchors in training. An anchor box is treated as a positive case if its IoU with a ground-truth box exceeds a threshold. And as background below a less one. Anchors in between are ignored.
  • Segmentation. The same ratio computed over pixel masks rather than boxes is the Jaccard index. And mean IoU is the standard segmentation metric.
  • Tracking. Frame-to-frame association is often IoU-based — a detection is matched to the track whose last box it overlaps most.
  • Test at a few thresholds. COCO's primary metric averages mAP over IoU thresholds from 0.5 to 0.95. It is what makes it reward tight boxes rather than approximate ones.

Common mistakes

  • Forgetting to clamp negative overlaps, giving a positive intersection for disjoint boxes.
  • Mixing box formats. Applying corner-format code to centre-format boxes gives plausible-looking nonsense.
  • Running NMS across classes when it should be per class, deleting legitimate detections of other objects.
  • An NMS threshold that is too low, which removes genuine nearby objects. Or too high, which leaves duplicates.
  • Tuning the confidence threshold on the test set rather than on validation data.
  • Assuming NMS is cheap. With tens of thousands of candidate boxes it can dominate inference time. Batched GPU implementations exist for this reason.

Questions people ask

What IoU threshold means "correct"? 0.5 by the Pascal VOC convention. COCO averages from 0.5 to 0.95 in steps of 0.05. It is stricter and now more standard.

Can IoU exceed 1? No — the intersection can never exceed the union.

Is NMS still needed? For most detectors, yes. DETR-style set-prediction nets avoid it by design, which is one of their main attractions.

How do I handle heavily overlapping objects? Soft-NMS, a more NMS threshold. Or an design that predicts a set right.

Should I train with an IoU-based loss? CIoU or DIoU generally beats smooth L1 on localisation quality. And both are readily on hand in modern detection libraries.

Why is my mAP low despite good-looking boxes? Often the strict IoU thresholds: boxes that look right to a human may score 0.6. It passes at 0.5 and fails at 0.75.

Recap in one screen

  • IoU is overlap divided by union — scale-invariant, bounded between 0 and 1.
  • Clamp negative overlaps to zero. Or disjoint boxes report a false positive area.
  • NMS keeps the highest-confidence box and removes overlapping duplicates, per class.
  • Two thresholds matter: confidence (how sure) and NMS IoU (how much overlap is a duplicate).
  • GIoU, DIoU and CIoU make IoU usable as a training loss by supplying gradients when boxes do not overlap.

Recall check

0 of 3

Say the answer out loud before you reveal it — recalling it is what makes it stick, and rereading it is not.

  1. What does this module say about “The problem it solves”?

  2. What does this module say about “NMS Threshold”?

  3. What does this module say about “Non-Max Suppression”?

Cheat sheet

IoU and Non-Max Suppression

A detector proposes four boxes for two objects. IoU measures the overlap, and Non-Max Suppression uses that number to throw the duplicates away.

COMPUTER VISION · vizlearn.in/computer_vision/iou_and_non_max_suppression.html

Further reading

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.