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