use crate::types::{Detection, DetectionExplanation};
#[derive(Clone, Debug)]
pub struct DecisionConfig {
pub epsilon: f32,
}
impl Default for DecisionConfig {
fn default() -> Self {
Self { epsilon: 0.0001 }
}
}
pub fn resolve(
mut candidates: Vec<Detection>,
thresholds: &dyn Fn(&Detection) -> f32,
) -> Vec<Detection> {
candidates.retain(|det| det.score >= thresholds(det));
candidates.sort_by(|a, b| {
a.start
.cmp(&b.start)
.then_with(|| a.end.cmp(&b.end))
});
let mut resolved = Vec::new();
for candidate in candidates {
if let Some(last) = resolved.last_mut() {
if overlaps(last, &candidate) {
if prefer_candidate(last, &candidate) {
*last = candidate;
}
continue;
}
}
resolved.push(candidate);
}
resolved
}
fn overlaps(a: &Detection, b: &Detection) -> bool {
a.start < b.end && b.start < a.end
}
fn prefer_candidate(current: &Detection, challenger: &Detection) -> bool {
if (current.score - challenger.score).abs() > 0.0001 {
return challenger.score > current.score;
}
let current_validator = matches!(current.explanation, DetectionExplanation::Validator { .. });
let challenger_validator = matches!(challenger.explanation, DetectionExplanation::Validator { .. });
if current_validator != challenger_validator {
return challenger_validator;
}
let current_len = current.end.saturating_sub(current.start);
let challenger_len = challenger.end.saturating_sub(challenger.start);
if current_len != challenger_len {
return challenger_len > current_len;
}
challenger.start < current.start
}