ringgrid 0.5.6

Pure-Rust detector for coded ring calibration targets
Documentation
use crate::DetectedMarker;
use crate::conic::Conic2D;

use super::{CircleRefinementMethod, DetectConfig};

pub(crate) fn warn_center_correction_without_intrinsics(config: &DetectConfig, has_mapper: bool) {
    if config.circle_refinement == CircleRefinementMethod::None || has_mapper {
        return;
    }

    tracing::warn!(
        "center correction is running without camera intrinsics/undistortion; \
         lens distortion can still bias corrected centers"
    );
}

/// Apply projective center correction to the provided marker slice.
pub(crate) fn apply_projective_centers(markers: &mut [DetectedMarker], config: &DetectConfig) {
    use crate::ring::projective_center::{
        RingCenterProjectiveOptions, ring_center_projective_with_debug,
    };

    if !config.circle_refinement.uses_projective_center() {
        return;
    }

    let expected_ratio = if config.projective_center.use_expected_ratio {
        Some(config.marker_spec.r_inner_expected as f64)
    } else {
        None
    };
    let opts = RingCenterProjectiveOptions {
        expected_ratio,
        ratio_penalty_weight: config.projective_center.ratio_penalty_weight,
        ..Default::default()
    };

    let mut n_missing_conics = 0usize;
    let mut n_solver_failed = 0usize;
    let mut n_rejected_shift = 0usize;
    let mut n_rejected_residual = 0usize;
    let mut n_rejected_eig_sep = 0usize;
    let mut n_applied = 0usize;

    for m in markers.iter_mut() {
        let (Some(inner), Some(outer)) = (m.ellipse_inner.as_ref(), m.ellipse_outer.as_ref())
        else {
            n_missing_conics += 1;
            continue;
        };

        let center_before = m.center;
        let q_inner = Conic2D::from_ellipse(inner).mat;
        let q_outer = Conic2D::from_ellipse(outer).mat;
        let Ok(res) = ring_center_projective_with_debug(&q_inner, &q_outer, opts) else {
            n_solver_failed += 1;
            continue;
        };

        if let Some(max_residual) = config.projective_center.max_selected_residual
            && (!res.debug.selected_residual.is_finite()
                || res.debug.selected_residual > max_residual)
        {
            n_rejected_residual += 1;
            continue;
        }

        if let Some(min_sep) = config.projective_center.min_eig_separation
            && (!res.debug.selected_eig_separation.is_finite()
                || res.debug.selected_eig_separation < min_sep)
        {
            n_rejected_eig_sep += 1;
            continue;
        }

        let center_projective = [res.center.x, res.center.y];
        let dx = center_projective[0] - center_before[0];
        let dy = center_projective[1] - center_before[1];
        let center_shift = (dx * dx + dy * dy).sqrt();
        if let Some(max_shift_px) = config.projective_center.max_center_shift_px
            && (!center_shift.is_finite() || center_shift > max_shift_px)
        {
            n_rejected_shift += 1;
            continue;
        }

        m.center = center_projective;
        n_applied += 1;
    }

    tracing::debug!(
        applied = n_applied,
        missing_conics = n_missing_conics,
        solver_failed = n_solver_failed,
        rejected_shift = n_rejected_shift,
        rejected_residual = n_rejected_residual,
        rejected_eig_sep = n_rejected_eig_sep,
        "projective-center application summary",
    );
}