0

I have a function that generates a grid of boolean values using a Cellular Automata simulation. The thing that I want is to be able to add a border around pixels. So something like this

Original Image

Add one pixel outline:

1 Pixel Outline

Add another pixel outline for a total of a 2-pixel outline:

2 Pixel Outline

Basically the same thing as Pixilart's outline tool

I have this function that generates the grid with Cellular Automata, with each "pixel" being either true or false

fn generate_grid(width: usize, height: usize, iterations: usize) -> Vec<Vec<bool>> {
    let mut grid = vec![vec![false; width]; height];

    for row in grid.iter_mut().skip(1).take(height - 2) {
        for cell in row.iter_mut().skip(1).take(width - 2) {
            *cell = rand::random();
        }
    }

    for _ in 0..iterations {
        let mut new_grid = vec![vec![false; width]; height];

        for y in 1..height - 1 {
            for x in 1..width - 1 {
                let mut solid_neighbors = 0;
                for dy in -1..=1 {
                    for dx in -1..=1 {
                        if grid[(y as isize + dy) as usize][(x as isize + dx) as usize] {
                            solid_neighbors += 1;
                        }
                    }
                }

                if solid_neighbors >= 5 || solid_neighbors == 0 {
                    new_grid[y][x] = true;
                }
            }
        }
        grid = new_grid;
    }

    grid
}

And another function that converts this grid to a grayscale image. Though this is simply just to check the final grid visually, and doesn't really serve a purpose.

fn grid_to_image(grid: &Vec<Vec<bool>>) -> GrayImage {
    let width = grid[0].len();
    let height = grid.len();
    let mut image = ImageBuffer::new(width as u32, height as u32);

    for (x, y, pixel) in image.enumerate_pixels_mut() {
        let color = if grid[y as usize][x as usize] {
            255u8
        } else {
            0u8
        };
        *pixel = image::Luma([color]);
    }

    GrayImage::from_raw(width as u32, height as u32, image.into_vec()).unwrap()
}

I imagine the function to look something like this, but I just can't wrap my head around what exactly I need to do.

fn add_pixel_border(grid, n_pixels, value) -> &Vec<Vec<bool>> {

    // Set neighbors of all 4 sides of value to value. The part I'm having trouble with

    if n_pixels > 0 {
        return add_pixel_border(grid, n_pixels - 1);
    }
    grid
}

I've tried multiple times thinking of ways to write the function, but I'm just having a hard time at setting the up, left, down, and right pixels to black

cafce25
  • 15,907
  • 4
  • 25
  • 31
wBennis
  • 1
  • 1
  • Not sure what your aim is, i.e. whether you just want the job done or it's a pedagogical exercise or something else, but the operation you are trying is essentially a *"morphological erosion with a `+` (plus sign) structuring element"*. I don't know Rust at all, but any decent image processing library will provide it... **OpenCV**, **vips**, **ImageMagick**. Read about it here https://imagemagick.org/Usage/morphology/#diamond – Mark Setchell Apr 16 '23 at 07:59
  • Note that a morphological erosion would be called a morphological dialtion if your image was white shapes on a black background. What I mean is that you can just as usefully read either about dilation or erosion and come to the same result by dilating a negative/inverted image or eroding a positive image. – Mark Setchell Apr 16 '23 at 08:08
  • You should [replace `&Vec>` with `&[Vec]` in the parameters](https://stackoverflow.com/questions/40006219/why-is-it-discouraged-to-accept-a-reference-string-vec-or-box-as-a-function) – cafce25 Apr 16 '23 at 08:48
  • @cafce25 Or even `&[impl AsRef<[bool]>]`. – cdhowie Apr 16 '23 at 13:54

0 Answers0