diff --git a/crates/ransac/src/lib.rs b/crates/ransac/src/lib.rs index 9ab03fc29b..471916f473 100644 --- a/crates/ransac/src/lib.rs +++ b/crates/ransac/src/lib.rs @@ -11,10 +11,8 @@ use ordered_float::NotNan; use rand::{seq::SliceRandom, Rng}; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub enum RansacFeature { - #[default] - None, Line(Line2), TwoLines(TwoLines), } @@ -51,7 +49,6 @@ impl RansacFeature { impl Distance> for RansacFeature { fn squared_distance_to(&self, point: Point2) -> f32 { match self { - RansacFeature::None => f32::INFINITY, RansacFeature::Line(line) => line.squared_distance_to(point), RansacFeature::TwoLines(two_lines) => two_lines.squared_distance_to(point), } @@ -65,7 +62,6 @@ impl IntoIterator for RansacResult { fn into_iter(self) -> Self::IntoIter { match self.feature { - RansacFeature::None => Vec::new(), RansacFeature::Line(line) => { RansacLineSegment::try_from_used_points(line, self.used_points) .map(|line_segment| vec![line_segment]) @@ -140,7 +136,7 @@ impl RansacLineSegment { } } -#[derive(Default, Debug, PartialEq)] +#[derive(Debug, PartialEq)] pub struct RansacResult { pub feature: RansacFeature, pub used_points: Vec>, @@ -164,21 +160,18 @@ impl Ransac { fit_two_lines: bool, maximum_score_distance: f32, maximum_inclusion_distance: f32, - ) -> RansacResult { + ) -> Option> { if self.unused_points.len() < 2 { - return RansacResult { - feature: RansacFeature::None, - used_points: vec![], - }; + return None; } if self.unused_points.len() == 2 { - return RansacResult { + return Some(RansacResult { feature: RansacFeature::Line(Line::from_points( self.unused_points[0], self.unused_points[1], )), used_points: self.unused_points.clone(), - }; + }); } let maximum_score_distance_squared = maximum_score_distance * maximum_score_distance; @@ -224,10 +217,10 @@ impl Ransac { }); self.unused_points = unused_points; - RansacResult { + Some(RansacResult { feature: best_feature, used_points, - } + }) } } @@ -247,20 +240,14 @@ mod test { fn ransac_empty_input() { let mut ransac = Ransac::::new(vec![]); let mut rng = ChaChaRng::from_entropy(); - assert_eq!( - ransac.next_feature(&mut rng, 10, false, 5.0, 5.0), - RansacResult::default() - ); + assert_eq!(ransac.next_feature(&mut rng, 10, false, 5.0, 5.0), None); } #[test] fn ransac_single_point() { let mut ransac = Ransac::::new(vec![]); let mut rng = ChaChaRng::from_entropy(); - assert_eq!( - ransac.next_feature(&mut rng, 10, false, 5.0, 5.0), - RansacResult::default() - ); + assert_eq!(ransac.next_feature(&mut rng, 10, false, 5.0, 5.0), None); } #[test] @@ -272,7 +259,9 @@ mod test { let RansacResult { feature, used_points, - } = ransac.next_feature(&mut rng, 10, false, 5.0, 5.0); + } = ransac + .next_feature(&mut rng, 10, false, 5.0, 5.0) + .expect("expected a feature"); println!("{feature:#?}"); println!("{used_points:#?}"); @@ -298,7 +287,9 @@ mod test { let mut ransac = Ransac::::new(points.clone()); let mut rng = ChaChaRng::from_entropy(); - let result = ransac.next_feature(&mut rng, 15, false, 1.0, 1.0); + let result = ransac + .next_feature(&mut rng, 15, false, 1.0, 1.0) + .expect("expected feature"); if let RansacFeature::Line(line) = result.feature { assert_relative_eq!(line.slope(), slope, epsilon = 0.0001); diff --git a/crates/vision/src/field_border_detection.rs b/crates/vision/src/field_border_detection.rs index fa0fb5278e..35a3dd6c20 100644 --- a/crates/vision/src/field_border_detection.rs +++ b/crates/vision/src/field_border_detection.rs @@ -110,13 +110,15 @@ fn find_border_lines( second_line_association_distance: f32, ) -> Vec> { // first line - let result = ransac.next_feature( + let Some(result) = ransac.next_feature( random_state, 20, false, first_line_association_distance, first_line_association_distance, - ); + ) else { + return Vec::new(); + }; if !matches!(result.feature, RansacFeature::Line(_)) || result.used_points.len() < min_points_per_line { @@ -124,13 +126,15 @@ fn find_border_lines( } let first_line = best_fit_line(&result.used_points); // second line - let result = ransac.next_feature( + let Some(result) = ransac.next_feature( random_state, 20, false, second_line_association_distance, second_line_association_distance, - ); + ) else { + return Vec::new(); + }; if !matches!(result.feature, RansacFeature::Line(_)) || result.used_points.len() < min_points_per_line { diff --git a/crates/vision/src/line_detection.rs b/crates/vision/src/line_detection.rs index 07f4939bd3..4fe52930c8 100644 --- a/crates/vision/src/line_detection.rs +++ b/crates/vision/src/line_detection.rs @@ -171,13 +171,15 @@ impl LineDetection { if ransac.unused_points.len() < *context.minimum_number_of_points_on_line { break; } - let ransac_result = ransac.next_feature( + let Some(ransac_result) = ransac.next_feature( &mut self.random_state, *context.ransac_iterations, *context.ransac_fit_two_lines, *context.maximum_fit_distance_in_ground, *context.maximum_fit_distance_in_ground + *context.margin_for_point_inclusion, - ); + ) else { + break; + }; detected_features.push(ransac_result.feature.clone()); for line_segment in ransac_result { @@ -241,7 +243,6 @@ impl LineDetection { detected_features .into_iter() .map(|feature| match feature { - RansacFeature::None => RansacFeature::None, RansacFeature::Line(line) => RansacFeature::Line(Line::from_points( context.camera_matrix.ground_to_pixel(line.point).unwrap(), context diff --git a/tools/twix/src/panels/image/overlays/line_detection.rs b/tools/twix/src/panels/image/overlays/line_detection.rs index 2a420fff29..d6ebc4e0f5 100644 --- a/tools/twix/src/panels/image/overlays/line_detection.rs +++ b/tools/twix/src/panels/image/overlays/line_detection.rs @@ -58,7 +58,6 @@ impl Overlay for LineDetection { }; for feature in detected_features { match feature { - RansacFeature::None => {} RansacFeature::Line(line) => { painter.line(line.point, line.direction, Stroke::new(2.0, Color32::RED)) }