ndarray_ndimage/
lib.rs

1#![warn(missing_docs, trivial_casts, trivial_numeric_casts, unused_qualifications)]
2
3//! The `ndarray-image` crate provides multidimensional image processing for `ArrayBase`,
4//! the *n*-dimensional array data structure provided by [`ndarray`].
5
6use ndarray::{arr3, Array, Array3, ArrayBase, Data, Dimension, Ix3, ShapeBuilder};
7
8mod filters;
9mod interpolation;
10mod measurements;
11mod morphology;
12mod pad;
13
14pub use filters::{
15    con_corr::{convolve, convolve1d, correlate, correlate1d, prewitt, sobel},
16    gaussian::{gaussian_filter, gaussian_filter1d},
17    median::median_filter,
18    min_max::{
19        maximum_filter, maximum_filter1d, maximum_filter1d_to, minimum_filter, minimum_filter1d,
20        minimum_filter1d_to,
21    },
22    uniform::{uniform_filter, uniform_filter1d},
23    BorderMode,
24};
25pub use interpolation::{shift, spline_filter, spline_filter1d, zoom};
26pub use measurements::{label, label_histogram, largest_connected_components, most_frequent_label};
27pub use morphology::{binary_closing, binary_dilation, binary_erosion, binary_opening};
28pub use pad::{pad, pad_to, PadMode};
29
30/// 3D mask
31pub type Mask = Array3<bool>;
32
33/// 3D common kernels. Also called Structuring Element.
34#[derive(Clone, Debug, PartialEq)]
35pub enum Kernel3d {
36    /// Diamond/star kernel (center and sides).
37    ///
38    /// Equivalent to SciPy `generate_binary_structure(3, 1)`.
39    Star,
40    /// Ball kernel (center and sides).
41    ///
42    /// Equivalent to SciPy `generate_binary_structure(3, 2)`.
43    Ball,
44    /// 3x3x3 cube.
45    ///
46    /// Equivalent to SciPy `generate_binary_structure(3, 3)`.
47    Full,
48}
49
50impl Kernel3d {
51    /// Generate a binary 3D kernel.
52    pub fn generate(&self) -> Array3<bool> {
53        match self {
54            Kernel3d::Star => arr3(&[
55                [[false, false, false], [false, true, false], [false, false, false]],
56                [[false, true, false], [true, true, true], [false, true, false]],
57                [[false, false, false], [false, true, false], [false, false, false]],
58            ]),
59            Kernel3d::Ball => arr3(&[
60                [[false, true, false], [true, true, true], [false, true, false]],
61                [[true, true, true], [true, true, true], [true, true, true]],
62                [[false, true, false], [true, true, true], [false, true, false]],
63            ]),
64            Kernel3d::Full => Array3::from_elem((3, 3, 3), true),
65        }
66    }
67}
68
69/// Utility function that returns a new *n*-dimensional array of dimension `shape` with the same
70/// datatype and memory order as the input `arr`.
71pub fn array_like<S, A, D, Sh>(arr: &ArrayBase<S, D>, shape: Sh, elem: A) -> Array<A, D>
72where
73    S: Data<Elem = A>,
74    A: Clone,
75    D: Dimension,
76    Sh: ShapeBuilder<Dim = D>,
77{
78    // TODO `is_standard_layout` only works on owned arrays. Change it if using `ArrayBase`.
79    if arr.is_standard_layout() {
80        Array::from_elem(shape, elem)
81    } else {
82        Array::from_elem(shape.f(), elem)
83    }
84}
85
86/// Utilitary function that returns the mask dimension minus 1 on all dimensions.
87pub fn dim_minus<S, A>(mask: &ArrayBase<S, Ix3>, n: usize) -> (usize, usize, usize)
88where
89    S: Data<Elem = A>,
90    A: Clone,
91{
92    let (width, height, depth) = mask.dim();
93    (width - n, height - n, depth - n)
94}
95
96// TODO Use x.round_ties_even() when available on stable
97// https://github.com/rust-lang/rust/issues/96710
98fn round_ties_even(x: f64) -> f64 {
99    let i = x as i32;
100    let f = (x - i as f64).abs();
101    if f == 0.5 {
102        if i & 1 == 1 {
103            // -1.5, 1.5, 3.5, ...
104            (x.abs() + 0.5).copysign(x)
105        } else {
106            (x.abs() - 0.5).copysign(x)
107        }
108    } else {
109        x.round()
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use crate::round_ties_even;
116
117    #[test]
118    fn test_round_ties_even() {
119        assert_eq!(round_ties_even(-2.51), -3.0);
120        assert_eq!(round_ties_even(-2.5), -2.0);
121        assert_eq!(round_ties_even(-1.5), -2.0);
122        assert_eq!(round_ties_even(-0.5), -0.0);
123        assert_eq!(round_ties_even(-0.1), 0.0);
124        assert_eq!(round_ties_even(-0.0), 0.0);
125        assert_eq!(round_ties_even(0.0), 0.0);
126        assert_eq!(round_ties_even(0.1), 0.0);
127        assert_eq!(round_ties_even(0.5), 0.0);
128        assert_eq!(round_ties_even(1.5), 2.0);
129        assert_eq!(round_ties_even(2.5), 2.0);
130        assert_eq!(round_ties_even(2.51), 3.0);
131    }
132}