Skip to content

Commit 36e49fd

Browse files
gfgafntwistedfall
authored andcommitted
Refactor: Use state amchine
1 parent 819b33c commit 36e49fd

File tree

1 file changed

+143
-92
lines changed

1 file changed

+143
-92
lines changed

examples/create_mask.rs

Lines changed: 143 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
use opencv::{
55
core::{bitwise_and, find_file, CommandLineParser, Point, Scalar, CV_8UC1, CV_8UC3},
6-
highgui::{self, *},
6+
highgui::{self, imshow},
77
imgcodecs::{imread, IMREAD_COLOR},
88
imgproc,
99
prelude::*,
@@ -19,6 +19,16 @@ use std::{
1919

2020
const SOURCE_WINDOW: &str = "Source image";
2121

22+
#[derive(Debug, Clone, Copy)]
23+
enum DrawingState {
24+
Init,
25+
DrawingMarkerPoint,
26+
DrawingMarkerPointFinished,
27+
DrawingMask,
28+
DrawingMaskFinished,
29+
Resetting,
30+
}
31+
2232
fn main() {
2333
let args: Vec<String> = env::args().collect();
2434
let (argc, argv) = (args.len() as i32, args.iter().map(|s| s.as_str()).collect::<Vec<&str>>());
@@ -33,18 +43,23 @@ fn main() {
3343
);
3444

3545
// HACK: replace with parser.get<String>("@input")
36-
let input_image = "lena.jpg";
37-
let input_image_path = find_file(input_image, true, false).expect("Cannot find input image");
38-
39-
let [src, mut img1, mut mask, mut final_img]: [Mat; 4];
46+
let input_image = argv.into_iter().nth(2).unwrap_or("lena.jpg");
47+
let input_image_path = find_file(input_image, true, false)
48+
.map(|path| {
49+
println!("find input_image {} in : {}", input_image, path);
50+
path
51+
})
52+
.unwrap_or_else(|_| panic!("Cannot find input_image: {}", input_image));
53+
54+
let [src, mut next_frame, mut mask, mut final_img]: [Mat; 4];
4055
src = imread(&input_image_path, IMREAD_COLOR).unwrap();
4156
if src.empty() {
4257
eprintln!("Error opening image: {}", input_image);
4358
process::exit(-1);
4459
}
4560

46-
highgui::named_window(SOURCE_WINDOW, WINDOW_AUTOSIZE).unwrap();
47-
let mouse_event_data: (i32, i32, i32, i32) = Default::default();
61+
highgui::named_window(SOURCE_WINDOW, highgui::WINDOW_AUTOSIZE).unwrap();
62+
let mouse_event_data = (highgui::MouseEventTypes::EVENT_MOUSEWHEEL, 0, 0, 0);
4863
let (mouse_event_data, should_handle_mouse_event) = (Arc::new(Mutex::new(mouse_event_data)), Arc::new(AtomicBool::new(false)));
4964

5065
let mouse_event_dispatcher = {
@@ -54,7 +69,7 @@ fn main() {
5469
move |event: i32, x: i32, y: i32, flags: i32| {
5570
// can intercept specific mouse events here to don't update the mouse_data
5671
if let Ok(mut mouse_data) = mouse_data.lock() {
57-
*mouse_data = (event, x, y, flags);
72+
*mouse_data = (mouse_event_from_i32(event), x, y, flags);
5873
}
5974
should_handle_mouse_event.store(true, atomic::Ordering::Relaxed);
6075
}
@@ -63,10 +78,9 @@ fn main() {
6378

6479
highgui::imshow(SOURCE_WINDOW, &src).unwrap();
6580

66-
let (mut num_points_has_add, mut drag_left_button, mut right_button_down) = (0, false, false);
67-
let mut points = Vec::<Point>::new();
81+
let (mut marker_points, mut drawing_state) = (Vec::<Point>::new(), DrawingState::Init);
6882

69-
img1 = Mat::new_size_with_default(src.size().unwrap(), CV_8UC3, Scalar::new(0.0, 0.0, 0.0, 0.0)).unwrap();
83+
next_frame = Mat::zeros_size(src.size().unwrap(), CV_8UC3).unwrap().to_mat().unwrap();
7084

7185
loop {
7286
// Press Esc to exit
@@ -75,104 +89,141 @@ fn main() {
7589
}
7690

7791
let (mouse_event, x, y, _) = {
78-
if !should_handle_mouse_event.load(atomic::Ordering::Relaxed) {
92+
if should_handle_mouse_event.load(atomic::Ordering::Relaxed) {
7993
continue;
80-
}
81-
should_handle_mouse_event.store(false, atomic::Ordering::Relaxed);
82-
83-
if let Ok(mouse_event_data) = mouse_event_data.lock() {
84-
*mouse_event_data
8594
} else {
86-
continue;
87-
}
88-
};
95+
should_handle_mouse_event.store(false, atomic::Ordering::Relaxed);
8996

90-
match mouse_event {
91-
EVENT_LBUTTONDOWN => {
92-
dbg!((mouse_event, x, y));
93-
94-
if !drag_left_button && !right_button_down {
95-
if num_points_has_add == 0 {
96-
img1 = src.clone();
97-
}
98-
99-
let point = Point::new(x, y);
100-
imgproc::circle(&mut img1, point, 2, Scalar::new(0., 0., 255., 0.), -1, imgproc::LINE_8, 0).unwrap();
101-
points.push(point);
102-
num_points_has_add += 1;
103-
drag_left_button = true;
104-
105-
if num_points_has_add > 1 {
106-
imgproc::line(
107-
&mut img1,
108-
points[num_points_has_add - 2],
109-
point,
110-
Scalar::new(0., 0., 255., 0.),
111-
2,
112-
imgproc::LINE_8,
113-
0,
114-
)
115-
.unwrap();
116-
}
117-
118-
imshow(SOURCE_WINDOW, &img1).unwrap();
97+
if let Ok(mouse_event_data) = mouse_event_data.lock() {
98+
*mouse_event_data
99+
} else {
100+
continue;
119101
}
120102
}
121-
EVENT_LBUTTONUP => {
122-
dbg!((mouse_event, x, y));
123-
124-
if drag_left_button {
125-
imshow(SOURCE_WINDOW, &img1).unwrap();
126-
drag_left_button = false;
127-
}
128-
}
129-
EVENT_RBUTTONDOWN => {
130-
dbg!((mouse_event, x, y));
103+
};
131104

132-
right_button_down = true;
133-
let mut img1 = src.clone();
105+
drawing_state = self::state_transform(drawing_state, mouse_event);
134106

135-
if num_points_has_add > 0 {
136-
let pts_mat = Mat::from_slice(points.as_slice()).unwrap();
137-
imgproc::polylines(&mut img1, &pts_mat, true, Scalar::new(0., 0., 0., 0.), 2, imgproc::LINE_8, 0).unwrap();
107+
match drawing_state {
108+
DrawingState::Init | DrawingState::DrawingMarkerPointFinished => { /* do nothing */ }
109+
DrawingState::DrawingMarkerPoint => {
110+
if marker_points.is_empty() {
111+
next_frame = src.clone();
138112
}
139113

140-
imshow(SOURCE_WINDOW, &img1).unwrap();
141-
}
142-
EVENT_RBUTTONUP => {
143-
dbg!((mouse_event, x, y));
144-
145-
right_button_down = false;
146-
final_img = Mat::zeros_size(src.size().unwrap(), CV_8UC3).unwrap().to_mat().unwrap();
147-
148-
mask = Mat::zeros_size(src.size().unwrap(), CV_8UC1).unwrap().to_mat().unwrap();
149-
150-
let points_mat = Mat::from_slice(points.as_slice()).unwrap();
151-
imgproc::fill_poly(
152-
&mut mask,
153-
&points_mat,
154-
Scalar::new(255., 255., 255., 255.),
114+
let point = Point::new(x, y);
115+
imgproc::circle(
116+
&mut next_frame,
117+
point,
118+
2,
119+
Scalar::new(0., 0., 255., 0.),
120+
-1,
155121
imgproc::LINE_8,
156122
0,
157-
Point::default(),
158123
)
159124
.unwrap();
125+
marker_points.push(point);
126+
127+
if marker_points.len() > 1 {
128+
imgproc::line(
129+
&mut next_frame,
130+
marker_points[marker_points.len() - 2],
131+
point,
132+
Scalar::new(0., 0., 255., 0.),
133+
2,
134+
imgproc::LINE_8,
135+
0,
136+
)
137+
.unwrap();
138+
}
160139

161-
bitwise_and(&src, &src, &mut final_img, &mask).unwrap();
162-
163-
imshow("Mask", &mask).unwrap();
164-
imshow("Result", &final_img).unwrap();
165-
imshow(SOURCE_WINDOW, &img1).unwrap();
140+
imshow(SOURCE_WINDOW, &next_frame).unwrap();
141+
}
142+
DrawingState::DrawingMask => {
143+
if !marker_points.is_empty() {
144+
next_frame = src.clone();
145+
146+
let pts_mat = Mat::from_slice(marker_points.as_slice()).unwrap();
147+
imgproc::polylines(
148+
&mut next_frame,
149+
&pts_mat,
150+
true,
151+
Scalar::new(0., 0., 0., 0.),
152+
2,
153+
imgproc::LINE_8,
154+
0,
155+
)
156+
.unwrap();
157+
158+
imshow(SOURCE_WINDOW, &next_frame).unwrap();
159+
}
160+
}
161+
DrawingState::DrawingMaskFinished => {
162+
if !marker_points.is_empty() {
163+
final_img = Mat::zeros_size(src.size().unwrap(), CV_8UC3).unwrap().to_mat().unwrap();
164+
mask = Mat::zeros_size(src.size().unwrap(), CV_8UC1).unwrap().to_mat().unwrap();
165+
166+
imgproc::fill_poly(
167+
&mut mask,
168+
&Mat::from_slice(marker_points.as_slice()).unwrap(),
169+
Scalar::new(255., 255., 255., 255.),
170+
imgproc::LINE_8,
171+
0,
172+
Point::default(),
173+
)
174+
.unwrap();
175+
176+
bitwise_and(&src, &src, &mut final_img, &mask).unwrap();
177+
178+
imshow("Mask", &mask).unwrap();
179+
imshow("Result", &final_img).unwrap();
180+
imshow(SOURCE_WINDOW, &next_frame).unwrap();
181+
}
166182
}
167-
EVENT_MBUTTONDOWN => {
168-
dbg!((mouse_event, x, y));
183+
DrawingState::Resetting => {
184+
if !marker_points.is_empty() {
185+
marker_points.clear();
186+
next_frame = src.clone();
169187

170-
num_points_has_add = 0;
171-
points.clear();
172-
img1 = src.clone();
173-
imshow(SOURCE_WINDOW, &img1).unwrap();
188+
imshow(SOURCE_WINDOW, &next_frame).unwrap();
189+
}
174190
}
175-
_ => {}
191+
}
192+
}
193+
}
194+
195+
/// Converts an `i32` to a `opencv::highgui::MouseEventTypes`
196+
///
197+
/// # Panics
198+
///
199+
/// Panics if the argument less than 0 or greater than 11.
200+
fn mouse_event_from_i32(value: i32) -> opencv::highgui::MouseEventTypes {
201+
(value.gt(&(opencv::highgui::MouseEventTypes::EVENT_MOUSEHWHEEL as i32) /* 11 */)
202+
|| (value.lt(&(opencv::highgui::MouseEventTypes::EVENT_MOUSEMOVE as i32) /* 0 */)))
203+
.then(|| panic!("Invalid cv::highgui::MouseEventTypes value: {}", value));
204+
205+
// Safe because of the previous check
206+
unsafe { std::mem::transmute(value) }
207+
}
208+
209+
fn state_transform(drawing_state: DrawingState, mouse_event: highgui::MouseEventTypes) -> DrawingState {
210+
use self::DrawingState::*;
211+
use opencv::highgui::MouseEventTypes::*;
212+
213+
match (&drawing_state, mouse_event) {
214+
(Init, EVENT_LBUTTONDOWN) => DrawingMarkerPoint,
215+
(DrawingMarkerPoint, EVENT_LBUTTONUP) => DrawingMarkerPointFinished,
216+
(DrawingMarkerPointFinished, EVENT_LBUTTONDOWN) => DrawingMarkerPoint,
217+
(DrawingMarkerPointFinished, EVENT_RBUTTONDOWN) => DrawingMask,
218+
(DrawingMask, EVENT_RBUTTONUP) => DrawingMaskFinished,
219+
(Init | DrawingMarkerPointFinished | DrawingMaskFinished, EVENT_MBUTTONDOWN) => Resetting,
220+
(Resetting, EVENT_MBUTTONUP) => Init,
221+
_ => {
222+
println!(
223+
"Invalid state transition from {:?} with event {:?}",
224+
drawing_state, mouse_event
225+
);
226+
drawing_state
176227
}
177228
}
178229
}

0 commit comments

Comments
 (0)