Intersection over Union
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 everywhere in detection: matching anchors to ground truth, labeling positives in RPN training, suppressing duplicates in NMS, and deciding true positives in evaluation. One metric, four jobs, which is why every detection page leans on it.
Think of It Like This
Two picnic blankets on the grass
Two groups spread blankets that partly overlap. Overlap alone says little: 2 square meters shared matters differently for handkerchiefs versus circus tents. Divide the shared area by the total grass covered by either blanket and you get agreement independent of blanket size.
That ratio is IoU. Identical blankets score 1, touching corners score 0, and half-overlap lands somewhere sensible in between.
How It Actually Works
Definition and a worked example
. Take box A and box B . Each covers . The overlap runs from 100 to 150 and from 100 to 150, so . Union is . IoU is . Below the usual 0.5 true-positive line, these count as different objects.
GIoU, DIoU and CIoU
Plain IoU gives zero gradient when boxes do not touch, stalling box regression. The upgrades fix that. GIoU subtracts the empty space in the smallest enclosing box: for the example above the enclosing covers , so GIoU is , still informative below zero. DIoU adds center-distance penalties for faster convergence, and CIoU adds aspect-ratio consistency for tighter finals. Use CIoU or DIoU as the regression loss and keep plain IoU for matching and evaluation.
Code
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 inter / (a1 + a2 - inter)
print(round(compute_iou([50, 50, 150, 150], [100, 100, 200, 200]), 4)) # -> 0.1429Watch Out For
One threshold for all object sizes
IoU 0.5 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 and 1 for identical ones.
- Worked example: boxes offset by half their size score .
- GIoU, DIoU and CIoU add enclosing-box, distance and shape terms that train through misses.
- Use IoU for matching and scoring, its upgrades for regression losses.