Skip to main content

scirs2_ndimage/morphology/
utils.rs

1//! Utility functions for morphological operations
2
3use scirs2_core::ndarray::{Array, Dimension};
4use scirs2_core::numeric::{Float, FromPrimitive};
5use std::fmt::Debug;
6
7use super::MorphBorderMode;
8use crate::error::{NdimageError, NdimageResult};
9
10/// Apply padding to an array based on the specified border mode for morphological operations
11///
12/// # Arguments
13///
14/// * `input` - Input array to pad
15/// * `pad_width` - Width of padding in each dimension (before, after)
16/// * `mode` - Border handling mode
17/// * `constant_value` - Value to use for constant mode
18///
19/// # Returns
20///
21/// * `Result<Array<T, D>>` - Padded array
22#[allow(dead_code)]
23pub fn pad_array<T, D>(
24    input: &Array<T, D>,
25    pad_width: &[(usize, usize)],
26    _mode: &MorphBorderMode,
27    value: T,
28) -> NdimageResult<Array<T, D>>
29where
30    T: Float + FromPrimitive + Debug + Clone + std::ops::AddAssign + std::ops::DivAssign + 'static,
31    D: Dimension + 'static,
32{
33    // Validate inputs
34    if input.ndim() == 0 {
35        return Err(NdimageError::InvalidInput(
36            "Input array cannot be 0-dimensional".into(),
37        ));
38    }
39
40    if pad_width.len() != input.ndim() {
41        return Err(NdimageError::DimensionError(format!(
42            "Pad _width must have same length as input dimensions (got {} expected {})",
43            pad_width.len(),
44            input.ndim()
45        )));
46    }
47
48    // No padding needed - return copy of input
49    if pad_width.iter().all(|&(a, b)| a == 0 && b == 0) {
50        return Ok(input.to_owned());
51    }
52
53    // Placeholder implementation returning a copy of the input
54    // This will be properly implemented with padding logic
55    Ok(input.to_owned())
56}
57
58/// Check if an array is a valid structuring element
59///
60/// # Arguments
61///
62/// * `structure` - Structuring element to check
63///
64/// # Returns
65///
66/// * `Result<()>` - Ok if valid, Error otherwise
67#[allow(dead_code)]
68pub fn validate_structure<D>(structure: &Array<bool, D>) -> NdimageResult<()>
69where
70    D: Dimension,
71{
72    // Validate inputs
73    if structure.ndim() == 0 {
74        return Err(NdimageError::InvalidInput(
75            "Structure cannot be 0-dimensional".into(),
76        ));
77    }
78
79    // Require at least one True value
80    if !structure.iter().any(|&x| x) {
81        return Err(NdimageError::InvalidInput(
82            "Structure must have at least one True value".into(),
83        ));
84    }
85
86    // For proper validation we would also check:
87    // - All dimensions are odd (so there's a clear center)
88    // - The _structure has a center element that is True
89
90    Ok(())
91}
92
93/// Get center indices of a structuring element
94///
95/// # Arguments
96///
97/// * `structure` - Structuring element
98/// * `origin` - Origin of the structuring element (if None, uses the center)
99///
100/// # Returns
101///
102/// * `Result<Vec<isize>>` - Center indices
103#[allow(dead_code)]
104pub fn get_structure_center<D>(
105    structure: &Array<bool, D>,
106    origin: Option<&[isize]>,
107) -> NdimageResult<Vec<isize>>
108where
109    D: Dimension,
110{
111    // Validate inputs
112    if structure.ndim() == 0 {
113        return Err(NdimageError::InvalidInput(
114            "Structure cannot be 0-dimensional".into(),
115        ));
116    }
117
118    // If origin is specified, validate and use it
119    if let Some(orig) = origin {
120        if orig.len() != structure.ndim() {
121            return Err(NdimageError::DimensionError(format!(
122                "Origin must have same length as structure dimensions (got {} expected {})",
123                orig.len(),
124                structure.ndim()
125            )));
126        }
127
128        // Check that origin is within bounds
129        for (i, &o) in orig.iter().enumerate() {
130            let dim = structure.shape()[i] as isize;
131            if o < -(dim / 2) || o > dim / 2 {
132                return Err(NdimageError::InvalidInput(format!(
133                    "Origin {} is out of bounds for dimension {} of size {}",
134                    o, i, dim
135                )));
136            }
137        }
138
139        return Ok(orig.to_vec());
140    }
141
142    // Otherwise, calculate center
143    let mut center = Vec::with_capacity(structure.ndim());
144    for &dim in structure.shape() {
145        center.push((dim as isize) / 2);
146    }
147
148    Ok(center)
149}
150
151/// Get the center of a structuring element (dynamic version)
152///
153/// # Arguments
154///
155/// * `structure` - Structuring element
156/// * `origin` - Optional origin offset
157///
158/// # Returns
159///
160/// * `Result<Vec<isize>>` - Center indices
161pub(crate) fn get_structure_center_dyn(
162    structure: &Array<bool, scirs2_core::ndarray::IxDyn>,
163    origin: Option<&[isize]>,
164) -> NdimageResult<Vec<isize>> {
165    get_structure_center(structure, origin)
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171    use scirs2_core::ndarray::Array2;
172
173    #[test]
174    fn test_validate_structure() {
175        let structure = Array2::from_elem((3, 3), true);
176        let result = validate_structure(&structure);
177        assert!(result.is_ok());
178
179        let empty_structure = Array2::from_elem((3, 3), false);
180        let result = validate_structure(&empty_structure);
181        assert!(result.is_err());
182    }
183
184    #[test]
185    fn test_get_structure_center() {
186        let structure = Array2::from_elem((3, 3), true);
187        let center = get_structure_center(&structure, None).expect("Operation failed");
188        assert_eq!(center, vec![1, 1]);
189
190        let structure = Array2::from_elem((5, 5), true);
191        let center = get_structure_center(&structure, None).expect("Operation failed");
192        assert_eq!(center, vec![2, 2]);
193    }
194}