#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum GradPolarity {
DarkToLight,
LightToDark,
Auto,
}
#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AngularAggregator {
Median,
TrimmedMean {
trim_fraction: f32,
},
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(default)]
pub struct MarkerSpec {
pub r_inner_expected: f32,
pub inner_search_halfwidth: f32,
pub inner_grad_polarity: GradPolarity,
pub radial_samples: usize,
pub theta_samples: usize,
pub aggregator: AngularAggregator,
pub min_theta_coverage: f32,
#[serde(default = "default_min_theta_consistency")]
pub min_theta_consistency: f32,
}
impl Default for MarkerSpec {
fn default() -> Self {
let r_inner_expected = 0.328f32 / 0.672f32;
Self {
r_inner_expected,
inner_search_halfwidth: 0.08,
inner_grad_polarity: GradPolarity::LightToDark,
radial_samples: 64,
theta_samples: 96,
aggregator: AngularAggregator::Median,
min_theta_coverage: 0.6,
min_theta_consistency: default_min_theta_consistency(),
}
}
}
impl MarkerSpec {
pub fn search_window(&self) -> [f32; 2] {
[
self.r_inner_expected - self.inner_search_halfwidth,
self.r_inner_expected + self.inner_search_halfwidth,
]
}
}
fn default_min_theta_consistency() -> f32 {
0.25
}