1#![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#[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; 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 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}