Choose a language and read the cells in order. These are small teaching examples; the visual lesson runs separately.
Follow the calculation
Same inputs · Same operation · Same result
3 teaching cells · Python standard library · Read-only
1. Prepare the pictures' references and the predictions
Five reference watermelons in three pictures, as [id, picture, x, y, w, h], and eight illustrative predictions written by hand (no model made them), as [id, picture, x, y, w, h, score in hundredths]. Coordinates are picture pixels.
import json refs = [ ["a", 1, 12, 19, 36, 34], ["b", 1, 70, 62, 40, 24], ["c", 2, 24, 31, 36, 34], ["d", 3, 14, 41, 36, 34], ["e", 3, 72, 18, 40, 24], ] preds = [ ["A", 1, 14, 20, 36, 34, 95], ["B", 1, 80, 12, 32, 30, 80], ["C", 1, 68, 60, 40, 26, 40], ["D", 2, 22, 30, 38, 36, 90], ["E", 2, 28, 34, 34, 32, 60], ["F", 2, 84, 46, 32, 30, 30], ["G", 3, 12, 42, 36, 32, 70], ["H", 3, 60, 10, 34, 26, 50], ] IOU_MATCH = 0.52. Rank, match, accumulate, then measure the area
Rank by score (ties by letter). Each prediction takes the highest-IoU reference in its own picture; IoU ≥ 0.5 and still unmatched is a true positive, a taken reference makes a duplicate (false positive). Precision and recall after each prediction; the envelope takes the best precision at the same or a later rank; AP adds recall rise × envelope. Round only the report.
def iou(a, b): w = max(0, min(a[0] + a[2], b[0] + b[2]) - max(a[0], b[0])) h = max(0, min(a[1] + a[3], b[1] + b[3]) - max(a[1], b[1])) inter = w * h union = a[2] * a[3] + b[2] * b[3] - inter return inter / union if union > 0 else 0 ranked = sorted(preds, key=lambda p: (-p[6], p[0])) matched, flags = set(), [] for p in ranked: best, best_iou = None, -1 for r in refs: if r[1] == p[1]: v = iou(p[2:6], r[2:6]) if v > best_iou: best, best_iou = r, v tp = best is not None and best_iou >= IOU_MATCH and best[0] not in matched if tp: matched.add(best[0]) flags.append(tp) tp = fp = 0 precision, recall = [], [] for f in flags: tp, fp = tp + f, fp + (not f) precision.append(tp / (tp + fp)) recall.append(tp / len(refs)) envelope = precision[:] for i in range(len(envelope) - 2, -1, -1): envelope[i] = max(envelope[i], envelope[i + 1]) ap, prev = 0, 0 for r, e in zip(recall, envelope): if r > prev: ap, prev = ap + (r - prev) * e, r result = { "order": "".join(p[0] for p in ranked), "verdicts": "".join("T" if f else "F" for f in flags), "ap": round(ap, 6), }3. Inspect the result
Print the result as JSON so both languages use the same output format.
print(json.dumps(result))
Expected result
Running the three cells in order produces this result in either language.
{
"order": "ADBGEHCF",
"verdicts": "TTFTFFTF",
"ap": 0.664286
}