imgproc_rs/filter/
mod.rs

1//! A module for image filtering operations
2
3pub use self::bilateral::*;
4pub use self::edge::*;
5pub use self::median::*;
6
7mod median;
8mod bilateral;
9mod edge;
10
11use crate::{error, util};
12use crate::enums::Thresh;
13use crate::error::ImgProcResult;
14use crate::image::{BaseImage, Image, Number};
15use crate::util::constants::{K_SHARPEN, K_UNSHARP_MASKING};
16
17#[cfg(feature = "rayon")]
18use rayon::prelude::*;
19
20/////////////////////
21// Linear filtering
22/////////////////////
23
24/// Applies a 1D filter. If `is_vert` is true, applies `kernel`
25/// as a vertical filter; otherwise applies `kernel` as a horizontal filter
26#[cfg(not(feature = "rayon"))]
27pub fn filter_1d(input: &Image<f32>, kernel: &[f32], is_vert: bool) -> ImgProcResult<Image<f32>> {
28    error::check_odd(kernel.len(), "kernel length")?;
29
30    let (width, height, channels) = input.info().whc();
31    let mut output = Image::blank(input.info());
32    let mut p_out = Vec::with_capacity(channels as usize);
33
34    for y in 0..height {
35        for x in 0..width {
36            util::apply_1d_kernel(&input.get_neighborhood_1d(x, y, kernel.len() as u32, is_vert),
37                                              &mut p_out, kernel)?;
38            output.set_pixel(x, y, &p_out);
39        }
40    }
41
42    Ok(output)
43}
44
45/// Applies a 1D filter. If `is_vert` is true, applies `kernel`
46/// as a vertical filter; otherwise applies `kernel` as a horizontal filter
47#[cfg(feature = "rayon")]
48pub fn filter_1d(input: &Image<f32>, kernel: &[f32], is_vert: bool) -> ImgProcResult<Image<f32>> {
49    error::check_odd(kernel.len(), "kernel length")?;
50
51    let (width, height, channels, alpha) = input.info().whca();
52
53    let data: Vec<Vec<f32>> = (0..input.info().size())
54        .into_par_iter()
55        .map(|i| {
56            let (x, y) = util::get_2d_coords(i, width);
57            util::apply_1d_kernel(&input.get_neighborhood_1d(x, y,kernel.len() as u32, is_vert), kernel).unwrap()
58        })
59        .collect();
60
61    Ok(Image::from_vec_of_vec(width, height, channels, alpha, data))
62}
63
64/// Applies a separable linear filter by first applying `vert_kernel` and then `horz_kernel`
65pub fn separable_filter(input: &Image<f32>, vert_kernel: &[f32], horz_kernel: &[f32]) -> ImgProcResult<Image<f32>> {
66    error::check_odd(vert_kernel.len(), "vert_kernel length")?;
67    error::check_odd(horz_kernel.len(), "horz_kernel length")?;
68    error::check_equal(vert_kernel.len(), horz_kernel.len(), "kernel lengths")?;
69
70    let vertical = filter_1d(input, vert_kernel, true)?;
71    Ok(filter_1d(&vertical, horz_kernel, false)?)
72}
73
74/// Applies an unseparable linear filter
75#[cfg(not(feature = "rayon"))]
76pub fn unseparable_filter(input: &Image<f32>, kernel: &[f32]) -> ImgProcResult<Image<f32>> {
77    error::check_odd(kernel.len(), "kernel length")?;
78    error::check_square(kernel.len() as f32, "kernel length")?;
79
80    let size = (kernel.len() as f32).sqrt() as u32;
81    let (width, height, channels) = input.info().whc();
82    let mut output = Image::blank(input.info());
83    let mut p_out = Vec::with_capacity(channels as usize);
84
85    for y in 0..height {
86        for x in 0..width {
87            util::apply_2d_kernel(&input.get_neighborhood_2d(x, y, size), &mut p_out, kernel)?;
88            output.set_pixel(x, y, &p_out);
89        }
90    }
91
92    Ok(output)
93}
94
95/// Applies an unseparable linear filter
96#[cfg(feature = "rayon")]
97pub fn unseparable_filter(input: &Image<f32>, kernel: &[f32]) -> ImgProcResult<Image<f32>> {
98    error::check_odd(kernel.len(), "kernel length")?;
99    error::check_square(kernel.len() as f32, "kernel length")?;
100
101    let size = (kernel.len() as f32).sqrt() as u32;
102    let (width, height, channels, alpha) = input.info().whca();
103
104    let data: Vec<Vec<f32>> = (0..input.info().size())
105        .into_par_iter()
106        .map(|i| {
107            let (x, y) = util::get_2d_coords(i, width);
108            util::apply_2d_kernel(&input.get_neighborhood_2d(x, y, size), kernel).unwrap()
109        })
110        .collect();
111
112    Ok(Image::from_vec_of_vec(width, height, channels, alpha, data))
113}
114
115/// Applies a linear filter using the 2D `kernel`
116pub fn linear_filter(input: &Image<f32>, kernel: &[f32]) -> ImgProcResult<Image<f32>> {
117    error::check_odd(kernel.len(), "kernel length")?;
118    error::check_square(kernel.len() as f32, "kernel length")?;
119
120    let separable = util::separate_kernel(kernel);
121    match separable {
122        Some((vert, horz)) => Ok(separable_filter(input, &vert, &horz)?),
123        None => Ok(unseparable_filter(input, &kernel)?)
124    }
125}
126
127//////////////
128// Blurring
129//////////////
130
131/// Applies a normalized box filter using a `size x size` kernel
132pub fn box_filter(input: &Image<f32>, size: u32) -> ImgProcResult<Image<f32>> {
133    error::check_odd(size, "size")?;
134
135    let len = (size * size) as usize;
136    let kernel = vec![1.0 / ((size * size) as f32); len];
137
138    Ok(separable_filter(input, &kernel, &kernel)?)
139}
140
141/// Applies a weighted average filter using a `size x size` kernel with a center weight of `weight`
142pub fn weighted_avg_filter(input: &Image<f32>, size: u32, weight: u32) -> ImgProcResult<Image<f32>> {
143    error::check_odd(size, "size")?;
144
145    let sum = (size * size) - 1 + weight;
146    let center = (size / 2) * size + (size / 2);
147    let mut kernel = vec![1.0 / (sum as f32); (size * size) as usize];
148    kernel[center as usize] = (weight as f32) / (sum as f32);
149
150    Ok(unseparable_filter(input, &kernel)?)
151}
152
153/// Applies a Gaussian blur using a `size x size` kernel
154pub fn gaussian_blur(input: &Image<f32>, size: u32, sigma: f32) -> ImgProcResult<Image<f32>> {
155    let kernel = util::generate_gaussian_kernel(size, sigma)?;
156    Ok(linear_filter(input, &kernel)?)
157}
158
159////////////////
160// Sharpening
161////////////////
162
163/// Sharpens image
164pub fn sharpen(input: &Image<f32>) -> ImgProcResult<Image<f32>> {
165    Ok(unseparable_filter(input, &K_SHARPEN)?)
166}
167
168/// Sharpens image by applying the unsharp masking kernel
169pub fn unsharp_masking(input: &Image<f32>) -> ImgProcResult<Image<f32>> {
170    Ok(unseparable_filter(input, &K_UNSHARP_MASKING)?)
171}
172
173//////////////////
174// Thresholding
175//////////////////
176
177/// Performs a thresholding operation based on `method`
178pub fn threshold(input: &Image<f32>, threshold: f32, max: f32, method: Thresh) -> ImgProcResult<Image<f32>> {
179    error::check_grayscale(input)?;
180
181    match method {
182        Thresh::Binary => {
183            Ok(input.map_channels_if_alpha(|channel| {
184                if channel > threshold {
185                    max
186                } else {
187                    0.0
188                }
189            }, |a| a))
190        },
191        Thresh::BinaryInv => {
192            Ok(input.map_channels_if_alpha(|channel| {
193                if channel > threshold {
194                    0.0
195                } else {
196                    max
197                }
198            }, |a| a))
199        },
200        Thresh::Trunc => {
201            Ok(input.map_channels_if_alpha(|channel| {
202                if channel > threshold {
203                    threshold
204                } else {
205                    channel
206                }
207            }, |a| a))
208        },
209        Thresh::ToZero => {
210            Ok(input.map_channels_if_alpha(|channel| {
211                if channel > threshold {
212                    channel
213                } else {
214                    0.0
215                }
216            }, |a| a))
217        },
218        Thresh::ToZeroInv => {
219            Ok(input.map_channels_if_alpha(|channel| {
220                if channel > threshold {
221                    0.0
222                } else {
223                    channel
224                }
225            }, |a| a))
226        },
227    }
228}
229
230//////////
231// Other
232//////////
233
234/// Returns the residual image of a filter operation
235pub fn residual<T: Number>(original: &Image<T>, filtered: &Image<T>) -> ImgProcResult<Image<T>> {
236    error::check_equal(original.info(), filtered.info(), "image dimensions")?;
237
238    let (width, height, channels, alpha) = original.info().whca();
239    let mut data = Vec::new();
240
241    for y in 0..height {
242        for x in 0..width {
243            let p_1 = original.get_pixel(x, y);
244            let p_2 = filtered.get_pixel(x, y);
245
246            for c in 0..(channels as usize) {
247                data.push(p_1[c] - p_2[c]);
248            }
249        }
250    }
251
252    Ok(Image::from_slice(width, height, channels, alpha, &data))
253}