Fix inner/outer detection in contour tracing
This commit is contained in:
1 parent
f25e9a00a8
commit
f68fa6f0b7
2 files changed
+182
-31
No files matched your search
+21
-13
@@ -1,30 +1,38 @@
|
||||
mod common;
|
||||
|
||||
use ccvl::kernel::Kernel;
|
||||
use ccvl::*;
|
||||
|
||||
fn draw(contour: &contour::Contour, img: &mut image::Image<image::SRGBA>) {
|
||||
let n = contour.vertices.len();
|
||||
fn draw(
|
||||
img: &mut image::Image<image::SRGBA>,
|
||||
path: &Vec<(usize, usize)>,
|
||||
colour: image::SRGBA,
|
||||
) {
|
||||
let n = path.len();
|
||||
for i in 0..n {
|
||||
let (x0, y0) = contour.vertices[i];
|
||||
let (x1, y1) = contour.vertices[(i + 1) % n];
|
||||
let colour = match contour.kind {
|
||||
contour::Kind::Outer => colours::GREEN,
|
||||
contour::Kind::Inner => colours::BLUE,
|
||||
};
|
||||
let (x0, y0) = path[i];
|
||||
let (x1, y1) = path[(i + 1) % n];
|
||||
draw::line(x0, y0, x1, y1, img, colour);
|
||||
}
|
||||
}
|
||||
|
||||
fn approx(a: f32, b: f32, epsilon: f32) -> bool {
|
||||
(a - b).abs() <= epsilon
|
||||
}
|
||||
|
||||
fn main() -> std::process::ExitCode {
|
||||
common::process_image(|mut img| {
|
||||
let greyscale = img.map(|p| image::Luminance::from(p));
|
||||
let thresholded = filter::threshold_auto(greyscale);
|
||||
|
||||
let contours = contour::find(thresholded);
|
||||
let blurred = kernel::gaussian::<5>(1.2).convolve_sat(&greyscale);
|
||||
let thresholded = filter::threshold_auto(blurred);
|
||||
let contours =
|
||||
contour::find(&thresholded, image::Luminance { y: 0.0 });
|
||||
for contour in contours {
|
||||
draw(&contour, &mut img);
|
||||
draw(&mut img, &contour.vertices, match contour.kind {
|
||||
contour::Kind::Outer => colours::GREEN,
|
||||
contour::Kind::Inner => colours::BLUE,
|
||||
});
|
||||
}
|
||||
|
||||
img
|
||||
})
|
||||
}
|
||||
+161
-18
@@ -45,12 +45,15 @@ trait Coordinate {
|
||||
fn set<P>(&self, img: &mut image::Image<P>, value: P);
|
||||
}
|
||||
|
||||
pub fn find(img: image::Image<image::Luminance>) -> Vec<Contour> {
|
||||
pub fn find<P>(img: &image::Image<P>, fg: P) -> Vec<Contour>
|
||||
where
|
||||
P: PartialEq + Copy + Send + Sync,
|
||||
{
|
||||
let mut map = img.map(|p| {
|
||||
if p.y == 1.0 {
|
||||
Pixel::Background
|
||||
} else {
|
||||
if p == fg {
|
||||
Pixel::Foreground
|
||||
} else {
|
||||
Pixel::Background
|
||||
}
|
||||
});
|
||||
|
||||
@@ -83,7 +86,8 @@ fn add(
|
||||
map: &mut image::Image<Pixel>,
|
||||
) {
|
||||
let label = contours.len();
|
||||
let vertices = trace(map, start, kind.start_dir(), label);
|
||||
let dir = kind.find_start_dir(start, map);
|
||||
let vertices = trace(map, start, dir, label);
|
||||
contours.push(Contour::new(parent, kind, vertices));
|
||||
if let Some(index) = parent {
|
||||
contours[index].add_child(label);
|
||||
@@ -98,9 +102,6 @@ fn trace(
|
||||
) -> Vec<(usize, usize)> {
|
||||
let mut q = start_q;
|
||||
let mut dir = start_dir;
|
||||
while background(img, dir.from(q)) {
|
||||
dir.next();
|
||||
}
|
||||
|
||||
let mut vertices = Vec::new();
|
||||
loop {
|
||||
@@ -124,20 +125,17 @@ fn trace(
|
||||
dir = dir.next();
|
||||
};
|
||||
|
||||
if q == start_q || !q.in_bounds(img.extent) {
|
||||
if dir != start_dir {
|
||||
vertices.push(q);
|
||||
}
|
||||
if q == start_q && dir == start_dir || !q.in_bounds(img.extent) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// println!();
|
||||
// for y in 0..img.extent.y {
|
||||
// print!(" ");
|
||||
// for x in 0..img.extent.x {
|
||||
// if let Some(label) = traced_up(img, (x, y)) {
|
||||
// print!("{:1}", label);
|
||||
// } else if let Some(label) = traced_down(img, (x, y)) {
|
||||
// } else if let Some(_) = traced_down(img, (x, y)) {
|
||||
// print!("-")
|
||||
// } else if foreground(img, (x, y)) {
|
||||
// print!(".");
|
||||
@@ -183,11 +181,19 @@ fn traced_down(
|
||||
}
|
||||
|
||||
impl Kind {
|
||||
fn start_dir(self) -> Dir {
|
||||
match self {
|
||||
Kind::Outer => Dir::E,
|
||||
Kind::Inner => Dir::W,
|
||||
fn find_start_dir(
|
||||
self,
|
||||
q: (usize, usize),
|
||||
img: &image::Image<Pixel>,
|
||||
) -> Dir {
|
||||
let mut dir = match self {
|
||||
Kind::Outer => Dir::W,
|
||||
Kind::Inner => Dir::E,
|
||||
};
|
||||
while background(img, dir.from(q)) {
|
||||
dir = dir.prev()
|
||||
}
|
||||
dir.flip()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +211,19 @@ impl Dir {
|
||||
}
|
||||
}
|
||||
|
||||
fn prev(self) -> Self {
|
||||
match self {
|
||||
Self::N => Self::NW,
|
||||
Self::NE => Self::N,
|
||||
Self::E => Self::NE,
|
||||
Self::SE => Self::E,
|
||||
Self::S => Self::SE,
|
||||
Self::SW => Self::S,
|
||||
Self::W => Self::SW,
|
||||
Self::NW => Self::W,
|
||||
}
|
||||
}
|
||||
|
||||
fn flip(self) -> Self {
|
||||
match self {
|
||||
Self::N => Self::S,
|
||||
@@ -292,3 +311,127 @@ impl Default for Pixel {
|
||||
Self::Background
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[rustfmt::skip]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn simple_block() {
|
||||
let img = image::Image::<usize> {
|
||||
extent: image::Extent { x: 4, y: 4 },
|
||||
pixels: vec![
|
||||
0, 0, 0, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 1, 1, 0,
|
||||
0, 0, 0, 0,
|
||||
]
|
||||
};
|
||||
|
||||
let contours = find(&img, 1);
|
||||
assert_eq!(contours.len(), 1);
|
||||
|
||||
let contour = &contours[0];
|
||||
assert_eq!(contour.kind, Kind::Outer);
|
||||
assert_eq!(contour.vertices, vec![
|
||||
(1, 1), (2, 1), (2, 2), (1, 2)
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_block_with_hole() {
|
||||
let img = image::Image::<usize> {
|
||||
extent: image::Extent { x: 8, y: 8 },
|
||||
pixels: vec![
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 1, 1, 0, 0, 1, 1, 0,
|
||||
0, 1, 1, 0, 0, 1, 1, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
]
|
||||
};
|
||||
|
||||
let contours = find(&img, 1);
|
||||
assert_eq!(contours.len(), 2);
|
||||
|
||||
let contour = &contours[0];
|
||||
assert_eq!(contour.parent, None);
|
||||
assert_eq!(contour.kind, Kind::Outer);
|
||||
assert_eq!(contour.vertices, vec![
|
||||
(1, 1), (6, 1), (6, 6), (1, 6)
|
||||
]);
|
||||
assert_eq!(contour.children, vec![ 1 ]);
|
||||
|
||||
let contour = &contours[1];
|
||||
assert_eq!(contour.parent, Some(0));
|
||||
assert_eq!(contour.kind, Kind::Inner);
|
||||
assert_eq!(contour.vertices, vec![
|
||||
(2, 3), (2, 4), (3, 5), (4, 5),
|
||||
(5, 4), (5, 3), (4, 2), (3, 2),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diagonally_connected_block_with_hole() {
|
||||
let img = image::Image::<usize> {
|
||||
extent: image::Extent { x: 8, y: 8 },
|
||||
pixels: vec![
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 0,
|
||||
0, 1, 1, 0, 0, 1, 1, 0,
|
||||
0, 1, 1, 0, 0, 1, 1, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
]
|
||||
};
|
||||
|
||||
let contours = find(&img, 1);
|
||||
assert_eq!(contours.len(), 2);
|
||||
|
||||
let contour = &contours[0];
|
||||
assert_eq!(contour.parent, None);
|
||||
assert_eq!(contour.kind, Kind::Outer);
|
||||
assert_eq!(contour.vertices, vec![
|
||||
(3, 1), (6, 1), (6, 6), (3, 6), (3, 5),
|
||||
(2, 4), (1, 4), (1, 3), (2, 3), (3, 2),
|
||||
]);
|
||||
assert_eq!(contour.children, vec![ 1 ]);
|
||||
|
||||
let contour = &contours[1];
|
||||
assert_eq!(contour.parent, Some(0));
|
||||
assert_eq!(contour.kind, Kind::Inner);
|
||||
assert_eq!(contour.vertices, vec![
|
||||
(2, 3), (2, 4), (3, 5), (4, 5),
|
||||
(5, 4), (5, 3), (4, 2), (3, 2),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn contour_passes_through_start() {
|
||||
let img = image::Image::<usize> {
|
||||
extent: image::Extent { x: 5, y: 5 },
|
||||
pixels: vec![
|
||||
0, 0, 0, 0, 0,
|
||||
0, 0, 1, 1, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0,
|
||||
]
|
||||
};
|
||||
|
||||
let contours = find(&img, 1);
|
||||
assert_eq!(contours.len(), 1);
|
||||
|
||||
let contour = &contours[0];
|
||||
assert_eq!(contour.kind, Kind::Outer);
|
||||
assert_eq!(contour.vertices, vec![
|
||||
(2, 1), (3, 1), (2, 1), (1, 2), (1, 3), (1, 2)
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user