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