Detection Metrics¶
polars-cv provides a comprehensive suite of detection metrics built on top of Polars lazy expressions and the polars-cv matching primitives. All curve computations use native Polars operations.
Architecture¶
The metrics system follows a three-layer architecture:
- Matchers convert raw data into a canonical
DetectionTable. - Metric functions compute curves and scalar metrics from the table.
- Result objects carry computed curves with convenience methods.
DetectionTable¶
The DetectionTable is the canonical intermediate representation. It wraps two
aligned lazy frames:
- detections — one row per detection with
image_id,class_id,score,is_tp,gt_idx,iou,det_idx. - image_metadata — one row per (image, class) with
n_gts,weight,gt_label.
Matchers¶
PreMatchedAdapter¶
For data that already has per-detection TP/FP assignments:
from polars_cv.metrics import PreMatchedAdapter, precision_recall_curve
adapter = PreMatchedAdapter()
table = adapter.match(
data,
pred_col="confidence",
gt_col="is_tp",
image_id_col="image_id",
)
result = precision_recall_curve(table)
ContourMatcher¶
For heatmap + binary mask inputs (used by FROC/LROC workflows):
from polars_cv.metrics import ContourMatcher, froc_curve
matcher = ContourMatcher(iou_threshold=0.5, extraction_threshold=0.1)
table = matcher.match(data, pred_col="heatmap", gt_col="gt_mask")
result = froc_curve(table)
BBoxMatcher¶
For bounding-box detection inputs:
from polars_cv.metrics import BBoxMatcher, precision_recall_curve
matcher = BBoxMatcher(iou_threshold=0.5)
table = matcher.match(
data,
pred_col="pred_bboxes",
gt_col="gt_bboxes",
score_col="pred_scores",
)
result = precision_recall_curve(table)
Available Metrics¶
Precision-Recall¶
from polars_cv.metrics import (
precision_recall_curve,
average_precision,
mean_average_precision,
precision_at_threshold,
recall_at_threshold,
f1_at_threshold,
)
pr = precision_recall_curve(table)
ap = average_precision(table)
map_val = mean_average_precision(table, iou_thresholds=[0.5, 0.55, 0.6, ..., 0.95])
FROC¶
from polars_cv.metrics import froc_curve
result = froc_curve(table)
print(result.auc(fp_range=(0, 8)))
print(result.sensitivity_at_fp(1.0))
print(result.summary_table())
LROC¶
from polars_cv.metrics import lroc_curve
result = lroc_curve(table)
print(result.auc())
print(result.sensitivity_at_fpf(0.5))
Confusion Matrix¶
from polars_cv.metrics import confusion_at_threshold
counts = confusion_at_threshold(table, threshold=0.5)
# {'tp': 10, 'fp': 3, 'fn': 2}
Bootstrap Confidence Intervals¶
# Sequential (works with any metric)
ci = result.bootstrap_ci(n_bootstrap=1000, seed=42)
# Vectorized (faster, for PR AUC)
from polars_cv.metrics import bootstrap_pr_auc
ci = bootstrap_pr_auc(table, n_bootstrap=1000, seed=42)
IoU Re-thresholding¶
The DetectionTable stores raw IoU values from matching, enabling
re-thresholding without re-running the matching step:
# Compute mAP across COCO IoU thresholds
map_val = mean_average_precision(
table,
iou_thresholds=[0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95],
)
Class-Aware Metrics¶
Pass a class_col to the matcher, then compute per-class or averaged metrics: