image2/
kernel.rs

1use std::f64;
2use std::ops;
3
4use crate::*;
5
6/// 2-dimensional convolution kernel
7#[derive(Debug, Clone, PartialEq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct Kernel {
10    rows: usize,
11    cols: usize,
12    data: Vec<Vec<f64>>,
13}
14
15impl From<Vec<Vec<f64>>> for Kernel {
16    fn from(data: Vec<Vec<f64>>) -> Kernel {
17        let rows = data.len();
18        let cols = data[0].len();
19        Kernel { data, rows, cols }
20    }
21}
22
23impl<'a> From<&'a [&'a [f64]]> for Kernel {
24    fn from(data: &'a [&'a [f64]]) -> Kernel {
25        let rows = data.len();
26        let cols = data[0].len();
27        let mut v = Vec::new();
28        for d in data {
29            v.push(Vec::from(*d))
30        }
31        Kernel {
32            data: v,
33            rows,
34            cols,
35        }
36    }
37}
38
39impl<const N: usize> From<[[f64; N]; N]> for Kernel {
40    fn from(data: [[f64; N]; N]) -> Kernel {
41        let data = data.iter().map(|d| d.to_vec()).collect();
42        Kernel {
43            data,
44            rows: N,
45            cols: N,
46        }
47    }
48}
49
50impl<T: Type, C: Color, U: Type, D: Color> Filter<T, C, U, D> for Kernel {
51    fn schedule(&self) -> Schedule {
52        Schedule::Image
53    }
54
55    fn compute_at(&self, pt: Point, input: &Input<T, C>, dest: &mut DataMut<U, D>) {
56        let r2 = (self.rows / 2) as isize;
57        let c2 = (self.cols / 2) as isize;
58        let mut f = input.new_pixel();
59        let mut x: f64;
60        for ky in -r2..=r2 {
61            let kr = &self.data[(ky + r2) as usize];
62            let pty = (pt.y as isize + ky) as usize;
63            for kx in -c2..=c2 {
64                let krc = kr[(kx + c2) as usize];
65                for c in 0..f.len() {
66                    x = input.get_f(((pt.x as isize + kx) as usize, pty), c, Some(0));
67                    f[c] += x * krc;
68                }
69            }
70        }
71        f.copy_to_slice(dest);
72    }
73}
74
75impl Kernel {
76    /// Create a new kernel with the given number of rows and columns
77    pub fn new(rows: usize, cols: usize) -> Kernel {
78        let data = vec![vec![0.0; cols]; rows];
79        Kernel { data, rows, cols }
80    }
81
82    /// Create a new, square kernel
83    pub fn square(x: usize) -> Kernel {
84        Self::new(x, x)
85    }
86
87    /// Ensures the sum of the kernel is <= 1
88    pub fn normalize(&mut self) {
89        let sum: f64 = self.data.iter().map(|x| -> f64 { x.iter().sum() }).sum();
90        if sum == 0.0 {
91            return;
92        }
93
94        for j in 0..self.rows {
95            for i in 0..self.cols {
96                self.data[j][i] /= sum
97            }
98        }
99    }
100
101    /// Create a new kernel and fill it by executing `f` with each possible (row, col) pair
102    pub fn create<F: Fn(usize, usize) -> f64>(rows: usize, cols: usize, f: F) -> Kernel {
103        let mut k = Self::new(rows, cols);
104        for j in 0..rows {
105            let d = &mut k.data[j];
106            for (i, item) in d.iter_mut().enumerate() {
107                *item = f(i, j);
108            }
109        }
110        k
111    }
112
113    /// Generate gaussian blur kernel
114    pub fn gaussian(n: usize, std: f64) -> Kernel {
115        assert!(n % 2 != 0);
116        let std2 = std * std;
117        let a = 1.0 / (2.0 * f64::consts::PI * std2);
118        let mut k = Kernel::create(n, n, |i, j| {
119            let x = (i * i + j * j) as f64 / (2.0 * std2);
120            a * f64::consts::E.powf(-1.0 * x)
121        });
122        k.normalize();
123        k
124    }
125
126    /// 3x3 pixel gaussian blur
127    pub fn gaussian_3x3() -> Kernel {
128        Self::gaussian(3, 1.4)
129    }
130
131    /// 5x5 pixel gaussian blur
132    pub fn gaussian_5x5() -> Kernel {
133        Self::gaussian(5, 1.4)
134    }
135
136    /// 7x7 pixel gaussian blur
137    pub fn gaussian_7x7() -> Kernel {
138        Self::gaussian(7, 1.4)
139    }
140
141    /// 9x9 pixel gaussian blur
142    pub fn gaussian_9x9() -> Kernel {
143        Self::gaussian(9, 1.4)
144    }
145
146    /// Sobel X
147    pub fn sobel_x() -> Kernel {
148        Kernel {
149            rows: 3,
150            cols: 3,
151            data: vec![
152                vec![1.0, 0.0, -1.0],
153                vec![2.0, 0.0, -2.0],
154                vec![1.0, 0.0, -1.0],
155            ],
156        }
157    }
158
159    /// Sobel Y
160    pub fn sobel_y() -> Kernel {
161        Kernel {
162            rows: 3,
163            cols: 3,
164            data: vec![
165                vec![1.0, 2.0, 1.0],
166                vec![0.0, 0.0, 0.0],
167                vec![-1.0, -2.0, -1.0],
168            ],
169        }
170    }
171
172    /// Laplacian
173    pub fn laplacian() -> Kernel {
174        Kernel::from([[0., -1., 0.], [-1., 4., -1.], [0., -1., 0.]])
175    }
176
177    /// Sobel X and Y combined
178    pub fn sobel() -> Kernel {
179        Kernel::sobel_x() + Kernel::sobel_y()
180    }
181}
182
183impl ops::Add for Kernel {
184    type Output = Kernel;
185
186    fn add(mut self, other: Kernel) -> Kernel {
187        for i in 0..self.rows {
188            for j in 0..self.cols {
189                self.data[i][j] += other.data[i][j];
190            }
191        }
192        self
193    }
194}
195
196impl ops::Sub for Kernel {
197    type Output = Kernel;
198
199    fn sub(mut self, other: Kernel) -> Kernel {
200        for i in 0..self.rows {
201            for j in 0..self.cols {
202                self.data[i][j] -= other.data[i][j];
203            }
204        }
205        self
206    }
207}
208
209impl ops::Mul for Kernel {
210    type Output = Kernel;
211
212    fn mul(mut self, other: Kernel) -> Kernel {
213        for i in 0..self.rows {
214            for j in 0..self.cols {
215                self.data[i][j] *= other.data[i][j];
216            }
217        }
218        self
219    }
220}
221
222impl ops::Div for Kernel {
223    type Output = Kernel;
224
225    fn div(mut self, other: Kernel) -> Kernel {
226        for i in 0..self.rows {
227            for j in 0..self.cols {
228                self.data[i][j] /= other.data[i][j];
229            }
230        }
231        self
232    }
233}