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 inputs
The lesson's three reference boxes and seven illustrative candidates, with the 0.60 confidence threshold and the 0.50 overlap rule.
import json # Reference boxes: [x, y, width, height], one per watermelon. references = {"large": [45, 52, 54, 51], "small": [258, 39, 36, 34], "wide": [216, 142, 60, 36]} # Illustrative candidates: [letter, x, y, width, height, score in hundredths]. candidates = [ ["A", 43, 50, 58, 55, 93], ["B", 213, 139, 64, 41, 81], ["C", 145, 46, 45, 47, 72], ["D", 266, 49, 38, 35, 66], ["E", 46, 153, 48, 38, 60], ["F", 256, 37, 41, 39, 42], ["G", 126, 144, 47, 48, 34], ] threshold = 60 # accept when score >= 0.60 iou_match = 0.52. Accept, then match one to one
Keep boxes scoring at or above the threshold, check them from the highest score down, and let each take the unused reference it overlaps most if that IoU reaches 0.50.
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])) shared = w * h return shared / (a[2] * a[3] + b[2] * b[3] - shared) accepted = sorted([c for c in candidates if c[5] >= threshold], key=lambda c: -c[5]) used, verdicts = set(), {} for c in accepted: best, best_iou = None, 0 for name, ref in references.items(): if name not in used and iou(c[1:5], ref) > best_iou: best, best_iou = name, iou(c[1:5], ref) if best is not None and best_iou >= iou_match: used.add(best) verdicts[c[0]] = "TP" else: verdicts[c[0]] = "FP"3. Count and divide
Unmatched references are misses. Precision and recall divide the true positives by the accepted boxes and by the references; an empty denominator gives null.
tp = sum(v == "TP" for v in verdicts.values()) fp = sum(v == "FP" for v in verdicts.values()) fn = len(references) - tp ratio = lambda n, d: n / d if d else None # an empty denominator is undefined result = {"verdicts": verdicts, "tp": tp, "fp": fp, "fn": fn, "precision": ratio(tp, tp + fp), "recall": ratio(tp, tp + fn)} print(json.dumps(result))
Expected result
Running the three cells in order produces this result in either language.
{
"verdicts": {
"A": "TP",
"B": "TP",
"C": "FP",
"D": "FP",
"E": "FP"
},
"tp": 2,
"fp": 3,
"fn": 1,
"precision": 0.4,
"recall": 0.6666666666666666
}