Skip to content

Commit de0a1d2

Browse files
committed
Add panel for displaying ransac features
1 parent bd9d391 commit de0a1d2

File tree

10 files changed

+91
-6
lines changed

10 files changed

+91
-6
lines changed

Cargo.lock

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/hulk/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ object_detection = { workspace = true }
2828
parameters = { workspace = true }
2929
path_serde = { workspace = true }
3030
projection = { workspace = true }
31+
ransac = { workspace = true }
3132
serde = { workspace = true }
3233
serde_json = { workspace = true }
3334
spl_network = { workspace = true }

crates/hulk_imagine/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ geometry = { workspace = true }
1717
hardware = { workspace = true }
1818
ittapi = { workspace = true }
1919
linear_algebra = { workspace = true }
20+
path_serde = { workspace = true }
2021
parameters = { workspace = true }
2122
projection = { workspace = true }
23+
ransac = { workspace = true }
2224
serde = { workspace = true }
23-
path_serde = { workspace = true }
2425
tokio = { workspace = true }
2526
types = { workspace = true }
2627
vision = { workspace = true }

crates/hulk_replayer/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ object_detection = { workspace = true }
3131
parameters = { workspace = true }
3232
path_serde = { workspace = true }
3333
projection = { workspace = true }
34+
ransac = { workspace = true }
3435
serde = { workspace = true }
3536
serde_json = { workspace = true }
3637
spl_network = { workspace = true }

crates/ransac/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ nalgebra = { workspace = true }
1313
ordered-float = { workspace = true }
1414
rand = { workspace = true }
1515
rand_chacha = { workspace = true }
16+
serde = { workspace = true }

