ringgrid/detector/config/
fit.rs1fn default_max_angular_gap_rad() -> f64 {
7 std::f64::consts::FRAC_PI_2
8}
9
10#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
12#[serde(default)]
13#[non_exhaustive]
14pub struct InnerFitConfig {
15 pub min_points: usize,
17 pub min_inlier_ratio: f32,
19 pub max_rms_residual: f64,
21 pub max_center_shift_px: f64,
23 pub max_ratio_abs_error: f64,
25 pub local_peak_halfwidth_idx: usize,
27 pub ransac: crate::conic::RansacConfig,
29 #[serde(default = "InnerFitConfig::default_miss_confidence_factor")]
38 pub miss_confidence_factor: f32,
39 #[serde(default = "default_max_angular_gap_rad")]
44 pub max_angular_gap_rad: f64,
45 #[serde(default = "InnerFitConfig::default_require_inner_fit")]
50 pub require_inner_fit: bool,
51}
52
53impl InnerFitConfig {
54 fn default_miss_confidence_factor() -> f32 {
55 0.7
56 }
57 fn default_require_inner_fit() -> bool {
58 false
59 }
60}
61
62impl Default for InnerFitConfig {
63 fn default() -> Self {
64 Self {
65 min_points: 20,
66 min_inlier_ratio: 0.5,
67 max_rms_residual: 1.0,
68 max_center_shift_px: 12.0,
69 max_ratio_abs_error: 0.15,
70 local_peak_halfwidth_idx: 3,
71 ransac: crate::conic::RansacConfig {
72 max_iters: 200,
73 inlier_threshold: 1.5,
74 min_inliers: 8,
75 seed: 43,
76 },
77 miss_confidence_factor: 0.7,
78 max_angular_gap_rad: default_max_angular_gap_rad(),
79 require_inner_fit: false,
80 }
81 }
82}
83
84#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
86#[serde(default)]
87#[non_exhaustive]
88pub struct OuterFitConfig {
89 pub min_direct_fit_points: usize,
91 pub min_ransac_points: usize,
93 pub ransac: crate::conic::RansacConfig,
95 #[serde(default = "OuterFitConfig::default_size_score_weight")]
103 pub size_score_weight: f32,
104 #[serde(default = "default_max_angular_gap_rad")]
109 pub max_angular_gap_rad: f64,
110}
111
112impl OuterFitConfig {
113 fn default_size_score_weight() -> f32 {
114 0.15
115 }
116}
117
118impl Default for OuterFitConfig {
119 fn default() -> Self {
120 Self {
121 min_direct_fit_points: 6,
122 min_ransac_points: 8,
123 ransac: crate::conic::RansacConfig {
124 max_iters: 200,
125 inlier_threshold: 1.5,
126 min_inliers: 6,
127 seed: 42,
128 },
129 size_score_weight: Self::default_size_score_weight(),
130 max_angular_gap_rad: default_max_angular_gap_rad(),
131 }
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn inner_fit_config_defaults_are_stable() {
141 let core = InnerFitConfig::default();
142 assert_eq!(core.min_points, 20);
143 assert!((core.min_inlier_ratio - 0.5).abs() < 1e-6);
144 assert!((core.max_rms_residual - 1.0).abs() < 1e-9);
145 assert!((core.max_center_shift_px - 12.0).abs() < 1e-9);
146 assert!((core.max_ratio_abs_error - 0.15).abs() < 1e-9);
147 assert_eq!(core.local_peak_halfwidth_idx, 3);
148 assert_eq!(core.ransac.max_iters, 200);
149 assert!((core.ransac.inlier_threshold - 1.5).abs() < 1e-9);
150 assert_eq!(core.ransac.min_inliers, 8);
151 assert_eq!(core.ransac.seed, 43);
152 assert!((core.miss_confidence_factor - 0.7).abs() < 1e-6);
153 assert!(
154 (core.max_angular_gap_rad - std::f64::consts::FRAC_PI_2).abs() < 1e-9,
155 "inner max_angular_gap_rad"
156 );
157 assert!(!core.require_inner_fit);
158 }
159
160 #[test]
161 fn outer_fit_config_defaults_are_stable() {
162 let core = OuterFitConfig::default();
163 assert_eq!(core.min_direct_fit_points, 6);
164 assert_eq!(core.min_ransac_points, 8);
165 assert_eq!(core.ransac.max_iters, 200);
166 assert!((core.ransac.inlier_threshold - 1.5).abs() < 1e-9);
167 assert_eq!(core.ransac.min_inliers, 6);
168 assert_eq!(core.ransac.seed, 42);
169 assert!((core.size_score_weight - 0.15).abs() < 1e-6);
170 assert!(
171 (core.max_angular_gap_rad - std::f64::consts::FRAC_PI_2).abs() < 1e-9,
172 "outer max_angular_gap_rad"
173 );
174 }
175
176 #[test]
177 fn outer_fit_config_deserialize_missing_size_weight_uses_default() {
178 let json = r#"{
179 "min_direct_fit_points": 6,
180 "min_ransac_points": 8,
181 "ransac": {
182 "max_iters": 200,
183 "inlier_threshold": 1.5,
184 "min_inliers": 6,
185 "seed": 42
186 }
187 }"#;
188 let cfg: OuterFitConfig = serde_json::from_str(json).expect("deserialize outer fit config");
189 assert!((cfg.size_score_weight - 0.15).abs() < 1e-6);
190 }
191}