use crate::topology::chain::{find_next_pixel, reverse_dir};
use geo_types::Coord;
use std::time::Instant;
pub struct ContourExtractor;
impl ContourExtractor {
pub fn extract(edges: &[u8], width: u32, height: u32) -> Vec<Vec<Coord<f32>>> {
let start = Instant::now();
let w = width as usize;
let h = height as usize;
let mut visited = vec![false; w * h];
let mut contours: Vec<Vec<Coord<f32>>> = Vec::new();
for y in 1..(h - 1) {
for x in 1..(w - 1) {
let idx = y * w + x;
if edges[idx] == 0 || visited[idx] {
continue;
}
let contour = Self::trace_contour(edges, &mut visited, w, h, x, y);
if contour.len() >= 4 {
contours.push(contour);
}
}
}
log::info!(
"[Topology::ContourExtractor] - Extracted {} contours. Elapsed: {}ms",
contours.len(),
start.elapsed().as_millis()
);
contours
}
fn trace_contour(
edges: &[u8],
visited: &mut Vec<bool>,
width: usize,
height: usize,
start_x: usize,
start_y: usize,
) -> Vec<Coord<f32>> {
let mut contour: Vec<Coord<f32>> = Vec::new();
let start_coord = Coord {
x: start_x as f32,
y: start_y as f32,
};
contour.push(start_coord);
visited[start_y * width + start_x] = true;
let mut cx = start_x;
let mut cy = start_y;
let mut search_dir = 0usize;
loop {
let max_contour_len = width * height;
if contour.len() > max_contour_len {
log::debug!(
"[Topology::ContourExtractor] - Contour trace exceeded perimeter limit, aborting."
);
break;
}
match find_next_pixel(edges, width, height, cx, cy, search_dir) {
None => {
break;
}
Some((nx, ny, found_dir)) => {
if nx == start_x && ny == start_y {
contour.push(start_coord);
break;
}
let nidx = ny * width + nx;
if visited[nidx] {
contour.push(start_coord);
break;
}
contour.push(Coord {
x: nx as f32,
y: ny as f32,
});
visited[nidx] = true;
cx = nx;
cy = ny;
search_dir = (reverse_dir(found_dir) + 1) % 8;
}
}
if contour.len() > width * height {
log::warn!(
"[Topology::ContourExtractor] - Contour trace exceeded image size, aborting at ({}, {}).",
cx,
cy
);
break;
}
}
contour
}
}