crates/ransac/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ use geometry::{
99
use linear_algebra::{Point2, Rotation2};
1010
use ordered_float::NotNan;
1111
use rand::{seq::SliceRandom, Rng};
12+
use serde::{Deserialize, Serialize};
1213

13-
#[derive(Default, Debug, PartialEq)]
14+
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
1415
pub enum RansacFeature<Frame> {
1516
#[default]
1617
None,

crates/vision/src/line_detection.rs

+41-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ mod segment_merger;
66
use std::{collections::HashSet, ops::Range};
77

88
use color_eyre::Result;
9-
use geometry::line_segment::LineSegment;
9+
use geometry::{line::Line, line_segment::LineSegment, two_lines::TwoLines};
1010
use itertools::Itertools;
1111
use rand::SeedableRng;
1212
use rand_chacha::ChaChaRng;
@@ -17,7 +17,7 @@ use coordinate_systems::{Ground, Pixel};
1717
use framework::{AdditionalOutput, MainOutput};
1818
use linear_algebra::{distance, Point2};
1919
use projection::{camera_matrix::CameraMatrix, Projection};
20-
use ransac::{Ransac, RansacLineSegment};
20+
use ransac::{Ransac, RansacFeature, RansacLineSegment};
2121
use types::{
2222
filtered_segments::FilteredSegments,
2323
image_segments::GenericSegment,
@@ -44,6 +44,7 @@ pub struct CycleContext {
4444
AdditionalOutput<Vec<(LineSegment<Pixel>, LineDiscardReason)>, "discarded_lines">,
4545
filtered_segments_output:
4646
AdditionalOutput<Vec<GenericSegment>, "line_detection.filtered_segments">,
47+
detected_features: AdditionalOutput<Vec<RansacFeature<Pixel>>, "detected_features">,
4748

4849
use_horizontal_segments:
4950
Parameter<bool, "line_detection.$cycler_instance.use_horizontal_segments">,
@@ -160,21 +161,23 @@ impl LineDetection {
160161
.unzip();
161162

162163
let mut ransac = Ransac::new(line_points);
164+
let mut detected_features = Vec::new();
163165
let mut line_segments = Vec::new();
164166
let mut discarded_line_segments = Vec::new();
165167

166168
for _ in 0..*context.maximum_number_of_features {
167169
if ransac.unused_points.len() < *context.minimum_number_of_points_on_line {
168170
break;
169171
}
170-
let ransac_feature = ransac.next_feature(
172+
let ransac_result = ransac.next_feature(
171173
&mut self.random_state,
172174
*context.ransac_iterations,
173175
*context.maximum_fit_distance_in_ground,
174176
*context.maximum_fit_distance_in_ground + *context.margin_for_point_inclusion,
175177
);
178+
detected_features.push(ransac_result.feature.clone());
176179

177-
for line_segment in ransac_feature {
180+
for line_segment in ransac_result {
178181
let RansacLineSegment {
179182
line_segment,
180183
sorted_used_points,
@@ -231,6 +234,40 @@ impl LineDetection {
231234
}
232235
}
233236

237+
context.detected_features.fill_if_subscribed(|| {
238+
detected_features
239+
.into_iter()
240+
.map(|feature| match feature {
241+
RansacFeature::None => RansacFeature::None,
242+
RansacFeature::Line(line) => RansacFeature::Line(Line::from_points(
243+
context.camera_matrix.ground_to_pixel(line.point).unwrap(),
244+
context
245+
.camera_matrix
246+
.ground_to_pixel(line.point + line.direction)
247+
.unwrap(),
248+
)),
249+
RansacFeature::TwoLines(two_lines) => {
250+
let point = context
251+
.camera_matrix
252+
.ground_to_pixel(two_lines.point)
253+
.unwrap();
254+
RansacFeature::TwoLines(TwoLines {
255+
point,
256+
direction1: context
257+
.camera_matrix
258+
.ground_to_pixel(two_lines.point + two_lines.direction1.normalize())
259+
.unwrap_or(point)
260+
- point,
261+
direction2: context
262+
.camera_matrix
263+
.ground_to_pixel(two_lines.point + two_lines.direction2.normalize())
264+
.unwrap_or(point)
265+
- point,
266+
})
267+
}
268+
})
269+
.collect()
270+
});
234271
context.lines_in_image.fill_if_subscribed(|| {
235272
line_segments
236273
.iter()

tools/twix/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ linear_algebra = { workspace = true }
3434
log = { workspace = true }
3535
mlua = { workspace = true }
3636
nalgebra = { workspace = true }
37+
ordered-float = { workspace = true }
3738
parameters = { workspace = true }
3839
projection = { workspace = true }
40+
ransac = { workspace = true }
3941
repository = { workspace = true }
4042
serde = { workspace = true }
4143
serde_json = { workspace = true }

tools/twix/src/panels/image/overlays/line_detection.rs

+28
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use eframe::epaint::{Color32, Stroke};
33

44
use coordinate_systems::Pixel;
55
use geometry::line_segment::LineSegment;
6+
use ransac::RansacFeature;
67
use types::{image_segments::GenericSegment, line_data::LineDiscardReason};
78

89
use crate::{
@@ -14,6 +15,7 @@ use crate::{
1415
type DiscardedLines = Vec<(LineSegment<Pixel>, LineDiscardReason)>;
1516

1617
pub struct LineDetection {
18+
detected_features: BufferHandle<Option<Vec<RansacFeature<Pixel>>>>,
1719
lines_in_image: BufferHandle<Option<Vec<LineSegment<Pixel>>>>,
1820
discarded_lines: BufferHandle<Option<DiscardedLines>>,
1921
filtered_segments: BufferHandle<Option<Vec<GenericSegment>>>,
@@ -25,6 +27,9 @@ impl Overlay for LineDetection {
2527
fn new(nao: std::sync::Arc<crate::nao::Nao>, selected_cycler: VisionCycler) -> Self {
2628
let cycler_path = selected_cycler.as_path();
2729
Self {
30+
detected_features: nao.subscribe_value(format!(
31+
"{cycler_path}.additional_outputs.detected_features"
32+
)),
2833
lines_in_image: nao
2934
.subscribe_value(format!("{cycler_path}.additional_outputs.lines_in_image")),
3035
discarded_lines: nao
@@ -36,6 +41,9 @@ impl Overlay for LineDetection {
3641
}
3742

3843
fn paint(&self, painter: &TwixPainter<Pixel>) -> Result<()> {
44+
let Some(detected_features) = self.detected_features.get_last_value()?.flatten() else {
45+
return Ok(());
46+
};
3947
let Some(lines_in_image) = self.lines_in_image.get_last_value()?.flatten() else {
4048
return Ok(());
4149
};
@@ -45,6 +53,26 @@ impl Overlay for LineDetection {
4553
let Some(filtered_segments) = self.filtered_segments.get_last_value()?.flatten() else {
4654
return Ok(());
4755
};
56+
for feature in detected_features {
57+
match feature {
58+
RansacFeature::None => {}
59+
RansacFeature::Line(line) => {
60+
painter.line(line.point, line.direction, Stroke::new(2.0, Color32::RED))
61+
}
62+
RansacFeature::TwoLines(two_lines) => {
63+
painter.line(
64+
two_lines.point,
65+
two_lines.direction1,
66+
Stroke::new(2.0, Color32::GREEN),
67+
);
68+
painter.line(
69+
two_lines.point,
70+
two_lines.direction2,
71+
Stroke::new(2.0, Color32::GREEN),
72+
);
73+
}
74+
};
75+
}
4876
for segment in filtered_segments {
4977
let stroke = Stroke::new(1.0, Color32::RED);
5078
painter.line_segment(segment.start.cast(), segment.end.cast(), stroke);

tools/twix/src/twix_painter.rs

+7
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,13 @@ impl<World> TwixPainter<World> {
286286
self.painter.line_segment([start, end], stroke);
287287
}
288288

289+
pub fn line(&self, point: Point2<World>, direction: Vector2<World>, stroke: Stroke) {
290+
let start = self.transform_world_to_pixel(point - direction * 10000.0);
291+
let end = self.transform_world_to_pixel(point + direction * 10000.0);
292+
let stroke = self.transform_stroke(stroke);
293+
self.painter.line_segment([start, end], stroke);
294+
}
295+
289296
pub fn rect_filled(&self, min: Point2<World>, max: Point2<World>, fill_color: Color32) {
290297
let right_bottom = point![max.x(), min.y()];
291298
let left_top = point![min.x(), max.y()];

0 commit comments

Comments
 (0)