1use std::cmp::Ordering;
5use std::collections::VecDeque;
6
7pub fn find_contours(
35 width: u32,
36 height: u32,
37 pixels: &[u32],
38 threshold: &u32,
39 order: Ordering,
40) -> Vec<Vec<[f32; 2]>> {
41 let width = width as usize;
42 let height = height as usize;
43 let padded_width = width + 2;
44 let padded_height = height + 2;
45
46 let at = |x: usize, y: usize| x + padded_width * y;
47
48 let mut image_values = vec![0i32; padded_height * padded_width];
49
50 for y in 0..height {
51 for x in 0..width {
52 let pixel = pixels[y * width + x];
53 image_values[at(x + 1, y + 1)] = if pixel.cmp(threshold) == order { 1 } else { 0 };
54 }
55 }
56
57 let mut diffs = VecDeque::from(vec![
58 [-1, 0], [-1, -1], [0, -1], [1, -1], [1, 0], [1, 1], [0, 1], [-1, 1], ]);
67
68 let mut contours: Vec<Contour> = Vec::new();
69 let mut curr_border_num = 1;
70 let mut parent_border_num = 1;
71
72 for y in 1..=height {
73 for x in 1..=width {
74 if image_values[at(x, y)] == 0 {
75 continue;
76 }
77
78 let curr = (x as i32, y as i32);
79
80 let (is_outer_border, adjacent_point) = if image_values[at(x, y)] == 1
81 && x > 0
82 && image_values[at(x - 1, y)] == 0
83 {
84 (true, (x as i32 - 1, y as i32))
85 } else if image_values[at(x, y)] > 0 && x + 1 < width && image_values[at(x + 1, y)] == 0
86 {
87 if image_values[at(x, y)] > 1 {
88 parent_border_num = image_values[at(x, y)] as usize;
89 }
90 (false, (x as i32 + 1, y as i32))
91 } else {
92 continue;
93 };
94
95 curr_border_num += 1;
96
97 let border_type = if is_outer_border {
98 BorderType::Outer
99 } else {
100 BorderType::Hole
101 };
102
103 let parent = if parent_border_num > 1 {
104 let parent_index = parent_border_num - 2;
105 let parent_contour = &contours[parent_index];
106 if (border_type == BorderType::Outer)
107 ^ (parent_contour.border_type == BorderType::Outer)
108 {
109 Some(parent_index)
110 } else {
111 parent_contour.parent
112 }
113 } else {
114 None
115 };
116
117 let mut contour_points: Vec<[f32; 2]> = Vec::new();
118 rotate_to_value(
119 &mut diffs,
120 [adjacent_point.0 - curr.0, adjacent_point.1 - curr.1],
121 );
122
123 let pos1_option = diffs.iter().find_map(|&diff| {
124 let nx = curr.0 + diff[0];
125 let ny = curr.1 + diff[1];
126 if nx >= 0
127 && nx < padded_width as i32
128 && ny >= 0
129 && ny < padded_height as i32
130 && image_values[at(nx as usize, ny as usize)] != 0
131 {
132 Some((nx, ny))
133 } else {
134 None
135 }
136 });
137
138 if let Some(pos1) = pos1_option {
139 let mut pos2 = pos1;
140 let mut pos3 = curr;
141
142 loop {
143 contour_points.push([pos3.0 as f32 - 1.0, pos3.1 as f32 - 1.0]);
144 rotate_to_value(&mut diffs, [pos2.0 - pos3.0, pos2.1 - pos3.1]);
145 let pos4 = diffs
146 .iter()
147 .rev()
148 .find_map(|&diff| {
149 let nx = pos3.0 + diff[0];
150 let ny = pos3.1 + diff[1];
151 if nx >= 0
152 && nx < padded_width as i32
153 && ny >= 0
154 && ny < padded_height as i32
155 && image_values[at(nx as usize, ny as usize)] != 0
156 {
157 Some((nx, ny))
158 } else {
159 None
160 }
161 })
162 .unwrap();
163
164 let mut is_right_edge = false;
165 for &diff in diffs.iter().rev() {
166 if diff == [pos4.0 - pos3.0, pos4.1 - pos3.1] {
167 break;
168 }
169 if diff == [1, 0] {
170 is_right_edge = true;
171 break;
172 }
173 }
174
175 if pos3.0 as usize + 1 == padded_width || is_right_edge {
176 image_values[at(pos3.0 as usize, pos3.1 as usize)] = -curr_border_num;
177 } else if image_values[at(pos3.0 as usize, pos3.1 as usize)] == 1 {
178 image_values[at(pos3.0 as usize, pos3.1 as usize)] = curr_border_num;
179 }
180
181 if pos4 == curr && pos3 == pos1 {
182 break;
183 }
184
185 pos2 = pos3;
186 pos3 = pos4;
187 }
188 } else {
189 contour_points.push([x as f32 - 1.0, y as f32 - 1.0]);
190 image_values[at(x, y)] = -curr_border_num;
191 }
192
193 contours.push(Contour::new(contour_points, border_type, parent));
194 }
195 }
196
197 contours
198 .into_iter()
199 .filter(|contour| contour.border_type() == &BorderType::Outer)
200 .map(|contour| contour.into_points())
201 .collect()
202}
203
204#[derive(Debug, Clone)]
206pub struct Contour {
207 points: Vec<[f32; 2]>,
208 border_type: BorderType,
209 parent: Option<usize>,
210}
211
212#[derive(Debug, Copy, Clone, PartialEq, Eq)]
213pub enum BorderType {
214 Outer,
215 Hole,
216}
217
218impl Contour {
219 pub fn new(points: Vec<[f32; 2]>, border_type: BorderType, parent: Option<usize>) -> Self {
220 Contour {
221 points,
222 border_type,
223 parent,
224 }
225 }
226
227 pub fn as_points(&self) -> &Vec<[f32; 2]> {
228 &self.points
229 }
230
231 pub fn into_points(self) -> Vec<[f32; 2]> {
232 self.points
233 }
234
235 pub fn border_type(&self) -> &BorderType {
236 &self.border_type
237 }
238
239 pub fn parent(&self) -> Option<usize> {
240 self.parent
241 }
242}
243
244fn rotate_to_value(values: &mut VecDeque<[i32; 2]>, value: [i32; 2]) {
245 if let Some(pos) = values.iter().position(|&v| v == value) {
246 values.rotate_left(pos);
247 }
248}
249
250pub fn find_labeled_contours(
273 width: u32,
274 height: u32,
275 pixels: &[u32],
276 labels: &Vec<u32>,
277) -> (Vec<u32>, Vec<Vec<[f32; 2]>>) {
278 let mut contours = Vec::with_capacity(labels.len());
279 let mut retained = Vec::with_capacity(labels.len());
280
281 for label in labels {
282 let contour = find_contours(width, height, pixels, label, Ordering::Equal)
283 .into_iter()
284 .max_by_key(|contour| contour.len());
285
286 if let Some(contour) = contour && contour.len() > 2 {
287 contours.push(contour);
288 retained.push(*label)
289 }
290 }
291
292 (retained, contours)
293}
294
295#[cfg(test)]
296mod test {
297
298 use super::*;
299
300 fn four_regions_small() -> (u32, u32, [u32; 9]) {
301 let mut buffer = [0u32; 9];
302
303 buffer[0] = 1u32;
304 buffer[2] = 1u32;
305 buffer[6] = 1u32;
306 buffer[8] = 1u32;
307
308 (3, 3, buffer)
309 }
310
311 fn four_regions_big() -> (u32, u32, [u32; 25]) {
312 let mut buffer = [0u32; 25];
313
314 buffer[0] = 1u32;
315 buffer[1] = 1u32;
316 buffer[5] = 1u32;
317 buffer[6] = 1u32;
318
319 buffer[3] = 1u32;
320 buffer[4] = 1u32;
321 buffer[8] = 1u32;
322 buffer[9] = 1u32;
323
324 buffer[15] = 2u32;
325 buffer[16] = 2u32;
326 buffer[20] = 2u32;
327 buffer[21] = 2u32;
328
329 buffer[18] = 3u32;
330 buffer[19] = 3u32;
331 buffer[23] = 3u32;
332 buffer[24] = 3u32;
333
334 (5, 5, buffer)
335 }
336
337 fn three_regions() -> (u32, u32, [u32; 9]) {
338 let mut buffer = [0u32; 9];
339
340 buffer[0] = 1u32;
341 buffer[2] = 1u32;
342 buffer[6] = 1u32;
343 buffer[7] = 1u32;
344 buffer[8] = 1u32;
345
346 (3, 3, buffer)
347 }
348
349 fn two_squares() -> (u32, u32, [u32; 100]) {
350 let mut buffer = [0u32; 100];
351
352 for i in 0..10 {
353 for j in 0..10 {
354 let idx = j * 10 + i;
355 if i < 4 && j < 4 {
356 buffer[idx] = 1u32;
357 } else if i >= 6 && j >= 6 {
358 buffer[idx] = 2u32;
359 } else {
360 buffer[idx] = 0u32;
361 }
362 }
363 }
364
365 (10, 10, buffer)
366 }
367
368 fn two_squares_touching() -> (u32, u32, [u32; 100]) {
369 let mut buffer = [0u32; 100];
370
371 for i in 0..10 {
372 for j in 0..10 {
373 let idx = j * 10 + i;
374 if i < 5 {
375 buffer[idx] = 1u32;
376 } else if i >= 5 {
377 buffer[idx] = 2u32;
378 } else {
379 buffer[idx] = 0u32;
380 }
381 }
382 }
383
384 (10, 10, buffer)
385 }
386
387 #[test]
388 fn test_four_regions_small() {
389 let (w, h, buffer) = four_regions_small();
390 let contours = find_contours(w, h, &buffer, &0, Ordering::Greater);
391
392 let n_contours = contours.len();
393 assert_eq!(n_contours, 4);
394
395 let p0 = &contours[0];
396 let p1 = &contours[1];
397 let p2 = &contours[2];
398 let p3 = &contours[3];
399
400 assert_eq!(*p0, vec![[0., 0.]]);
401 assert_eq!(*p1, vec![[2., 0.]]);
402 assert_eq!(*p2, vec![[0., 2.]]);
403 assert_eq!(*p3, vec![[2., 2.]]);
404 }
405
406 #[test]
407 fn test_four_regions_big() {
408 let (w, h, buffer) = four_regions_big();
409 let contours = find_contours(w, h, &buffer, &0, Ordering::Greater);
410
411 let n_contours = contours.len();
412 assert_eq!(n_contours, 4);
413
414 let p0 = &contours[0];
415 let p1 = &contours[1];
416 let p2 = &contours[2];
417 let p3 = &contours[3];
418
419 assert_eq!(*p0, vec![[0., 0.], [0., 1.], [1., 1.], [1., 0.]]);
420 assert_eq!(*p1, vec![[3., 0.], [3., 1.], [4., 1.], [4., 0.]]);
421 assert_eq!(*p2, vec![[0., 3.], [0., 4.], [1., 4.], [1., 3.]]);
422 assert_eq!(*p3, vec![[3., 3.], [3., 4.], [4., 4.], [4., 3.]]);
423 }
424
425 #[test]
426 fn test_three_regions() {
427 let (w, h, buffer) = three_regions();
428 let contours = find_contours(w, h, &buffer, &0, Ordering::Greater);
429
430 let n_contours = contours.len();
431 assert_eq!(n_contours, 3);
432
433 let p0 = &contours[0];
434 let p1 = &contours[1];
435 let p2 = &contours[2];
436
437 assert_eq!(*p0, vec![[0., 0.]]);
438 assert_eq!(*p1, vec![[2., 0.]]);
439 assert_eq!(*p2, vec![[0., 2.], [1., 2.], [2., 2.], [1., 2.]]);
440 }
441
442 #[test]
443 fn test_two_squares() {
444 let (w, h, buffer) = two_squares();
445 let contours = find_contours(w, h, &buffer, &0, Ordering::Greater);
446
447 let n_contours = contours.len();
448 assert_eq!(n_contours, 2);
449
450 let p0 = &contours[0];
451 let p1 = &contours[1];
452
453 assert_eq!(
454 *p0,
455 vec![
456 [0., 0.],
457 [0., 1.],
458 [0., 2.],
459 [0., 3.],
460 [1., 3.],
461 [2., 3.],
462 [3., 3.],
463 [3., 2.],
464 [3., 1.],
465 [3., 0.],
466 [2., 0.],
467 [1., 0.],
468 ]
469 );
470
471 assert_eq!(
472 *p1,
473 vec![
474 [6., 6.],
475 [6., 7.],
476 [6., 8.],
477 [6., 9.],
478 [7., 9.],
479 [8., 9.],
480 [9., 9.],
481 [9., 8.],
482 [9., 7.],
483 [9., 6.],
484 [8., 6.],
485 [7., 6.],
486 ]
487 );
488 }
489
490 #[test]
491 fn test_two_squares_touching() {
492 let (w, h, buffer) = two_squares_touching();
493 let contours = find_contours(w, h, &buffer, &0, Ordering::Greater);
494
495 assert_eq!(contours.len(), 1);
496
497 let (_, contours) = find_labeled_contours(w, h, &buffer, &vec![1u32, 2u32]);
498
499 let p0 = &contours[0];
500 let p1 = &contours[1];
501
502 assert_eq!(
503 *p0,
504 vec![
505 [0., 0.],
506 [0., 1.],
507 [0., 2.],
508 [0., 3.],
509 [0., 4.],
510 [0., 5.],
511 [0., 6.],
512 [0., 7.],
513 [0., 8.],
514 [0., 9.],
515 [1., 9.],
516 [2., 9.],
517 [3., 9.],
518 [4., 9.],
519 [4., 8.],
520 [4., 7.],
521 [4., 6.],
522 [4., 5.],
523 [4., 4.],
524 [4., 3.],
525 [4., 2.],
526 [4., 1.],
527 [4., 0.],
528 [3., 0.],
529 [2., 0.],
530 [1., 0.]
531 ]
532 );
533
534 assert_eq!(
535 *p1,
536 vec![
537 [5., 0.],
538 [5., 1.],
539 [5., 2.],
540 [5., 3.],
541 [5., 4.],
542 [5., 5.],
543 [5., 6.],
544 [5., 7.],
545 [5., 8.],
546 [5., 9.],
547 [6., 9.],
548 [7., 9.],
549 [8., 9.],
550 [9., 9.],
551 [9., 8.],
552 [9., 7.],
553 [9., 6.],
554 [9., 5.],
555 [9., 4.],
556 [9., 3.],
557 [9., 2.],
558 [9., 1.],
559 [9., 0.],
560 [8., 0.],
561 [7., 0.],
562 [6., 0.],
563 ]
564 );
565 }
566}