imgproc_rs/
morphology.rs

1//! A module for image morphology operations
2
3use crate::{error, util};
4use crate::error::ImgProcResult;
5use crate::image::{Image, BaseImage};
6
7/// Erodes a binary image (grayscale image with pixel values of 0 or 255) using a kernel of size
8/// `(2 * radius + 1) x (2 * radius + 1)`
9pub fn erode(input: &Image<u8>, radius: u32) -> ImgProcResult<Image<u8>> {
10    error::check_grayscale(input)?;
11
12    let (width, height) =  input.info().wh();
13    let size = 2 * radius + 1;
14    let max_sum = (size * size * 255) as f32;
15    let table = util::generate_summed_area_table(&input.clone().into());
16    let mut output = Image::blank(input.info());
17
18    for y in 0..height {
19        for x in 0..width {
20            let mut x_top = x;
21            let mut x_bot = x;
22            let mut y_top = y;
23            let mut y_bot = y;
24
25            if x >= radius {
26                x_top -= radius;
27            }
28            if x < width - radius {
29                x_bot += radius;
30            }
31            if y >= radius {
32                y_top -= radius;
33            }
34            if y < height - radius {
35                y_bot += radius;
36            }
37
38            if (util::rectangular_intensity_sum(&table, x_top, y_top, x_bot, y_bot)[0] - max_sum).abs() < f32::EPSILON {
39                output.set_pixel(x, y, &[255]);
40            }
41        }
42    }
43
44    Ok(output)
45}
46
47/// Dilates a binary image (grayscale image with pixel values of 0 or 255) using a kernel of size
48/// `(2 * radius + 1) x (2 * radius + 1)`
49pub fn dilate(input: &Image<u8>, radius: u32) -> ImgProcResult<Image<u8>> {
50    error::check_grayscale(input)?;
51
52    let (width, height) =  input.info().wh();
53    let table = util::generate_summed_area_table(&input.clone().into());
54    let mut output = Image::blank(input.info());
55
56    for y in 0..height {
57        for x in 0..width {
58            let mut x_top = x;
59            let mut x_bot = x;
60            let mut y_top = y;
61            let mut y_bot = y;
62
63            if x >= radius {
64                x_top -= radius;
65            }
66            if x < width - radius {
67                x_bot += radius;
68            }
69            if y >= radius {
70                y_top -= radius;
71            }
72            if y < height - radius {
73                y_bot += radius;
74            }
75
76            if util::rectangular_intensity_sum(&table, x_top, y_top, x_bot, y_bot)[0] >= 255.0 {
77                output.set_pixel(x, y, &[255]);
78            }
79        }
80    }
81
82    Ok(output)
83}
84
85/// Sets output pixel to the majority-valued pixel in the input image under a kernel of size
86/// `(2 * radius + 1) x (2 * radius + 1)`
87pub fn majority(input: &Image<u8>, radius: u32) -> ImgProcResult<Image<u8>> {
88    error::check_grayscale(input)?;
89
90    let (width, height) =  input.info().wh();
91    let table = util::generate_summed_area_table(&input.clone().into());
92    let mut output = Image::blank(input.info());
93
94    for y in 0..height {
95        for x in 0..width {
96            let mut x_top = x;
97            let mut x_bot = x;
98            let mut y_top = y;
99            let mut y_bot = y;
100
101            if x >= radius {
102                x_top -= radius;
103            }
104            if x < width - radius {
105                x_bot += radius;
106            }
107            if y >= radius {
108                y_top -= radius;
109            }
110            if y < height - radius {
111                y_bot += radius;
112            }
113
114            if util::rectangular_intensity_sum(&table, x_top, y_top, x_bot, y_bot)[0] >= 255.0 {
115                output.set_pixel(x, y, &[255]);
116            }
117        }
118    }
119
120    Ok(output)
121}
122
123/// Applies an erosion followed by a dilation
124pub fn open(input: &Image<u8>, radius: u32) -> ImgProcResult<Image<u8>> {
125    Ok(dilate(&erode(input, radius)?, radius)?)
126}
127
128/// Applies a dilation followed by an erosion
129pub fn close(input: &Image<u8>, radius: u32) -> ImgProcResult<Image<u8>> {
130    Ok(erode(&dilate(input, radius)?, radius)?)
131}
132
133/// Returns the difference between dilation and erosion of the image
134#[allow(unused_parens)]
135pub fn gradient(input: &Image<u8>, radius: u32) -> ImgProcResult<Image<u8>> {
136    error::check_grayscale(input)?;
137
138    let (width, height) =  input.info().wh();
139    let size = 2 * radius + 1;
140    let max_sum = (size * size * 255) as f32;
141    let table = util::generate_summed_area_table(&input.clone().into());
142    let mut output = Image::blank(input.info());
143
144    for y in 0..height {
145        for x in 0..width {
146            let mut x_top = x;
147            let mut x_bot = x;
148            let mut y_top = y;
149            let mut y_bot = y;
150
151            if x >= radius {
152                x_top -= radius;
153            }
154            if x < width - radius {
155                x_bot += radius;
156            }
157            if y >= radius {
158                y_top -= radius;
159            }
160            if y < height - radius {
161                y_bot += radius;
162            }
163
164            let sum = util::rectangular_intensity_sum(&table, x_top, y_top, x_bot, y_bot)[0];
165            let erode = ((sum - max_sum).abs() < f32::EPSILON);
166            let dilate = (sum >= 255.0);
167
168            if erode ^ dilate {
169                output.set_pixel(x, y, &[255]);
170            }
171        }
172    }
173
174    Ok(output)
175}