"""Threshold-independent and threshold-dependent verification metrics.""" import numpy as np from numpy.typing import ArrayLike from sklearn.metrics import det_curve, roc_curve def verification_metrics(labels: ArrayLike, scores: ArrayLike) -> dict[str, object]: """Compute FAR/FRR curves, ROC, DET and the approximate EER. Labels use 0 for forgery and 1 for genuine. Scores are lower for more genuine-looking samples. """ y = np.asarray(labels, dtype=np.int8) values = np.asarray(scores, dtype=np.float64) if y.ndim != 1 or values.ndim != 1 or len(y) != len(values) or len(y) == 0: raise ValueError("labels and scores must be non-empty 1D arrays of equal length") if not np.isin(y, [0, 1]).all() or len(np.unique(y)) != 2: raise ValueError("labels must contain both classes, encoded as 0=forgery and 1=genuine") fpr, tpr, roc_thresholds = roc_curve(y, -values) false_accept, false_reject, det_thresholds = det_curve(y, -values) fnr = 1.0 - tpr crossing = int(np.argmin(np.abs(fpr - fnr))) return { "far": fpr, "frr": fnr, "roc_fpr": fpr, "roc_tpr": tpr, "roc_thresholds": -roc_thresholds, "det_fpr": false_accept, "det_fnr": false_reject, "det_thresholds": -det_thresholds, "eer": float((fpr[crossing] + fnr[crossing]) / 2.0), "eer_threshold": float(-roc_thresholds[crossing]), }