MediumComputer Vision

Non-Maximum Suppression

Computer Vision

Medium

Problem

Non-Maximum Suppression removes duplicate object detections. Sort original box indices by descending confidence score, keeping input order when scores tie. Repeatedly select the first remaining box and suppress every later box whose IoU with it is greater than or equal to the threshold.

For boxes A and B:

A_{\mathrm{intersection}}=\max(0,x_R-x_L)\max(0,y_B-y_T)

\operatorname{IoU}(A,B)=\frac{A_{\mathrm{intersection}}}{A_A+A_B-A_{\mathrm{intersection}}}

Return the selected original indices in selection order. Return an empty list for empty input.

Theory

Non-Maximum Suppression (NMS) is a technique used to eliminate redundant overlapping detections, keeping only the best one. When object detectors produce multiple bounding boxes for the same object, NMS filters them down to a single detection by suppressing boxes that are not local maxima in terms of confidence scores.


Why NMS is Necessary

Detector behavior: Object detectors like YOLO, Faster R-CNN, and SSD generate thousands of candidate bounding boxes with confidence scores.

Overlapping predictions: Multiple boxes often cover the same object with varying degrees of accuracy.

Sliding window legacy: Even modern detectors can propose multiple boxes for one object due to anchor boxes or grid-based predictions.

Clean output requirement: Downstream tasks need exactly one detection per object for counting, tracking, or analysis.


The Core Idea

Goal: For each group of overlapping detections of the same object, keep only the one with the highest confidence score.

Method: Iteratively select the highest-scoring box, remove all boxes that overlap significantly with it, and repeat.

Key parameter: IoU threshold - defines what counts as "significant overlap"


Intersection over Union (IoU)

IoU measures the overlap between two bounding boxes:

\text{IoU}(A, B) = \frac{\text{Area}(A \cap B)}{\text{Area}(A \cup B)}

Properties:

Calculation:

\text{Area}(A \cap B) = \max(0, x_2^{int} - x_1^{int}) \times \max(0, y_2^{int} - y_1^{int})

Where intersection coordinates:

\text{Area}(A \cup B) = \text{Area}(A) + \text{Area}(B) - \text{Area}(A \cap B)


The NMS Algorithm

Input:

Process:

  1. Sort all boxes by confidence score (descending)
  2. Select the box with highest score, add to output
  3. Remove all boxes with IoU > threshold with the selected box
  4. Repeat steps 2-3 until no boxes remain

Output: Filtered list of non-overlapping boxes


Worked Example

Detections (box coordinates and scores):

NMS with threshold = 0.5:

Iteration 1:

Iteration 2:

Output: [Box A, Box C]

Box A and C represent different objects (low IoU). Boxes B and D were duplicates of A.


IoU Threshold Selection

High threshold (e.g., 0.7):

Low threshold (e.g., 0.3):

Common default: 0.5 balances false positives and missed detections


Class-Aware NMS

When detecting multiple object classes:

Option 1 - Per-class NMS:

Option 2 - Class-agnostic NMS:


Soft-NMS

Standard NMS completely removes overlapping boxes. Soft-NMS reduces their scores instead:

Linear decay:

s_i = \begin{cases} s_i & \text{if IoU}(M, b_i) < \text{threshold} \\ s_i (1 - \text{IoU}(M, b_i)) & \text{otherwise} \end{cases}

Gaussian decay:

s_i = s_i \cdot e^{-\frac{\text{IoU}(M, b_i)^2}{\sigma}}

Where M is the selected box and b_i is another box.

Benefit: Better handles cases where objects are genuinely close together


Computational Complexity

Naive implementation: O(N²) where N is number of boxes

Optimized implementations:

Practical consideration: N is typically small after confidence thresholding (hundreds, not thousands)


Bounding Box Representation

Common formats:

Corner format: (x1, y1, x2, y2) - top-left and bottom-right corners

Center format: (cx, cy, w, h) - center coordinates and dimensions

YOLO format: (cx, cy, w, h) normalized to [0, 1] relative to image size

Conversion required: IoU calculation typically uses corner format


Edge Cases

No boxes: Return empty list

Single box: Return that box (no suppression needed)

All boxes suppressed: Possible if one high-confidence box overlaps all others

Ties in confidence: Order may affect results; typically handled by stable sort


Limitations of Standard NMS

Greedy selection: May not find optimal global solution

Hard threshold: Binary decision to keep or remove

Occlusion handling: Struggles when objects genuinely overlap

Speed: Can be bottleneck for real-time systems with many detections


NMS Variants

Weighted NMS: Averages coordinates of merged boxes weighted by confidence

Cluster-NMS: Groups boxes into clusters before suppression

Matrix NMS: Parallelizable version using matrix operations

DIoU-NMS: Uses Distance-IoU instead of standard IoU, considers center distance


Integration in Detection Pipelines

Typical flow:

  1. Detector produces raw predictions (thousands of boxes)
  2. Confidence thresholding removes low-score boxes
  3. NMS removes duplicate detections
  4. Final output: clean set of detections

When applied: Post-processing step after model inference


Where Non-Maximum Suppression Shows Up

Examples

Example 1

Input
boxes = [[0, 0, 4, 4], [1, 0, 5, 4]], scores = [0.9, 0.8], iou_threshold = 0.5
Output
[0]
Explanation
Box 0 is selected first and suppresses Box 1 because their IoU is 0.6.

Example 2

Input
boxes = [[0, 0, 2, 2], [5, 5, 7, 7], [10, 10, 12, 12]], scores = [0.7, 0.9, 0.8], iou_threshold = 0.5
Output
[1, 2, 0]

Hints

  1. Sort range(len(scores)) with the scores as the key and reverse enabled.
  2. After selecting an index, retain only candidates whose IoU is below the threshold.

Requirements

Constraints

Starter Code

def nms(boxes: list, scores: list, iou_threshold: float) -> list:
    """
    Returns a list of retained indices.
    """
    # Write code here
    pass

Test Cases

CaseMatches
two overlappingpublic
no overlappublic