1use crate::error::ImageError;
6
7pub fn dilate(
13 image: &[f32],
14 width: usize,
15 height: usize,
16 se: &[f32],
17 se_w: usize,
18 se_h: usize,
19) -> Result<Vec<f32>, ImageError> {
20 validate_inputs(image, width, height, se, se_w, se_h)?;
21 let mut output = vec![0.0f32; width * height];
22 for y in 0..height {
23 for x in 0..width {
24 output[y * width + x] = neighborhood_max(image, width, height, se, se_w, se_h, x, y);
25 }
26 }
27 Ok(output)
28}
29
30pub fn erode(
36 image: &[f32],
37 width: usize,
38 height: usize,
39 se: &[f32],
40 se_w: usize,
41 se_h: usize,
42) -> Result<Vec<f32>, ImageError> {
43 validate_inputs(image, width, height, se, se_w, se_h)?;
44 let mut output = vec![0.0f32; width * height];
45 for y in 0..height {
46 for x in 0..width {
47 output[y * width + x] = neighborhood_min(image, width, height, se, se_w, se_h, x, y);
48 }
49 }
50 Ok(output)
51}
52
53#[allow(clippy::too_many_arguments)]
55fn neighborhood_max(
56 image: &[f32],
57 width: usize,
58 height: usize,
59 se: &[f32],
60 se_w: usize,
61 se_h: usize,
62 px: usize,
63 py: usize,
64) -> f32 {
65 let half_w = se_w / 2;
66 let half_h = se_h / 2;
67 let mut max_val = f32::NEG_INFINITY;
68 for sy in 0..se_h {
69 for sx in 0..se_w {
70 if se[sy * se_w + sx] <= 0.0 {
71 continue;
72 }
73 let iy = py as isize + sy as isize - half_h as isize;
74 let ix = px as isize + sx as isize - half_w as isize;
75 if iy >= 0 && iy < height as isize && ix >= 0 && ix < width as isize {
76 let val = image[iy as usize * width + ix as usize];
77 if val > max_val {
78 max_val = val;
79 }
80 }
81 }
82 }
83 if max_val == f32::NEG_INFINITY {
84 0.0
85 } else {
86 max_val
87 }
88}
89
90#[allow(clippy::too_many_arguments)]
92fn neighborhood_min(
93 image: &[f32],
94 width: usize,
95 height: usize,
96 se: &[f32],
97 se_w: usize,
98 se_h: usize,
99 px: usize,
100 py: usize,
101) -> f32 {
102 let half_w = se_w / 2;
103 let half_h = se_h / 2;
104 let mut min_val = f32::INFINITY;
105 for sy in 0..se_h {
106 for sx in 0..se_w {
107 if se[sy * se_w + sx] <= 0.0 {
108 continue;
109 }
110 let iy = py as isize + sy as isize - half_h as isize;
111 let ix = px as isize + sx as isize - half_w as isize;
112 if iy >= 0 && iy < height as isize && ix >= 0 && ix < width as isize {
113 let val = image[iy as usize * width + ix as usize];
114 if val < min_val {
115 min_val = val;
116 }
117 }
118 }
119 }
120 if min_val == f32::INFINITY {
121 0.0
122 } else {
123 min_val
124 }
125}
126
127pub fn opening(
133 image: &[f32],
134 width: usize,
135 height: usize,
136 se: &[f32],
137 se_w: usize,
138 se_h: usize,
139) -> Result<Vec<f32>, ImageError> {
140 let eroded = erode(image, width, height, se, se_w, se_h)?;
141 dilate(&eroded, width, height, se, se_w, se_h)
142}
143
144pub fn closing(
150 image: &[f32],
151 width: usize,
152 height: usize,
153 se: &[f32],
154 se_w: usize,
155 se_h: usize,
156) -> Result<Vec<f32>, ImageError> {
157 let dilated = dilate(image, width, height, se, se_w, se_h)?;
158 erode(&dilated, width, height, se, se_w, se_h)
159}
160
161fn validate_inputs(
162 image: &[f32],
163 width: usize,
164 height: usize,
165 se: &[f32],
166 se_w: usize,
167 se_h: usize,
168) -> Result<(), ImageError> {
169 if image.len() != width * height {
170 return Err(ImageError::BufferLengthMismatch {
171 expected: width * height,
172 got: image.len(),
173 width,
174 height,
175 });
176 }
177 if se.len() != se_w * se_h || se_w == 0 || se_h == 0 {
178 return Err(ImageError::InvalidKernelSize { kw: se_w, kh: se_h });
179 }
180 Ok(())
181}