use crate::ransac::{kernels::TukeyKernel, Consensus, ConsensusOutcome, RobustKernel};
#[derive(Debug, Clone, Copy)]
pub struct MagsacConsensus {
pub max_sigma: f64,
}
impl MagsacConsensus {
pub fn new(max_sigma: f64) -> Self {
Self { max_sigma }
}
}
impl Consensus for MagsacConsensus {
fn consensus(&self, residuals: &[f64], inliers_out: &mut Vec<bool>) -> ConsensusOutcome {
inliers_out.clear();
inliers_out.reserve(residuals.len());
let max_sigma_sq = self.max_sigma.max(0.0);
let kernel = TukeyKernel;
let mut score = 0.0f64;
let mut count = 0usize;
for &r in residuals {
let w = kernel.weight(r, max_sigma_sq);
if w > 0.0 {
count += 1;
}
score += w;
inliers_out.push(w > 0.0);
}
ConsensusOutcome {
score,
inlier_count: count,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_inliers_max_score() {
let m = MagsacConsensus::new(1.0);
let r = vec![0.0; 10];
let mut mask = Vec::new();
let out = m.consensus(&r, &mut mask);
assert!((out.score - 10.0).abs() < 1e-12);
assert_eq!(out.inlier_count, 10);
assert!(mask.iter().all(|&b| b));
}
#[test]
fn all_outliers_zero_score() {
let m = MagsacConsensus::new(1.0);
let r = vec![100.0; 5];
let mut mask = Vec::new();
let out = m.consensus(&r, &mut mask);
assert_eq!(out.score, 0.0);
assert_eq!(out.inlier_count, 0);
assert!(mask.iter().all(|&b| !b));
}
#[test]
fn smoother_residuals_score_higher() {
let m = MagsacConsensus::new(1.0);
let mut mask = Vec::new();
let tight: Vec<f64> = (0..20).map(|i| 0.01 * i as f64).collect();
let loose: Vec<f64> = tight.iter().map(|r| r + 0.4).collect();
let s_tight = m.consensus(&tight, &mut mask).score;
let s_loose = m.consensus(&loose, &mut mask).score;
assert!(
s_tight > s_loose,
"tighter residuals should score higher: tight={s_tight} loose={s_loose}"
);
}
}