Intersection over Union Scores Box Overlap
Intersection over Union divides the overlap of two boxes by their combined area, giving one number between 0 and 1 for how well boxes agree.
Why Does This Exist?
Detectors output coordinates, but coordinate distance means nothing: is 5 pixels off good or terrible? It depends on box size. IoU normalizes agreement into one scale-free number used in four jobs: matching anchors to ground truth, labeling positives during training, suppressing duplicates in non-maximum suppression, and deciding true positives in evaluation. One metric, four jobs, which is why every detection page leans on it.
The general detection setup lives in object detection, and the boxes IoU matches start life as anchor boxes. Mask versions of the same ratio live on the IoU and Dice page.
Think of It Like This
Two picnic blankets on the grass
Two groups spread blankets that partly overlap. Overlap alone says little: 2 shared square meters matters differently for handkerchiefs versus circus tents. Divide the shared area by the total grass covered by either blanket and agreement becomes independent of blanket size.
That ratio is IoU: identical blankets score 1, blankets touching at a corner score 0, and a strong partial overlap lands near 0.74. Where it stops: blankets that do not touch all score 0 no matter how far apart they lie, so the ratio alone cannot guide a lost blanket home.
How It Actually Works
For boxes and , . The convention is fixed: IoU at or above usually counts as a true positive, and matching plus evaluation keep plain IoU even when training uses its upgrades. Plain IoU gives zero gradient when boxes do not touch, which stalls box regression at initialization.
The upgrades fix the stall. GIoU subtracts the empty space inside the smallest enclosing box, staying informative below zero. DIoU adds a center-distance penalty for faster convergence, and CIoU adds aspect-ratio consistency for tighter final boxes. Use CIoU or DIoU as the regression loss and keep plain IoU for matching and scoring.
Worked example
Take box A and box B . Each covers pixels. The overlap runs from to and from to , so pixels. The union is . IoU is , about : comfortably above the true-positive line, so these count as the same object.
GIoU on the same pair: the enclosing box covers . GIoU is . Nearly identical here because the boxes nearly coincide; on a clean miss the penalty term is what keeps training alive.
Code
The point of this snippet is the ratio itself: pure standard-library code recomputes the worked example exactly.
def compute_iou(box1: list, box2: list) -> float: x1, y1 = max(box1[0], box2[0]), max(box1[1], box2[1]) x2, y2 = min(box1[2], box2[2]), min(box1[3], box2[3]) inter = max(0, x2 - x1) * max(0, y2 - y1) a1 = (box1[2] - box1[0]) * (box1[3] - box1[1]) a2 = (box2[2] - box2[0]) * (box2[3] - box2[1]) return round(inter / (a1 + a2 - inter), 4)
print(compute_iou([50, 50, 150, 150], [58, 58, 158, 158]))# -> 0.7337Boxes offset by pixels on a -pixel side score . Below the line they would count as different objects.
Watch Out For
One threshold for all object sizes
IoU on a 20-pixel object tolerates far sloppier absolute errors than on a 400-pixel one, yet teams judge both by the same line. The symptom is small-object scores that look fine and mean little. Report across thresholds, as COCO-style AP does, instead of a single number.
IoU loss on non-overlapping starts
Plain IoU as a regression loss gives exactly zero gradient when prediction and target are disjoint, freezing early training. The symptom is boxes that never move from initialization. Use GIoU, DIoU, or CIoU for the loss while keeping IoU for decisions.
The Quick Version
- IoU is overlap area divided by union area: 0 for disjoint boxes, 1 for identical ones.
- The fixture scores , above the true-positive line.
- GIoU on the pair gives about ; on clean misses its penalty keeps gradients alive.
- Use plain IoU for matching and scoring, DIoU or CIoU for regression losses.