use scirs2_core::ndarray::{Array, Array1, Array2, Dimension, Ix1, Ix2, IxDyn};
use crate::error::{NdimageError, NdimageResult};
use crate::morphology::structuring::generate_binary_structure_dyn;
use crate::morphology::utils::get_structure_center_dyn;
#[allow(dead_code)]
pub fn binary_erosion<D>(
input: &Array<bool, D>,
structure: Option<&Array<bool, D>>,
iterations: Option<usize>,
mask: Option<&Array<bool, D>>,
border_value: Option<bool>,
origin: Option<&[isize]>,
brute_force: Option<bool>,
) -> NdimageResult<Array<bool, D>>
where
D: Dimension + 'static,
{
if input.ndim() == 0 {
return Err(NdimageError::InvalidInput(
"Input array cannot be 0-dimensional".into(),
));
}
match input.ndim() {
1 => {
if let Ok(input_1d) = input.clone().into_dimensionality::<Ix1>() {
let structure_1d = match structure {
Some(s) => {
if let Ok(s1d) = s.clone().into_dimensionality::<Ix1>() {
Some(s1d)
} else {
return Err(NdimageError::DimensionError(
"Failed to convert structure to 1D".to_string(),
));
}
}
None => None,
};
let mask_1d = match mask {
Some(m) => {
if let Ok(m1d) = m.clone().into_dimensionality::<Ix1>() {
Some(m1d)
} else {
return Err(NdimageError::DimensionError(
"Failed to convert mask to 1D".to_string(),
));
}
}
None => None,
};
let result_1d = binary_erosion1d(
&input_1d,
structure_1d.as_ref(),
iterations,
mask_1d.as_ref(),
border_value,
origin,
brute_force,
)?;
return result_1d.into_dimensionality().map_err(|_| {
NdimageError::DimensionError(
"Failed to convert result back to original dimensionality".to_string(),
)
});
}
}
2 => {
if let Ok(input_2d) = input.clone().into_dimensionality::<Ix2>() {
let structure_2d = match structure {
Some(s) => {
if let Ok(s2d) = s.clone().into_dimensionality::<Ix2>() {
Some(s2d)
} else {
return Err(NdimageError::DimensionError(
"Failed to convert structure to 2D".to_string(),
));
}
}
None => None,
};
let mask_2d = match mask {
Some(m) => {
if let Ok(m2d) = m.clone().into_dimensionality::<Ix2>() {
Some(m2d)
} else {
return Err(NdimageError::DimensionError(
"Failed to convert mask to 2D".to_string(),
));
}
}
None => None,
};
let result_2d = binary_erosion2d(
&input_2d,
structure_2d.as_ref(),
iterations,
mask_2d.as_ref(),
border_value,
origin,
brute_force,
)?;
return result_2d.into_dimensionality().map_err(|_| {
NdimageError::DimensionError(
"Failed to convert result back to original dimensionality".to_string(),
)
});
}
}
_ => {
if let Ok(input_dyn) = input.clone().into_dimensionality::<IxDyn>() {
let structure_dyn = match structure {
Some(s) => {
if let Ok(sdyn) = s.clone().into_dimensionality::<IxDyn>() {
Some(sdyn)
} else {
return Err(NdimageError::DimensionError(
"Failed to convert structure to dynamic dimension".to_string(),
));
}
}
None => None,
};
let mask_dyn = match mask {
Some(m) => {
if let Ok(mdyn) = m.clone().into_dimensionality::<IxDyn>() {
Some(mdyn)
} else {
return Err(NdimageError::DimensionError(
"Failed to convert mask to dynamic dimension".to_string(),
));
}
}
None => None,
};
let result_dyn = binary_erosion_dyn(
&input_dyn,
structure_dyn.as_ref(),
iterations,
mask_dyn.as_ref(),
border_value,
origin,
brute_force,
)?;
return result_dyn.into_dimensionality().map_err(|_| {
NdimageError::DimensionError(
"Failed to convert result back to original dimensionality".to_string(),
)
});
}
}
}
Err(NdimageError::DimensionError(
"Unsupported array dimensions for erosion".to_string(),
))
}
#[allow(dead_code)]
fn binary_erosion1d(
input: &Array1<bool>,
structure: Option<&Array1<bool>>,
iterations: Option<usize>,
mask: Option<&Array1<bool>>,
border_value: Option<bool>,
origin: Option<&[isize]>,
brute_force: Option<bool>,
) -> NdimageResult<Array1<bool>> {
let iters = iterations.unwrap_or(1);
let border_val = border_value.unwrap_or(false);
let brute_force_algo = brute_force.unwrap_or(false);
let owned_structure;
let struct_elem = if let Some(s) = structure {
s
} else {
owned_structure = Array1::from_elem(3, true);
&owned_structure
};
let origin_vec: Vec<isize> = if let Some(o) = origin {
if o.len() != 1 {
return Err(NdimageError::DimensionError(format!(
"Origin must have same length as input dimensions (got {} expected {})",
o.len(),
1
)));
}
o.to_vec()
} else {
vec![(struct_elem.len() as isize) / 2]
};
let mut result = input.to_owned();
for _ in 0..iters {
let mut temp = Array1::from_elem(input.len(), false);
let prev = result.clone();
for (i, val) in temp.indexed_iter_mut() {
if let Some(m) = mask {
if !m[i] {
*val = prev[i];
continue;
}
}
let mut fits = true;
for (s_i, &s_val) in struct_elem.indexed_iter() {
if !s_val {
continue; }
let offset = s_i as isize - origin_vec[0];
let pos = i as isize + offset;
if pos < 0 || pos >= prev.len() as isize {
if !border_val {
fits = false;
break;
}
} else if !prev[pos as usize] {
fits = false;
break;
}
}
*val = fits;
}
result = temp;
if !brute_force_algo && result == prev {
break;
}
}
Ok(result)
}
#[allow(dead_code)]
fn binary_erosion2d(
input: &Array2<bool>,
structure: Option<&Array2<bool>>,
iterations: Option<usize>,
mask: Option<&Array2<bool>>,
border_value: Option<bool>,
origin: Option<&[isize]>,
brute_force: Option<bool>,
) -> NdimageResult<Array2<bool>> {
let iters = iterations.unwrap_or(1);
let border_val = border_value.unwrap_or(false);
let brute_force_algo = brute_force.unwrap_or(false);
let owned_structure;
let struct_elem = if let Some(s) = structure {
s
} else {
let size = [3, 3];
owned_structure = Array2::from_elem((size[0], size[1]), true);
&owned_structure
};
let origin_vec: Vec<isize> = if let Some(o) = origin {
if o.len() != 2 {
return Err(NdimageError::DimensionError(format!(
"Origin must have same length as input dimensions (got {} expected {})",
o.len(),
2
)));
}
o.to_vec()
} else {
struct_elem
.shape()
.iter()
.map(|&s| (s as isize) / 2)
.collect()
};
let shape = input.shape();
let mut result = input.to_owned();
for iter in 0..iters {
let prev = result.clone();
let mut temp = Array2::from_elem((shape[0], shape[1]), false);
let s_rows = struct_elem.shape()[0];
let s_cols = struct_elem.shape()[1];
let half_height = origin_vec[0];
let half_width = origin_vec[1];
for i in 0..shape[0] {
for j in 0..shape[1] {
if let Some(m) = mask {
if !m[[i, j]] {
temp[[i, j]] = prev[[i, j]];
continue;
}
}
let mut fits = true;
'outer: for si in 0..s_rows {
for sj in 0..s_cols {
if !struct_elem[[si, sj]] {
continue; }
let ni = i as isize + (si as isize - half_height);
let nj = j as isize + (sj as isize - half_width);
if ni < 0 || ni >= shape[0] as isize || nj < 0 || nj >= shape[1] as isize {
if !border_val {
fits = false;
break 'outer;
}
} else if !prev[[ni as usize, nj as usize]] {
fits = false;
break 'outer;
}
}
}
temp[[i, j]] = fits;
}
}
result = temp;
if !brute_force_algo && iter > 0 && result == prev {
break;
}
}
Ok(result)
}
#[allow(dead_code)]
fn binary_erosion_dyn(
input: &Array<bool, IxDyn>,
structure: Option<&Array<bool, IxDyn>>,
iterations: Option<usize>,
mask: Option<&Array<bool, IxDyn>>,
border_value: Option<bool>,
origin: Option<&[isize]>,
_brute_force: Option<bool>,
) -> NdimageResult<Array<bool, IxDyn>> {
let iterations = iterations.unwrap_or(1);
let border = border_value.unwrap_or(false);
let default_structure = if let Some(s) = structure {
s.to_owned()
} else {
generate_binary_structure_dyn(input.ndim())?
};
if input.ndim() != default_structure.ndim() {
return Err(NdimageError::DimensionError(
"Input and structure must have the same number of dimensions".into(),
));
}
if let Some(m) = mask {
if m.ndim() != input.ndim() || m.shape() != input.shape() {
return Err(NdimageError::InvalidInput(
"Mask must have the same shape as input".into(),
));
}
}
let center = get_structure_center_dyn(&default_structure, origin)?;
let mut result = input.to_owned();
for _ in 0..iterations {
let temp = result.clone();
for idx in scirs2_core::ndarray::indices(input.shape()) {
let idx_vec: Vec<_> = idx.slice().to_vec();
if let Some(m) = mask {
if !m[idx_vec.as_slice()] {
continue;
}
}
let mut all_fit = true;
for str_idx in scirs2_core::ndarray::indices(default_structure.shape()) {
let str_idx_vec: Vec<_> = str_idx.slice().to_vec();
if !default_structure[str_idx_vec.as_slice()] {
continue;
}
let mut input_pos = vec![0isize; input.ndim()];
for d in 0..input.ndim() {
input_pos[d] = idx_vec[d] as isize + str_idx_vec[d] as isize - center[d];
}
let mut within_bounds = true;
for (d, &pos) in input_pos.iter().enumerate().take(input.ndim()) {
if pos < 0 || pos >= input.shape()[d] as isize {
within_bounds = false;
break;
}
}
let val = if within_bounds {
let input_idx: Vec<_> = input_pos.iter().map(|&x| x as usize).collect();
temp[input_idx.as_slice()]
} else {
border
};
if !val {
all_fit = false;
break;
}
}
result[idx_vec.as_slice()] = all_fit;
}
}
Ok(result)
}