scirs2_ndimage/morphology/binary/
dilation.rs1use scirs2_core::ndarray::{Array, Array1, Array2, Dimension, Ix1, Ix2, IxDyn};
8
9use crate::error::{NdimageError, NdimageResult};
10use crate::morphology::structuring::generate_binary_structure_dyn;
11use crate::morphology::utils::get_structure_center_dyn;
12
13#[allow(dead_code)]
32pub fn binary_dilation<D>(
33 input: &Array<bool, D>,
34 structure: Option<&Array<bool, D>>,
35 iterations: Option<usize>,
36 mask: Option<&Array<bool, D>>,
37 border_value: Option<bool>,
38 origin: Option<&[isize]>,
39 brute_force: Option<bool>,
40) -> NdimageResult<Array<bool, D>>
41where
42 D: Dimension + 'static,
43{
44 if input.ndim() == 0 {
46 return Err(NdimageError::InvalidInput(
47 "Input array cannot be 0-dimensional".into(),
48 ));
49 }
50
51 match input.ndim() {
53 1 => {
54 if let Ok(input_1d) = input.clone().into_dimensionality::<Ix1>() {
55 let structure_1d = match structure {
57 Some(s) => {
58 if let Ok(s1d) = s.clone().into_dimensionality::<Ix1>() {
59 Some(s1d)
60 } else {
61 return Err(NdimageError::DimensionError(
62 "Failed to convert structure to 1D".to_string(),
63 ));
64 }
65 }
66 None => None,
67 };
68
69 let mask_1d = match mask {
71 Some(m) => {
72 if let Ok(m1d) = m.clone().into_dimensionality::<Ix1>() {
73 Some(m1d)
74 } else {
75 return Err(NdimageError::DimensionError(
76 "Failed to convert mask to 1D".to_string(),
77 ));
78 }
79 }
80 None => None,
81 };
82
83 let result_1d = binary_dilation1d(
85 &input_1d,
86 structure_1d.as_ref(),
87 iterations,
88 mask_1d.as_ref(),
89 border_value,
90 origin,
91 brute_force,
92 )?;
93
94 return result_1d.into_dimensionality().map_err(|_| {
96 NdimageError::DimensionError(
97 "Failed to convert result back to original dimensionality".to_string(),
98 )
99 });
100 }
101 }
102 2 => {
103 if let Ok(input_2d) = input.clone().into_dimensionality::<Ix2>() {
104 let structure_2d = match structure {
106 Some(s) => {
107 if let Ok(s2d) = s.clone().into_dimensionality::<Ix2>() {
108 Some(s2d)
109 } else {
110 return Err(NdimageError::DimensionError(
111 "Failed to convert structure to 2D".to_string(),
112 ));
113 }
114 }
115 None => None,
116 };
117
118 let mask_2d = match mask {
120 Some(m) => {
121 if let Ok(m2d) = m.clone().into_dimensionality::<Ix2>() {
122 Some(m2d)
123 } else {
124 return Err(NdimageError::DimensionError(
125 "Failed to convert mask to 2D".to_string(),
126 ));
127 }
128 }
129 None => None,
130 };
131
132 let result_2d = binary_dilation2d(
134 &input_2d,
135 structure_2d.as_ref(),
136 iterations,
137 mask_2d.as_ref(),
138 border_value,
139 origin,
140 brute_force,
141 )?;
142
143 return result_2d.into_dimensionality().map_err(|_| {
145 NdimageError::DimensionError(
146 "Failed to convert result back to original dimensionality".to_string(),
147 )
148 });
149 }
150 }
151 _ => {
152 if let Ok(input_dyn) = input.clone().into_dimensionality::<IxDyn>() {
154 let structure_dyn = match structure {
156 Some(s) => {
157 if let Ok(sdyn) = s.clone().into_dimensionality::<IxDyn>() {
158 Some(sdyn)
159 } else {
160 return Err(NdimageError::DimensionError(
161 "Failed to convert structure to dynamic dimension".to_string(),
162 ));
163 }
164 }
165 None => None,
166 };
167
168 let mask_dyn = match mask {
170 Some(m) => {
171 if let Ok(mdyn) = m.clone().into_dimensionality::<IxDyn>() {
172 Some(mdyn)
173 } else {
174 return Err(NdimageError::DimensionError(
175 "Failed to convert mask to dynamic dimension".to_string(),
176 ));
177 }
178 }
179 None => None,
180 };
181
182 let result_dyn = binary_dilation_dyn(
184 &input_dyn,
185 structure_dyn.as_ref(),
186 iterations,
187 mask_dyn.as_ref(),
188 border_value,
189 origin,
190 brute_force,
191 )?;
192
193 return result_dyn.into_dimensionality().map_err(|_| {
195 NdimageError::DimensionError(
196 "Failed to convert result back to original dimensionality".to_string(),
197 )
198 });
199 }
200 }
201 }
202
203 Err(NdimageError::DimensionError(
205 "Unsupported array dimensions for dilation".to_string(),
206 ))
207}
208
209#[allow(dead_code)]
211fn binary_dilation1d(
212 input: &Array1<bool>,
213 structure: Option<&Array1<bool>>,
214 iterations: Option<usize>,
215 mask: Option<&Array1<bool>>,
216 border_value: Option<bool>,
217 origin: Option<&[isize]>,
218 brute_force: Option<bool>,
219) -> NdimageResult<Array1<bool>> {
220 let iters = iterations.unwrap_or(1);
222 let border_val = border_value.unwrap_or(false);
223 let brute_force_algo = brute_force.unwrap_or(false);
224
225 let owned_structure;
227 let struct_elem = if let Some(s) = structure {
228 s
229 } else {
230 owned_structure = Array1::from_elem(3, true);
232 &owned_structure
233 };
234
235 let origin_vec: Vec<isize> = if let Some(o) = origin {
237 if o.len() != 1 {
238 return Err(NdimageError::DimensionError(format!(
239 "Origin must have same length as input dimensions (got {} expected {})",
240 o.len(),
241 1
242 )));
243 }
244 o.to_vec()
245 } else {
246 vec![(struct_elem.len() as isize) / 2]
248 };
249
250 let mut result = input.to_owned();
252
253 for _ in 0..iters {
255 let mut temp = Array1::from_elem(input.len(), false);
257 let prev = result.clone();
258
259 for (i, val) in temp.indexed_iter_mut() {
261 if let Some(m) = mask {
263 if !m[i] {
264 *val = prev[i];
265 continue;
266 }
267 }
268
269 *val = prev[i];
271
272 if *val {
274 continue;
275 }
276
277 for (s_i, &s_val) in struct_elem.indexed_iter() {
279 if !s_val {
280 continue; }
282
283 let offset = origin_vec[0] - s_i as isize;
285 let pos = i as isize + offset;
286
287 if pos < 0 || pos >= prev.len() as isize {
289 if border_val {
291 *val = true;
292 break;
293 }
294 } else if prev[pos as usize] {
295 *val = true;
297 break;
298 }
299 }
300 }
301
302 result = temp;
303
304 if !brute_force_algo && result == prev {
306 break;
307 }
308 }
309
310 Ok(result)
311}
312
313#[allow(dead_code)]
315fn binary_dilation2d(
316 input: &Array2<bool>,
317 structure: Option<&Array2<bool>>,
318 iterations: Option<usize>,
319 mask: Option<&Array2<bool>>,
320 border_value: Option<bool>,
321 origin: Option<&[isize]>,
322 brute_force: Option<bool>,
323) -> NdimageResult<Array2<bool>> {
324 let iters = iterations.unwrap_or(1);
326 let border_val = border_value.unwrap_or(false);
327 let brute_force_algo = brute_force.unwrap_or(false);
328
329 let owned_structure;
331 let struct_elem = if let Some(s) = structure {
332 s
333 } else {
334 let size = [3, 3];
336 owned_structure = Array2::from_elem((size[0], size[1]), true);
337 &owned_structure
338 };
339
340 let origin_vec: Vec<isize> = if let Some(o) = origin {
342 if o.len() != 2 {
343 return Err(NdimageError::DimensionError(format!(
344 "Origin must have same length as input dimensions (got {} expected {})",
345 o.len(),
346 2
347 )));
348 }
349 o.to_vec()
350 } else {
351 struct_elem
353 .shape()
354 .iter()
355 .map(|&s| (s as isize) / 2)
356 .collect()
357 };
358
359 let shape = input.shape();
360 let mut result = input.to_owned();
361
362 for iter in 0..iters {
364 let prev = result.clone();
365 let mut temp = Array2::from_elem((shape[0], shape[1]), false);
366
367 let s_rows = struct_elem.shape()[0];
369 let s_cols = struct_elem.shape()[1];
370
371 let half_height = origin_vec[0];
373 let half_width = origin_vec[1];
374
375 for i in 0..shape[0] {
377 for j in 0..shape[1] {
378 if let Some(m) = mask {
380 if !m[[i, j]] {
381 temp[[i, j]] = prev[[i, j]];
382 continue;
383 }
384 }
385
386 temp[[i, j]] = prev[[i, j]];
388
389 if temp[[i, j]] {
391 continue;
392 }
393
394 let mut found_true = false;
396
397 'outer: for si in 0..s_rows {
399 for sj in 0..s_cols {
400 if !struct_elem[[si, sj]] {
401 continue; }
403
404 let ni = i as isize - (si as isize - half_height);
406 let nj = j as isize - (sj as isize - half_width);
407
408 if ni < 0 || ni >= shape[0] as isize || nj < 0 || nj >= shape[1] as isize {
410 if border_val {
412 found_true = true;
413 break 'outer;
414 }
415 } else if prev[[ni as usize, nj as usize]] {
416 found_true = true;
418 break 'outer;
419 }
420 }
421 }
422
423 if found_true {
424 temp[[i, j]] = true;
425 }
426 }
427 }
428
429 result = temp;
430
431 if !brute_force_algo && iter > 0 && result == prev {
433 break;
434 }
435 }
436
437 Ok(result)
438}
439
440#[allow(dead_code)]
442fn binary_dilation_dyn(
443 input: &Array<bool, IxDyn>,
444 structure: Option<&Array<bool, IxDyn>>,
445 iterations: Option<usize>,
446 mask: Option<&Array<bool, IxDyn>>,
447 border_value: Option<bool>,
448 origin: Option<&[isize]>,
449 _brute_force: Option<bool>,
450) -> NdimageResult<Array<bool, IxDyn>> {
451 let iterations = iterations.unwrap_or(1);
452 let border = border_value.unwrap_or(false);
453
454 let default_structure = if let Some(s) = structure {
456 s.to_owned()
457 } else {
458 generate_binary_structure_dyn(input.ndim())?
459 };
460
461 if input.ndim() != default_structure.ndim() {
463 return Err(NdimageError::DimensionError(
464 "Input and structure must have the same number of dimensions".into(),
465 ));
466 }
467
468 if let Some(m) = mask {
470 if m.ndim() != input.ndim() || m.shape() != input.shape() {
471 return Err(NdimageError::InvalidInput(
472 "Mask must have the same shape as input".into(),
473 ));
474 }
475 }
476
477 let center = get_structure_center_dyn(&default_structure, origin)?;
479
480 let mut result = input.to_owned();
482
483 for _ in 0..iterations {
485 let temp = result.clone();
486
487 for idx in scirs2_core::ndarray::indices(input.shape()) {
489 let idx_vec: Vec<_> = idx.slice().to_vec();
490
491 if let Some(m) = mask {
493 if !m[idx_vec.as_slice()] {
494 continue;
495 }
496 }
497
498 let mut any_fit = false;
500
501 for str_idx in scirs2_core::ndarray::indices(default_structure.shape()) {
503 let str_idx_vec: Vec<_> = str_idx.slice().to_vec();
504
505 if !default_structure[str_idx_vec.as_slice()] {
507 continue;
508 }
509
510 let mut input_pos = vec![0isize; input.ndim()];
512 for d in 0..input.ndim() {
513 input_pos[d] = idx_vec[d] as isize + str_idx_vec[d] as isize - center[d];
514 }
515
516 let mut within_bounds = true;
518 for (d, &pos) in input_pos.iter().enumerate().take(input.ndim()) {
519 if pos < 0 || pos >= input.shape()[d] as isize {
520 within_bounds = false;
521 break;
522 }
523 }
524
525 let val = if within_bounds {
527 let input_idx: Vec<_> = input_pos.iter().map(|&x| x as usize).collect();
528 temp[input_idx.as_slice()]
529 } else {
530 border
531 };
532
533 if val {
535 any_fit = true;
536 break;
537 }
538 }
539
540 result[idx_vec.as_slice()] = any_fit;
541 }
542 }
543
544 Ok(result)
545}