Skip to content

Commit

Permalink
flood fill algorithm and regression test
Browse files Browse the repository at this point in the history
  • Loading branch information
tkallady committed Sep 5, 2024
1 parent c153f57 commit d2a187c
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 0 deletions.
62 changes: 62 additions & 0 deletions src/drawing/fill.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use image::{GenericImage, Pixel};

/// Equivalent to bucket tool in MS-PAINT
/// Performs 4-way flood-fill based on this algorithm: https://en.wikipedia.org/wiki/Flood_fill#Span_filling
pub fn flood_fill<I>(image: &mut I, x: u32, y: u32, fill_with: I::Pixel)
where
I: GenericImage,
{
let target = image.get_pixel(x, y);

let mut stack = Vec::new();

stack.push((x as i32, x as i32, y as i32, 1 as i32));
stack.push((x as i32, x as i32, y as i32 - 1, -1 as i32));

while !stack.is_empty() {
let (x1, x2, y, dy) = stack.pop().unwrap();
let mut x1 = x1;
let mut x = x1;
if inside(image, x, y, target) {
while inside(image, x - 1, y, target) {
image.put_pixel(x as u32 - 1, y as u32, fill_with);
x = x - 1;
}
if x < x1 {
stack.push((x, x1 - 1, y - dy, -dy))
}
}
while x1 <= x2 {
while inside(image, x1, y, target) {
image.put_pixel(x1 as u32, y as u32, fill_with);
x1 = x1 + 1;
}
if x1 > x {
stack.push((x, x1 - 1, y + dy, dy))
}
if x1 - 1 > x2 {
stack.push((x2 + 1, x1 - 1, y - dy, -dy))
}
x1 = x1 + 1;
while x1 < x2 && !inside(image, x1, y, target) {
x1 = x1 + 1
}
x = x1
}
}
}

/// Determines whether (x,y) is within the image bounds and if the pixel there is equal to target_color
fn inside<I>(image: &I, x: i32, y: i32, target_color: I::Pixel) -> bool
where
I: GenericImage,
{
if x < 0 || y < 0 {
return false;
}
let x = x as u32;
let y = y as u32;
let (width, height) = image.dimensions();
//TODO: Compare pixel equality without conversion to rgba
x < width && y < height && image.get_pixel(x, y).to_rgba() == target_color.to_rgba()
}
3 changes: 3 additions & 0 deletions src/drawing/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ pub use self::rect::{
mod text;
pub use self::text::{draw_text, draw_text_mut, text_size};

mod fill;
pub use self::fill::flood_fill;

// Set pixel at (x, y) to color if this point lies within image bounds,
// otherwise do nothing.
fn draw_if_in_bounds<C>(canvas: &mut C, x: i32, y: i32, color: C::Pixel)
Expand Down
Binary file added tests/data/truth/flood_filled_shape.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions tests/regression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use image::{

use imageproc::contrast::ThresholdType;
use imageproc::definitions::Image;
use imageproc::drawing::flood_fill;
use imageproc::filter::bilateral::GaussianEuclideanColorDistance;
use imageproc::filter::bilateral_filter;
use imageproc::kernel::{self};
Expand Down Expand Up @@ -748,6 +749,25 @@ fn test_draw_filled_ellipse() {
compare_to_truth_image(&image, "filled_ellipse.png");
}

#[test]
fn test_draw_flood_filled_shape() {
use imageproc::drawing::{draw_hollow_ellipse_mut, flood_fill};

let red = Rgb([255, 0, 0]);
let green = Rgb([0, 255, 0]);
let blue = Rgb([0, 0, 255]);
let mut image = RgbImage::from_pixel(200, 200, Rgb([255, 255, 255]));

draw_hollow_ellipse_mut(&mut image, (100, 100), 50, 50, red);
draw_hollow_ellipse_mut(&mut image, (50, 100), 40, 90, blue);
draw_hollow_ellipse_mut(&mut image, (100, 150), 80, 30, green);
draw_hollow_ellipse_mut(&mut image, (150, 150), 100, 60, blue);

flood_fill(&mut image, 120, 120, red);

compare_to_truth_image(&image, "flood_filled_shape.png");
}

#[test]
fn test_hough_line_detection() {
use imageproc::hough::{detect_lines, draw_polar_lines, LineDetectionOptions, PolarLine};
Expand Down

0 comments on commit d2a187c

Please sign in to comment.