1use crate::core::*;
2use crate::processing::*;
3use core::mem::MaybeUninit;
4use core::ops::Neg;
5use ndarray::{prelude::*, s, DataMut, OwnedRepr, Zip};
6use num_traits::{cast::FromPrimitive, real::Real, Num, NumAssignOps};
7use std::marker::Sized;
8
9pub trait SobelExt
11where
12 Self: Sized,
13{
14 type Output;
16 fn apply_sobel(&self) -> Result<Self::Output, Error>;
18
19 fn full_sobel(&self) -> Result<(Self::Output, Self::Output), Error>;
22}
23
24fn get_edge_images<T, U>(mat: &ArrayBase<U, Ix3>) -> Result<(Array3<T>, Array3<T>), Error>
25where
26 U: DataMut<Elem = T>,
27 T: Copy + Clone + Num + NumAssignOps + Neg<Output = T> + FromPrimitive + Real,
28{
29 let v_temp: Array3<T> = SobelFilter::build_with_params(Orientation::Vertical).unwrap();
30 let h_temp: Array3<T> = SobelFilter::build_with_params(Orientation::Horizontal).unwrap();
31 let shape = (v_temp.shape()[0], v_temp.shape()[1], mat.shape()[2]);
32 let mut h_kernel = Array3::<T>::uninit(shape);
33 let mut v_kernel = Array3::<T>::uninit(shape);
34 for i in 0..mat.dim().2 {
35 h_temp
36 .slice(s![.., .., 0])
37 .assign_to(h_kernel.slice_mut(s![.., .., i]));
38 v_temp
39 .slice(s![.., .., 0])
40 .assign_to(v_kernel.slice_mut(s![.., .., i]));
41 }
42 let h_kernel = unsafe { h_kernel.assume_init() };
43 let v_kernel = unsafe { v_kernel.assume_init() };
44 let h_deriv = mat.conv2d(h_kernel.view())?;
45 let v_deriv = mat.conv2d(v_kernel.view())?;
46
47 Ok((h_deriv, v_deriv))
48}
49
50impl<T, U> SobelExt for ArrayBase<U, Ix3>
51where
52 U: DataMut<Elem = T>,
53 T: Copy + Clone + Num + NumAssignOps + Neg<Output = T> + FromPrimitive + Real,
54{
55 type Output = ArrayBase<OwnedRepr<T>, Ix3>;
56
57 fn apply_sobel(&self) -> Result<Self::Output, Error> {
58 let (h_deriv, v_deriv) = get_edge_images(self)?;
59 let res_shape = h_deriv.dim();
60 let mut result = Self::Output::uninit(res_shape);
61 for r in 0..res_shape.0 {
62 for c in 0..res_shape.1 {
63 for channel in 0..res_shape.2 {
64 let temp = (h_deriv[[r, c, channel]].powi(2)
65 + v_deriv[[r, c, channel]].powi(2))
66 .sqrt();
67 unsafe {
68 *result.uget_mut([r, c, channel]) = MaybeUninit::new(temp);
69 }
70 }
71 }
72 }
73 Ok(unsafe { result.assume_init() })
74 }
75
76 fn full_sobel(&self) -> Result<(Self::Output, Self::Output), Error> {
77 let (h_deriv, v_deriv) = get_edge_images(self)?;
78
79 let mut magnitude = h_deriv.mapv(|x| x.powi(2)) + v_deriv.mapv(|x| x.powi(2));
80 magnitude.mapv_inplace(|x| x.sqrt());
81
82 let dim = h_deriv.dim();
83 let mut rotation = Array3::uninit((dim.0, dim.1, dim.2));
84 Zip::from(&mut rotation)
85 .and(&h_deriv)
86 .and(&v_deriv)
87 .for_each(|r, &h, &v| *r = MaybeUninit::new(h.atan2(v)));
88
89 let rotation = unsafe { rotation.assume_init() };
90
91 Ok((magnitude, rotation))
92 }
93}
94
95impl<T, U, C> SobelExt for ImageBase<U, C>
96where
97 U: DataMut<Elem = T>,
98 T: Copy + Clone + Num + NumAssignOps + Neg<Output = T> + FromPrimitive + Real,
99 C: ColourModel,
100{
101 type Output = Image<T, C>;
102
103 fn apply_sobel(&self) -> Result<Self::Output, Error> {
104 let data = self.data.apply_sobel()?;
105 Ok(Image::from_data(data))
106 }
107
108 fn full_sobel(&self) -> Result<(Self::Output, Self::Output), Error> {
109 self.data
110 .full_sobel()
111 .map(|(m, r)| (Image::from_data(m), Image::from_data(r)))
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use approx::*;
119
120 #[test]
121 fn simple() {
122 let mut image: Image<f64, Gray> = ImageBase::new(11, 11);
123 image.data.slice_mut(s![4..7, 4..7, ..]).fill(1.0);
124 image.data.slice_mut(s![3..8, 5, ..]).fill(1.0);
125 image.data.slice_mut(s![5, 3..8, ..]).fill(1.0);
126
127 let sobel = image.full_sobel().unwrap();
128
129 #[rustfmt::skip]
131 let mag = vec![0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
132 0.0, 0.0, 0.0, 1.41421356237301, 2.0, 1.41421356237301, 0.0, 0.0, 0.0,
133 0.0, 0.0, 1.41421356237301, 4.24264068711929, 4.0, 4.24264068711929, 1.4142135623731, 0.0, 0.0,
134 0.0, 1.4142135623731, 4.24264068711929, 4.24264068711929, 2.0, 4.24264068711929, 4.24264068711929, 1.4142135623731, 0.0,
135 0.0, 2.0, 4.0, 2.0, 0.0, 2.0, 4.0, 2.0, 0.0,
136 0.0, 1.4142135623731, 4.24264068711929, 4.24264068711929, 2.0, 4.24264068711929, 4.24264068711929, 1.4142135623731, 0.0,
137 0.0, 0.0, 1.4142135623731, 4.24264068711929, 4.0, 4.24264068711929, 1.4142135623731, 0.0,
138 0.0, 0.0, 0.0, 0.0, 1.4142135623731, 2.0, 1.4142135623731, 0.0, 0.0,
139 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
140 ];
141
142 let mag = Array::from_shape_vec((9, 9), mag).unwrap();
143
144 assert_abs_diff_eq!(sobel.0.data.slice(s![1..10, 1..10, 0]), mag, epsilon = 1e-5);
145
146 let only_mag = image.apply_sobel().unwrap();
147 assert_abs_diff_eq!(sobel.0.data, only_mag.data);
148
149 #[rustfmt::skip]
151 let rot = vec![0.00000000000000,0.00000000000000,0.00000000000000,0.00000000000000,0.00000000000000,0.00000000000000,0.00000000000000,0.00000000000000,0.00000000000000,
152 0.00000000000000,0.00000000000000,0.00000000000000,-2.35619449019234,3.14159265358979,2.35619449019234,0.00000000000000,0.00000000000000,0.00000000000000,
153 0.00000000000000,0.00000000000000,-2.35619449019234,-2.35619449019234,3.14159265358979,2.35619449019234,2.35619449019234,0.00000000000000,0.00000000000000,
154 0.00000000000000,-2.35619449019234,-2.35619449019234,-2.35619449019234,3.14159265358979,2.35619449019234,2.35619449019234,2.35619449019234,0.00000000000000,
155 0.00000000000000,-1.57079632679490,-1.57079632679490,-1.57079632679490,0.00000000000000,1.57079632679490,1.57079632679490,1.57079632679490,0.00000000000000,
156 0.00000000000000,-0.78539816339745,-0.78539816339745,-0.78539816339745,0.00000000000000,0.78539816339745,0.78539816339745,0.78539816339745,0.00000000000000,
157 0.00000000000000,0.00000000000000,-0.78539816339745,-0.78539816339745,0.00000000000000,0.78539816339745,0.78539816339745,0.00000000000000,0.00000000000000,
158 0.00000000000000,0.00000000000000,0.00000000000000,-0.78539816339745,0.00000000000000,0.78539816339745,0.00000000000000,0.00000000000000,0.00000000000000,
159 0.00000000000000,0.00000000000000,0.00000000000000,0.00000000000000,0.00000000000000,0.00000000000000,0.00000000000000,0.00000000000000,0.00000000000000];
160 let rot = Array::from_shape_vec((9, 9), rot).unwrap();
161
162 assert_abs_diff_eq!(sobel.1.data.slice(s![1..10, 1..10, 0]), rot, epsilon = 1e-5);
163 }
164}