Skip to main content

libblur/
lib.rs

1// Copyright (c) Radzivon Bartoshyk. All rights reserved.
2
3//
4// Redistribution and use in source and binary forms, with or without modification,
5// are permitted provided that the following conditions are met:
6//
7// 1.  Redistributions of source code must retain the above copyright notice, this
8// list of conditions and the following disclaimer.
9//
10// 2.  Redistributions in binary form must reproduce the above copyright notice,
11// this list of conditions and the following disclaimer in the documentation
12// and/or other materials provided with the distribution.
13//
14// 3.  Neither the name of the copyright holder nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28#![allow(
29    clippy::too_many_arguments,
30    clippy::int_plus_one,
31    stable_features,
32    unused_features
33)]
34#![cfg_attr(docsrs, feature(doc_cfg))]
35#![cfg_attr(
36    all(feature = "nightly_fcma", target_arch = "aarch64"),
37    feature(stdarch_neon_fcma)
38)]
39#![cfg_attr(
40    all(
41        feature = "nightly_avx512",
42        any(target_arch = "x86", target_arch = "x86_64")
43    ),
44    feature(cfg_version)
45)]
46#![cfg_attr(
47    all(
48        feature = "nightly_avx512",
49        any(target_arch = "x86", target_arch = "x86_64")
50    ),
51    feature(avx512_target_feature)
52)]
53#![cfg_attr(
54    all(
55        feature = "nightly_avx512",
56        any(target_arch = "x86", target_arch = "x86_64")
57    ),
58    feature(stdarch_x86_avx512)
59)]
60#![cfg_attr(
61    all(
62        feature = "nightly_avx512",
63        any(target_arch = "x86", target_arch = "x86_64")
64    ),
65    feature(x86_amx_intrinsics)
66)]
67#![cfg_attr(feature = "nightly_f16", feature(f16))]
68#![deny(
69    clippy::print_stdout,
70    clippy::print_stderr,
71    clippy::print_literal,
72    clippy::print_in_format_impl
73)]
74#[cfg(feature = "fft")]
75#[cfg_attr(docsrs, doc(cfg(feature = "fft")))]
76mod adaptive_blur;
77#[cfg(all(target_arch = "x86_64", feature = "avx"))]
78mod avx;
79mod bilateral;
80mod box_filter;
81mod channels_configuration;
82mod edge_mode;
83mod fast_bilateral_filter;
84#[cfg(feature = "image")]
85#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
86mod fast_bilateral_image;
87mod fast_divide;
88mod fast_gaussian;
89#[cfg(feature = "image")]
90#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
91mod fast_gaussian_image;
92#[cfg(feature = "image")]
93#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
94mod fast_gaussian_image_next;
95mod fast_gaussian_next;
96mod filter1d;
97mod filter2d;
98mod gamma_curves;
99mod gaussian;
100#[cfg(feature = "image")]
101#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
102mod gaussian_blur_image;
103mod image;
104mod image_linearization;
105mod img_size;
106mod laplacian;
107mod lens;
108mod median_blur;
109mod mlaf;
110mod motion_blur;
111#[cfg(all(target_arch = "aarch64", feature = "neon"))]
112mod neon;
113mod primitives;
114mod safe_math;
115mod sobel;
116#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
117mod sse;
118#[cfg(feature = "image")]
119#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
120mod stack_blur_image;
121mod stackblur;
122mod threading_policy;
123mod to_storage;
124mod unsafe_slice;
125mod util;
126#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
127mod wasm32;
128
129#[cfg(feature = "fft")]
130#[cfg_attr(docsrs, doc(cfg(feature = "fft")))]
131pub use adaptive_blur::adaptive_blur;
132pub use bilateral::{bilateral_filter, BilateralBlurParams};
133pub use box_filter::{
134    box_blur, box_blur_f32, box_blur_u16, gaussian_box_blur, gaussian_box_blur_f32,
135    gaussian_box_blur_u16, tent_blur, tent_blur_f32, tent_blur_u16, BoxBlurParameters,
136    CLTParameters,
137};
138pub use channels_configuration::FastBlurChannels;
139pub use edge_mode::{BorderHandle, EdgeMode, EdgeMode2D, Scalar};
140pub use fast_bilateral_filter::{
141    fast_bilateral_filter, fast_bilateral_filter_f32, fast_bilateral_filter_u16,
142};
143#[cfg(feature = "image")]
144#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
145pub use fast_bilateral_image::fast_bilateral_filter_image;
146#[cfg(feature = "nightly_f16")]
147pub use fast_gaussian::fast_gaussian_f16;
148pub use fast_gaussian::{fast_gaussian, fast_gaussian_f32, fast_gaussian_u16};
149#[cfg(feature = "image")]
150#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
151pub use fast_gaussian_image::fast_gaussian_blur_image;
152#[cfg(feature = "image")]
153#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
154pub use fast_gaussian_image_next::fast_gaussian_next_blur_image;
155#[cfg(feature = "nightly_f16")]
156pub use fast_gaussian_next::fast_gaussian_next_f16;
157pub use fast_gaussian_next::{fast_gaussian_next, fast_gaussian_next_f32, fast_gaussian_next_u16};
158pub use filter1d::{
159    filter_1d_approx, filter_1d_complex, filter_1d_complex_fixed_point, filter_1d_exact,
160    KernelShape,
161};
162#[cfg(feature = "fft")]
163#[cfg_attr(docsrs, doc(cfg(feature = "fft")))]
164pub use filter2d::{
165    fft_next_good_size, filter_2d_fft, filter_2d_fft_complex, filter_2d_rgb_fft,
166    filter_2d_rgb_fft_complex, filter_2d_rgba_fft, filter_2d_rgba_fft_complex, FftNumber,
167};
168pub use filter2d::{filter_2d, filter_2d_arbitrary, filter_2d_rgb, filter_2d_rgba};
169pub use gamma_curves::TransferFunction;
170#[cfg(feature = "nightly_f16")]
171pub use gaussian::gaussian_blur_f16;
172pub use gaussian::{
173    complex_gaussian_kernel, gaussian_blur, gaussian_blur_f32, gaussian_blur_u16,
174    gaussian_kernel_1d, gaussian_kernel_1d_f64, sigma_size, sigma_size_d, ConvolutionMode,
175    GaussianBlurParams, IeeeBinaryConvolutionMode,
176};
177#[cfg(feature = "image")]
178#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
179pub use gaussian_blur_image::gaussian_blur_image;
180pub use image::{BlurImage, BlurImageMut, BufferStore};
181pub use img_size::ImageSize;
182pub use laplacian::{laplacian, laplacian_kernel};
183pub use lens::lens_kernel;
184pub use median_blur::median_blur;
185pub use motion_blur::{generate_motion_kernel, motion_blur};
186pub use sobel::sobel;
187#[cfg(feature = "image")]
188#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
189pub use stack_blur_image::stack_blur_image;
190pub use stackblur::stack_blur::stack_blur;
191#[cfg(feature = "nightly_f16")]
192pub use stackblur::stack_blur_f16::stack_blur_f16;
193pub use stackblur::stack_blur_f32::stack_blur_f32;
194pub use stackblur::stack_blur_u16;
195pub use threading_policy::ThreadingPolicy;
196pub use util::{BlurError, MismatchedSize};
197
198/// Asymmetric radius container
199#[derive(Copy, Clone, Default, PartialOrd, PartialEq, Debug)]
200pub struct AnisotropicRadius {
201    pub x_axis: u32,
202    pub y_axis: u32,
203}
204
205impl AnisotropicRadius {
206    pub fn new(radius: u32) -> AnisotropicRadius {
207        AnisotropicRadius {
208            x_axis: radius,
209            y_axis: radius,
210        }
211    }
212
213    pub fn create(x: u32, y: u32) -> AnisotropicRadius {
214        AnisotropicRadius {
215            x_axis: x,
216            y_axis: y,
217        }
218    }
219
220    pub fn clamp(&self, min: u32, max: u32) -> AnisotropicRadius {
221        AnisotropicRadius {
222            x_axis: self.x_axis.clamp(min, max),
223            y_axis: self.y_axis.clamp(min, max),
224        }
225    }
226
227    pub fn max(&self, max: u32) -> AnisotropicRadius {
228        AnisotropicRadius {
229            x_axis: self.x_axis.max(max),
230            y_axis: self.y_axis.max(max),
231        }
232    }
233}
234
235#[cfg(test)]
236mod tests {
237
238    #[cfg(feature = "fft")]
239    fn gaussian_kernel_9x9(sigma: f32) -> Vec<f32> {
240        let mut kernel = [[0.0f32; 9]; 9];
241        let mut sum = 0.0;
242
243        let radius = 4; // (9 - 1) / 2
244
245        let two_sigma_sq = 2.0 * sigma * sigma;
246        let norm = 1.0 / (std::f32::consts::PI * two_sigma_sq);
247
248        for y in -radius..=radius {
249            for x in -radius..=radius {
250                let value = norm * ((-(x * x + y * y) as f32) / two_sigma_sq).exp();
251                kernel[(y + radius) as usize][(x + radius) as usize] = value;
252                sum += value;
253            }
254        }
255
256        // Normalize
257        for row in &mut kernel {
258            for v in row.iter_mut() {
259                *v /= sum;
260            }
261        }
262
263        kernel
264            .iter()
265            .flat_map(|x| x)
266            .map(|&x| x)
267            .collect::<Vec<f32>>()
268    }
269
270    #[cfg(feature = "fft")]
271    #[test]
272    fn test_fft_rgb() {
273        use super::*;
274        let width: usize = 188;
275        let height: usize = 188;
276        let src = vec![126u8; width * height * 3];
277        let src_image = BlurImage::borrow(
278            &src,
279            width as u32,
280            height as u32,
281            FastBlurChannels::Channels3,
282        );
283        let mut dst = BlurImageMut::default();
284
285        let kernel = gaussian_kernel_9x9(3.);
286
287        filter_2d_rgb_fft::<u8, f32>(
288            &src_image,
289            &mut dst,
290            &kernel,
291            KernelShape::new(9, 9),
292            EdgeMode::Clamp.as_2d(),
293            Scalar::default(),
294            ThreadingPolicy::Single,
295        )
296        .unwrap();
297        for (i, &cn) in dst.data.borrow_mut().iter().enumerate() {
298            let diff = (cn as i32 - 126).abs();
299            assert!(
300                diff <= 3,
301                "Diff expected to be less than 3 but it was {diff} at {i}"
302            );
303        }
304    }
305}