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(clippy::too_many_arguments, clippy::int_plus_one, stable_features)]
29#![cfg_attr(docsrs, feature(doc_cfg))]
30#![cfg_attr(
31    all(feature = "nightly_fcma", target_arch = "aarch64"),
32    feature(stdarch_neon_fcma)
33)]
34#![cfg_attr(
35    all(
36        feature = "nightly_avx512",
37        any(target_arch = "x86", target_arch = "x86_64")
38    ),
39    feature(cfg_version)
40)]
41#![cfg_attr(
42    all(
43        feature = "nightly_avx512",
44        any(target_arch = "x86", target_arch = "x86_64")
45    ),
46    feature(avx512_target_feature)
47)]
48#![cfg_attr(
49    all(
50        feature = "nightly_avx512",
51        any(target_arch = "x86", target_arch = "x86_64")
52    ),
53    feature(stdarch_x86_avx512)
54)]
55#![cfg_attr(
56    all(
57        feature = "nightly_avx512",
58        any(target_arch = "x86", target_arch = "x86_64")
59    ),
60    feature(x86_amx_intrinsics)
61)]
62#![cfg_attr(feature = "nightly_f16", feature(f16))]
63#![deny(
64    clippy::print_stdout,
65    clippy::print_stderr,
66    clippy::print_literal,
67    clippy::print_in_format_impl
68)]
69#[cfg(feature = "fft")]
70#[cfg_attr(docsrs, doc(cfg(feature = "fft")))]
71mod adaptive_blur;
72#[cfg(all(target_arch = "x86_64", feature = "avx"))]
73mod avx;
74mod bilateral;
75mod box_filter;
76mod channels_configuration;
77mod edge_mode;
78mod fast_bilateral_filter;
79#[cfg(feature = "image")]
80#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
81mod fast_bilateral_image;
82mod fast_divide;
83mod fast_gaussian;
84#[cfg(feature = "image")]
85#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
86mod fast_gaussian_image;
87#[cfg(feature = "image")]
88#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
89mod fast_gaussian_image_next;
90mod fast_gaussian_next;
91mod filter1d;
92mod filter2d;
93mod gamma_curves;
94mod gaussian;
95#[cfg(feature = "image")]
96#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
97mod gaussian_blur_image;
98mod image;
99mod image_linearization;
100mod img_size;
101mod laplacian;
102mod lens;
103mod median_blur;
104mod mlaf;
105mod motion_blur;
106#[cfg(all(target_arch = "aarch64", feature = "neon"))]
107mod neon;
108mod primitives;
109mod safe_math;
110mod sobel;
111#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
112mod sse;
113#[cfg(feature = "image")]
114#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
115mod stack_blur_image;
116mod stackblur;
117mod threading_policy;
118mod to_storage;
119mod unsafe_slice;
120mod util;
121#[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
122mod wasm32;
123
124#[cfg(feature = "fft")]
125#[cfg_attr(docsrs, doc(cfg(feature = "fft")))]
126pub use adaptive_blur::adaptive_blur;
127pub use bilateral::{bilateral_filter, BilateralBlurParams};
128pub use box_filter::{
129    box_blur, box_blur_f32, box_blur_u16, gaussian_box_blur, gaussian_box_blur_f32,
130    gaussian_box_blur_u16, tent_blur, tent_blur_f32, tent_blur_u16, BoxBlurParameters,
131    CLTParameters,
132};
133pub use channels_configuration::FastBlurChannels;
134pub use edge_mode::{BorderHandle, EdgeMode, EdgeMode2D, Scalar};
135pub use fast_bilateral_filter::{
136    fast_bilateral_filter, fast_bilateral_filter_f32, fast_bilateral_filter_u16,
137};
138#[cfg(feature = "image")]
139#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
140pub use fast_bilateral_image::fast_bilateral_filter_image;
141#[cfg(feature = "nightly_f16")]
142pub use fast_gaussian::fast_gaussian_f16;
143pub use fast_gaussian::{fast_gaussian, fast_gaussian_f32, fast_gaussian_u16};
144#[cfg(feature = "image")]
145#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
146pub use fast_gaussian_image::fast_gaussian_blur_image;
147#[cfg(feature = "image")]
148#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
149pub use fast_gaussian_image_next::fast_gaussian_next_blur_image;
150#[cfg(feature = "nightly_f16")]
151pub use fast_gaussian_next::fast_gaussian_next_f16;
152pub use fast_gaussian_next::{fast_gaussian_next, fast_gaussian_next_f32, fast_gaussian_next_u16};
153pub use filter1d::{
154    filter_1d_approx, filter_1d_complex, filter_1d_complex_fixed_point, filter_1d_exact,
155    KernelShape,
156};
157#[cfg(feature = "fft")]
158#[cfg_attr(docsrs, doc(cfg(feature = "fft")))]
159pub use filter2d::{
160    fft_next_good_size, filter_2d_fft, filter_2d_fft_complex, filter_2d_rgb_fft,
161    filter_2d_rgb_fft_complex, filter_2d_rgba_fft, filter_2d_rgba_fft_complex, FftNumber,
162};
163pub use filter2d::{filter_2d, filter_2d_arbitrary, filter_2d_rgb, filter_2d_rgba};
164pub use gamma_curves::TransferFunction;
165#[cfg(feature = "nightly_f16")]
166pub use gaussian::gaussian_blur_f16;
167pub use gaussian::{
168    complex_gaussian_kernel, gaussian_blur, gaussian_blur_f32, gaussian_blur_u16,
169    gaussian_kernel_1d, gaussian_kernel_1d_f64, sigma_size, sigma_size_d, ConvolutionMode,
170    GaussianBlurParams, IeeeBinaryConvolutionMode,
171};
172#[cfg(feature = "image")]
173#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
174pub use gaussian_blur_image::gaussian_blur_image;
175pub use image::{BlurImage, BlurImageMut, BufferStore};
176pub use img_size::ImageSize;
177pub use laplacian::{laplacian, laplacian_kernel};
178pub use lens::lens_kernel;
179pub use median_blur::median_blur;
180pub use motion_blur::{generate_motion_kernel, motion_blur};
181pub use sobel::sobel;
182#[cfg(feature = "image")]
183#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
184pub use stack_blur_image::stack_blur_image;
185pub use stackblur::stack_blur::stack_blur;
186#[cfg(feature = "nightly_f16")]
187pub use stackblur::stack_blur_f16::stack_blur_f16;
188pub use stackblur::stack_blur_f32::stack_blur_f32;
189pub use stackblur::stack_blur_u16;
190pub use threading_policy::ThreadingPolicy;
191pub use util::{BlurError, MismatchedSize};
192
193/// Asymmetric radius container
194#[derive(Copy, Clone, Default, PartialOrd, PartialEq, Debug)]
195pub struct AnisotropicRadius {
196    pub x_axis: u32,
197    pub y_axis: u32,
198}
199
200impl AnisotropicRadius {
201    pub fn new(radius: u32) -> AnisotropicRadius {
202        AnisotropicRadius {
203            x_axis: radius,
204            y_axis: radius,
205        }
206    }
207
208    pub fn create(x: u32, y: u32) -> AnisotropicRadius {
209        AnisotropicRadius {
210            x_axis: x,
211            y_axis: y,
212        }
213    }
214
215    pub fn clamp(&self, min: u32, max: u32) -> AnisotropicRadius {
216        AnisotropicRadius {
217            x_axis: self.x_axis.clamp(min, max),
218            y_axis: self.y_axis.clamp(min, max),
219        }
220    }
221
222    pub fn max(&self, max: u32) -> AnisotropicRadius {
223        AnisotropicRadius {
224            x_axis: self.x_axis.max(max),
225            y_axis: self.y_axis.max(max),
226        }
227    }
228}
229
230#[cfg(test)]
231mod tests {
232
233    #[cfg(feature = "fft")]
234    fn gaussian_kernel_9x9(sigma: f32) -> Vec<f32> {
235        let mut kernel = [[0.0f32; 9]; 9];
236        let mut sum = 0.0;
237
238        let radius = 4; // (9 - 1) / 2
239
240        let two_sigma_sq = 2.0 * sigma * sigma;
241        let norm = 1.0 / (std::f32::consts::PI * two_sigma_sq);
242
243        for y in -radius..=radius {
244            for x in -radius..=radius {
245                let value = norm * ((-(x * x + y * y) as f32) / two_sigma_sq).exp();
246                kernel[(y + radius) as usize][(x + radius) as usize] = value;
247                sum += value;
248            }
249        }
250
251        // Normalize
252        for row in &mut kernel {
253            for v in row.iter_mut() {
254                *v /= sum;
255            }
256        }
257
258        kernel
259            .iter()
260            .flat_map(|x| x)
261            .map(|&x| x)
262            .collect::<Vec<f32>>()
263    }
264
265    #[cfg(feature = "fft")]
266    #[test]
267    fn test_fft_rgb() {
268        use super::*;
269        let width: usize = 188;
270        let height: usize = 188;
271        let src = vec![126u8; width * height * 3];
272        let src_image = BlurImage::borrow(
273            &src,
274            width as u32,
275            height as u32,
276            FastBlurChannels::Channels3,
277        );
278        let mut dst = BlurImageMut::default();
279
280        let kernel = gaussian_kernel_9x9(3.);
281
282        filter_2d_rgb_fft::<u8, f32>(
283            &src_image,
284            &mut dst,
285            &kernel,
286            KernelShape::new(9, 9),
287            EdgeMode::Clamp.as_2d(),
288            Scalar::default(),
289            ThreadingPolicy::Single,
290        )
291        .unwrap();
292        for (i, &cn) in dst.data.borrow_mut().iter().enumerate() {
293            let diff = (cn as i32 - 126).abs();
294            assert!(
295                diff <= 3,
296                "Diff expected to be less than 3 but it was {diff} at {i}"
297            );
298        }
299    }
300}