Skip to main content

iris/morphology/
ops.rs

1use crate::core::types::Size;
2use crate::error::Result;
3use crate::image::Image;
4use burn::tensor::backend::Backend;
5
6/// Types of morphological operations.
7#[derive(Clone, Copy, Debug, PartialEq)]
8pub enum MorphOp {
9    Opening,
10    Closing,
11    Gradient,
12    TopHat,
13    BlackHat,
14}
15
16/// Shapes of structuring elements.
17#[derive(Clone, Copy, Debug, PartialEq)]
18pub enum MorphShape {
19    Rect,
20    Cross,
21    Ellipse,
22}
23
24impl<B: Backend> Image<B> {
25    /// Performs advanced morphological transformations.
26    pub fn morphology_ex(&self, op: MorphOp, kernel_size: usize) -> Result<Self> {
27        match op {
28            MorphOp::Opening => self.clone().morph_open(kernel_size),
29            MorphOp::Closing => self.clone().morph_close(kernel_size),
30            MorphOp::Gradient => {
31                let dilated = self.clone().dilate(kernel_size)?;
32                let eroded = self.clone().erode(kernel_size)?;
33                dilated.subtract(&eroded)
34            }
35            MorphOp::TopHat => {
36                let opened = self.clone().morph_open(kernel_size)?;
37                self.subtract(&opened)
38            }
39            MorphOp::BlackHat => {
40                let closed = self.clone().morph_close(kernel_size)?;
41                closed.subtract(self)
42            }
43        }
44    }
45}
46
47pub struct Morphology;
48
49impl Morphology {
50    /// Creates a structuring element (kernel matrix) for morphology.
51    #[must_use]
52    pub fn get_structuring_element(shape: MorphShape, ksize: Size<usize>) -> Vec<Vec<u8>> {
53        let w = ksize.width;
54        let h = ksize.height;
55        let mut element = vec![vec![0; w]; h];
56
57        let xc = (w / 2) as f64;
58        let yc = (h / 2) as f64;
59
60        for (y, row) in element.iter_mut().enumerate() {
61            for (x, val) in row.iter_mut().enumerate() {
62                *val = match shape {
63                    MorphShape::Rect => 1,
64                    MorphShape::Cross => u8::from(x == w / 2 || y == h / 2),
65                    MorphShape::Ellipse => {
66                        let dx = x as f64 - xc;
67                        let dy = y as f64 - yc;
68                        let r_x = w as f64 / 2.0;
69                        let r_y = h as f64 / 2.0;
70
71                        if r_x > 0.0 && r_y > 0.0 {
72                            let term = (dx * dx) / (r_x * r_x) + (dy * dy) / (r_y * r_y);
73                            u8::from(term <= 1.05)
74                        } else {
75                            1
76                        }
77                    }
78                };
79            }
80        }
81
82        element
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn test_structuring_elements() {
92        let size = Size::new(5, 5);
93        let rect = Morphology::get_structuring_element(MorphShape::Rect, size);
94        assert_eq!(rect[0][0], 1);
95
96        let cross = Morphology::get_structuring_element(MorphShape::Cross, size);
97        assert_eq!(cross[0][0], 0);
98        assert_eq!(cross[2][2], 1);
99
100        let ellipse = Morphology::get_structuring_element(MorphShape::Ellipse, size);
101        assert_eq!(ellipse[0][0], 0);
102    }
103}