From 4dbd79bea7aaefa4111dcf96d6b3d952fe01a53c Mon Sep 17 00:00:00 2001 From: Radzivon Bartoshyk Date: Fri, 8 May 2026 16:57:11 +0100 Subject: [PATCH] Bilateral improvements --- app/src/main.rs | 30 ++++++- src/bilateral/bp8.rs | 33 ++++--- src/bilateral/neon/bp8.rs | 8 +- src/fast_bilateral_filter.rs | 166 ++++++++--------------------------- src/fast_bilateral_image.rs | 14 --- src/filter1d/arena.rs | 16 ++-- src/filter1d/arena_roi.rs | 6 +- 7 files changed, 104 insertions(+), 169 deletions(-) diff --git a/app/src/main.rs b/app/src/main.rs index c05affaa..b7180496 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -214,14 +214,36 @@ fn main() { // ) // .unwrap(); - median_blur( + fast_bilateral_filter( &cvt.to_immutable_ref(), &mut dst_image, - 35, + 7., + 5., ThreadingPolicy::Single, ) .unwrap(); + // bilateral_filter( + // &cvt.to_immutable_ref(), + // &mut dst_image, + // BilateralBlurParams { + // kernel_size: 35, + // spatial_sigma: 7., + // range_sigma: 5., + // }, + // EdgeMode2D::new(EdgeMode::Clamp), + // Scalar::default(), + // ThreadingPolicy::Adaptive, + // ) + // .unwrap(); + // median_blur( + // &cvt.to_immutable_ref(), + // &mut dst_image, + // 35, + // ThreadingPolicy::Single, + // ) + // .unwrap(); + // tent_blur( // &cvt.to_immutable_ref(), // &mut dst_image, @@ -340,7 +362,7 @@ fn main() { if components == 3 { image::save_buffer( - "blurred_stack_next35_2.jpg", + "blurred_stack_next_fbil.jpg", bytes.as_bytes(), dimensions.0, dimensions.1, @@ -349,7 +371,7 @@ fn main() { .unwrap(); } else { image::save_buffer( - "blurred_stack_next_f1.png", + "blurred_stack_next_bil.png", bytes.as_bytes(), dimensions.0, dimensions.1, diff --git a/src/bilateral/bp8.rs b/src/bilateral/bp8.rs index 2c8991e9..da720390 100644 --- a/src/bilateral/bp8.rs +++ b/src/bilateral/bp8.rs @@ -119,8 +119,10 @@ impl BilateralUnit for ExecutionUnit4<'_> { let dst_row = &mut dst_row[..useful_width]; let src_row = &src_row[..useful_width]; for (x, (dst, center)) in dst_row - .chunks_exact_mut(N) - .zip(src_row.chunks_exact(N)) + .as_chunks_mut::() + .0 + .iter_mut() + .zip(src_row.as_chunks::().0.iter()) .enumerate() { let mut sum0 = 0f32; @@ -139,7 +141,7 @@ impl BilateralUnit for ExecutionUnit4<'_> { { let c_slice = (y + ky) * a_stride + x * N; let c_px_slice = &a_src[c_slice..(c_slice + N * self.params.kernel_size)]; - for (c_px, &rwz) in c_px_slice.chunks_exact(N).zip(ky_row.iter()) { + for (c_px, &rwz) in c_px_slice.as_chunks::().0.iter().zip(ky_row.iter()) { let z0 = rwz * ss[(center[0] as u16 * 256 + c_px[0] as u16) as usize]; let z1 = rwz * ss[(center[1] as u16 * 256 + c_px[1] as u16) as usize]; let z2 = rwz * ss[(center[2] as u16 * 256 + c_px[2] as u16) as usize]; @@ -178,8 +180,10 @@ impl BilateralUnit for ExecutionUnit3<'_> { let dst_row = &mut dst_row[..useful_width]; let src_row = &src_row[..useful_width]; for (x, (dst, center)) in dst_row - .chunks_exact_mut(N) - .zip(src_row.chunks_exact(N)) + .as_chunks_mut::() + .0 + .iter_mut() + .zip(src_row.as_chunks::().0.iter()) .enumerate() { let mut sum0 = 0f32; @@ -196,7 +200,7 @@ impl BilateralUnit for ExecutionUnit3<'_> { { let c_slice = (y + ky) * a_stride + x * N; let c_px_slice = &a_src[c_slice..(c_slice + N * self.params.kernel_size)]; - for (c_px, &rwz) in c_px_slice.chunks_exact(N).zip(ky_row.iter()) { + for (c_px, &rwz) in c_px_slice.as_chunks::().0.iter().zip(ky_row.iter()) { let z0 = rwz * ss[(center[0] as u16 * 256 + c_px[0] as u16) as usize]; let z1 = rwz * ss[(center[1] as u16 * 256 + c_px[1] as u16) as usize]; let z2 = rwz * ss[(center[2] as u16 * 256 + c_px[2] as u16) as usize]; @@ -230,8 +234,10 @@ impl BilateralUnit for ExecutionUnitPlane<'_> { let dst_row = &mut dst_row[..useful_width]; let src_row = &src_row[..useful_width]; for (x, (dst, center)) in dst_row - .chunks_exact_mut(N) - .zip(src_row.chunks_exact(N)) + .as_chunks_mut::() + .0 + .iter_mut() + .zip(src_row.as_chunks::().0.iter()) .enumerate() { let mut sum0 = 0f32; @@ -243,7 +249,7 @@ impl BilateralUnit for ExecutionUnitPlane<'_> { { let c_slice = (y + ky) * a_stride + x * N; let c_px_slice = &a_src[c_slice..(c_slice + N * self.params.kernel_size)]; - for (c_px, &rwz) in c_px_slice.chunks_exact(N).zip(ky_row.iter()) { + for (c_px, &rwz) in c_px_slice.as_chunks::().0.iter().zip(ky_row.iter()) { let z0 = rwz * ss[(center[0] as u16 * 256 + c_px[0] as u16) as usize]; sum0 += z0 * c_px[0] as f32; iw0 += z0; @@ -271,7 +277,7 @@ fn bilateral_filter_impl( src.size_matches_mut(dst)?; let arena = make_arena::( - src.data.as_ref(), + src.projected(), src.row_stride() as usize, src.size(), ArenaPads::constant(params.kernel_size / 2), @@ -320,12 +326,11 @@ fn bilateral_filter_impl( let thread_count = threading_policy.thread_count(src.width, src.height) as u32; let thread_pool = novtb::ThreadPool::new(thread_count as usize); - dst.data - .borrow_mut() + dst.projected() .tb_par_chunks_exact_mut(dst_row_stride) .for_each_enumerated(&thread_pool, |y, dst| { - let src_row = &src.data.as_ref() - [y * src.row_stride() as usize..(y + 1) * src.row_stride() as usize]; + let src_row = &src.data.as_ref()[y * src.row_stride() as usize + ..y * src.row_stride() as usize + src.width as usize * src.channels.channels()]; _unit.execute(arena_src, y, dst, src_row) }); diff --git a/src/bilateral/neon/bp8.rs b/src/bilateral/neon/bp8.rs index 18eeb8e5..9a20357d 100644 --- a/src/bilateral/neon/bp8.rs +++ b/src/bilateral/neon/bp8.rs @@ -77,6 +77,7 @@ impl BilateralUnit for BilateralExecutionUnitNeon<'_, N> { let a_stride = self.arena.width * self.arena.components; let mut dst_row = &mut dst_row[..useful_width]; let mut src_row = &src_row[..useful_width]; + let mut offset = 0usize; unsafe { for (wx, (dst, center)) in dst_row @@ -164,6 +165,7 @@ impl BilateralUnit for BilateralExecutionUnitNeon<'_, N> { let vss0 = vqmovn_u16(vcombine_u16(ss0, ss1)); let vss1 = vqmovn_u16(vcombine_u16(ss2, ss3)); vst1q_u8(dst.as_mut_ptr().cast(), vcombine_u8(vss0, vss1)); + offset += 16; } dst_row = dst_row.chunks_exact_mut(16).into_remainder(); @@ -184,7 +186,7 @@ impl BilateralUnit for BilateralExecutionUnitNeon<'_, N> { let cx = vshll_n_u8::<8>(vld1_u8(center.as_ptr().cast())); - let x = wx * 8; + let x = offset + wx * 8; for (ky, ky_row) in sliced_range .chunks_exact(self.params.kernel_size) @@ -230,6 +232,7 @@ impl BilateralUnit for BilateralExecutionUnitNeon<'_, N> { let vss0 = vqmovn_u16(vcombine_u16(ss0, ss1)); vst1_u8(dst.as_mut_ptr().cast(), vss0); + offset += 8; } } @@ -240,6 +243,8 @@ impl BilateralUnit for BilateralExecutionUnitNeon<'_, N> { let mut sum0 = 0f32; let mut iw0 = 0f32; + let x = x + offset; + let sx = x % N; for (ky, ky_row) in sliced_range .chunks_exact(self.params.kernel_size) @@ -258,6 +263,7 @@ impl BilateralUnit for BilateralExecutionUnitNeon<'_, N> { iw0 = if iw0 == 0. { 1. } else { iw0 }; *dst = (sum0 / iw0).round().min(255.).max(0.) as u8; + offset += 1; } } } diff --git a/src/fast_bilateral_filter.rs b/src/fast_bilateral_filter.rs index 8108c3e3..b4222102 100644 --- a/src/fast_bilateral_filter.rs +++ b/src/fast_bilateral_filter.rs @@ -33,7 +33,7 @@ use num_traits::AsPrimitive; use num_traits::real::Real; use rayon::ThreadPool; use rayon::iter::{IndexedParallelIterator, ParallelIterator}; -use rayon::prelude::{IntoParallelIterator, ParallelSliceMut}; +use rayon::prelude::{IntoParallelIterator, ParallelSlice, ParallelSliceMut}; use std::fmt::{Debug, Display}; use std::ops::{Add, AddAssign, Div, Index, Mul, MulAssign, Sub}; @@ -315,7 +315,6 @@ fn fast_bilateral_filter_impl< >( img: &BlurImage, dst: &mut [T], - kernel_size: u32, spatial_sigma: f32, range_sigma: f32, ) where @@ -335,18 +334,8 @@ fn fast_bilateral_filter_impl< let padding_xy = 2.; let padding_z = 2.; - let spatial_sigma_scale = if spatial_sigma > 1. { - (1. / spatial_sigma * (if spatial_sigma > 1.3 { 1.3 } else { 1. })) - .min(1. / 1.2f32) - .max(1. / 5f32) - } else { - 1. - }; - let range_sigma_scale = if range_sigma > 1. { - (1. / range_sigma).min(1. / 1.1f32).max(1. / 5f32) - } else { - 1. - }; + let spatial_sigma_scale = 1. / spatial_sigma; + let range_sigma_scale = 1. / range_sigma; let small_width = (((width - 1) as f32 * spatial_sigma_scale) + 1. + 2. * padding_xy) as usize; let small_height = @@ -380,9 +369,9 @@ fn fast_bilateral_filter_impl< let mut buffer = Array3D::::new(&mut target2, small_width, small_height, small_depth); - let preferred_sigma = (spatial_sigma * spatial_sigma + range_sigma * range_sigma).sqrt(); - - let gaussian_kernel = gaussian_kernel_1d(kernel_size, preferred_sigma); + let preferred_sigma = 1.0f32; + let grid_kernel_size = 7u32; // 6*sigma+1, fixed + let gaussian_kernel = gaussian_kernel_1d(grid_kernel_size, preferred_sigma); let half_kernel = gaussian_kernel.len() / 2; // Unrolled 3D convolution @@ -477,7 +466,7 @@ fn fast_bilateral_filter_impl< buffer.set(x, y, z, sum); } - for z in small_depth.saturating_sub(small_depth)..small_depth { + for z in small_depth.saturating_sub(half_kernel)..small_depth { let mut sum = data .get(x, y, z) .mul(unsafe { *gaussian_kernel.get_unchecked(half_kernel) }.as_()); @@ -555,15 +544,12 @@ fn fast_bilateral_filter_impl< std::mem::swap(&mut buffer, &mut data); - for (src, dst) in img.iter().zip(dst.iter_mut()) { - *dst = *src; - } - dst.par_chunks_exact_mut(width as usize) + .zip(img.par_chunks_exact(width as usize)) .enumerate() - .for_each(|(y, row)| { - for (x, t) in row.iter_mut().enumerate() { - let z = (*t - base_min).as_(); + .for_each(|(y, (row, src_row))| { + for (x, (t, s)) in row.iter_mut().zip(src_row.iter()).enumerate() { + let z = (*s - base_min).as_(); let d = data.trilinear_interpolation( (x as f32) * spatial_sigma_scale + padding_xy, (y as f32) * spatial_sigma_scale + padding_xy, @@ -625,7 +611,6 @@ fn fast_bilateral_filter_plane_impl< >( img: &BlurImage, dst: &mut BlurImageMut, - kernel_size: u32, spatial_sigma: f32, range_sigma: f32, pool: &ThreadPool, @@ -636,7 +621,6 @@ fn fast_bilateral_filter_plane_impl< img.size_matches_mut(dst)?; let width = img.width; let height = img.height; - assert_ne!(kernel_size & 1, 0, "kernel size must be odd"); assert!( !(spatial_sigma <= 0. || range_sigma <= 0.0), "Spatial sigma and range sigma must be more than 0" @@ -656,7 +640,6 @@ fn fast_bilateral_filter_plane_impl< fast_bilateral_filter_impl( &in_image, working_dst.data.borrow_mut(), - kernel_size, spatial_sigma, range_sigma, ); @@ -683,14 +666,12 @@ pub(crate) fn fast_bilateral_filter_gray_alpha_impl< >( img: &BlurImage, dst: &mut BlurImageMut, - kernel_size: u32, spatial_sigma: f32, range_sigma: f32, threading_policy: ThreadingPolicy, ) -> Result<(), BlurError> { img.check_layout_channels(2)?; dst.check_layout_channels(2, Some(img))?; - assert_ne!(kernel_size & 1, 0, "kernel size must be odd"); assert!( !(spatial_sigma <= 0. || range_sigma <= 0.0), "Spatial sigma and range sigma must be more than 0" @@ -717,7 +698,7 @@ pub(crate) fn fast_bilateral_filter_gray_alpha_impl< for ((r, g), src) in dst0 .iter_mut() .zip(dst1.iter_mut()) - .zip(src.chunks_exact(2)) + .zip(src.as_chunks::<2>().0.iter()) { *r = src[0].to_bilinear_f32(); *g = src[1].to_bilinear_f32(); @@ -735,7 +716,6 @@ pub(crate) fn fast_bilateral_filter_gray_alpha_impl< fast_bilateral_filter_impl( &ref0, working_dst0.data.borrow_mut(), - kernel_size, spatial_sigma, range_sigma, ); @@ -744,7 +724,6 @@ pub(crate) fn fast_bilateral_filter_gray_alpha_impl< fast_bilateral_filter_impl( &ref1, working_dst1.data.borrow_mut(), - kernel_size, spatial_sigma, range_sigma, ); @@ -759,7 +738,12 @@ pub(crate) fn fast_bilateral_filter_gray_alpha_impl< .zip(working_dst0.data.borrow().chunks_exact(width as usize)) .zip(working_dst1.data.borrow().chunks_exact(width as usize)) .for_each(|((dst, src0), src1)| { - for ((dst, src0), src1) in dst.chunks_exact_mut(2).zip(src0.iter()).zip(src1.iter()) + for ((dst, src0), src1) in dst + .as_chunks_mut::<2>() + .0 + .iter_mut() + .zip(src0.iter()) + .zip(src1.iter()) { dst[0] = V::from_bilinear_f32(*src0); dst[1] = V::from_bilinear_f32(*src1); @@ -774,7 +758,6 @@ fn fast_bilateral_filter_rgb_impl< >( img: &BlurImage, dst: &mut BlurImageMut, - kernel_size: u32, spatial_sigma: f32, range_sigma: f32, pool: &ThreadPool, @@ -785,7 +768,6 @@ fn fast_bilateral_filter_rgb_impl< img.size_matches_mut(dst)?; let width = img.width; let height = img.height; - assert_ne!(kernel_size & 1, 0, "kernel size must be odd"); assert!( !(spatial_sigma <= 0. || range_sigma <= 0.0), "Spatial sigma and range sigma must be more than 0" @@ -805,7 +787,7 @@ fn fast_bilateral_filter_rgb_impl< .iter_mut() .zip(dst1.iter_mut()) .zip(dst2.iter_mut()) - .zip(src.chunks_exact(3)) + .zip(src.as_chunks::<3>().0.iter()) { *r = src[0].to_bilinear_f32(); *g = src[1].to_bilinear_f32(); @@ -826,7 +808,6 @@ fn fast_bilateral_filter_rgb_impl< fast_bilateral_filter_impl( &ref0, working_dst0.data.borrow_mut(), - kernel_size, spatial_sigma, range_sigma, ); @@ -835,7 +816,6 @@ fn fast_bilateral_filter_rgb_impl< fast_bilateral_filter_impl( &ref1, working_dst1.data.borrow_mut(), - kernel_size, spatial_sigma, range_sigma, ); @@ -844,7 +824,6 @@ fn fast_bilateral_filter_rgb_impl< fast_bilateral_filter_impl( &ref2, working_dst2.data.borrow_mut(), - kernel_size, spatial_sigma, range_sigma, ); @@ -861,7 +840,9 @@ fn fast_bilateral_filter_rgb_impl< .zip(working_dst2.data.borrow().chunks_exact(width as usize)) .for_each(|(((dst, src0), src1), src2)| { for (((dst, src0), src1), src2) in dst - .chunks_exact_mut(3) + .as_chunks_mut::<3>() + .0 + .iter_mut() .zip(src0.iter()) .zip(src1.iter()) .zip(src2) @@ -881,7 +862,6 @@ fn fast_bilateral_filter_rgba_impl< >( img: &BlurImage, dst: &mut BlurImageMut, - kernel_size: u32, spatial_sigma: f32, range_sigma: f32, pool: &ThreadPool, @@ -892,7 +872,6 @@ fn fast_bilateral_filter_rgba_impl< img.size_matches_mut(dst)?; let width = img.width; let height = img.height; - assert_ne!(kernel_size & 1, 0, "kernel size must be odd"); assert!( !(spatial_sigma <= 0. || range_sigma <= 0.0), "Spatial sigma and range sigma must be more than 0" @@ -915,7 +894,7 @@ fn fast_bilateral_filter_rgba_impl< .zip(dst1.iter_mut()) .zip(dst2.iter_mut()) .zip(dst3.iter_mut()) - .zip(src.chunks_exact(4)) + .zip(src.as_chunks::<4>().0.iter()) { *r = src[0].to_bilinear_f32(); *g = src[1].to_bilinear_f32(); @@ -939,7 +918,6 @@ fn fast_bilateral_filter_rgba_impl< fast_bilateral_filter_impl( &ref0, working_dst0.data.borrow_mut(), - kernel_size, spatial_sigma, range_sigma, ); @@ -949,7 +927,6 @@ fn fast_bilateral_filter_rgba_impl< fast_bilateral_filter_impl( &ref1, working_dst1.data.borrow_mut(), - kernel_size, spatial_sigma, range_sigma, ); @@ -959,7 +936,6 @@ fn fast_bilateral_filter_rgba_impl< fast_bilateral_filter_impl( &ref2, working_dst2.data.borrow_mut(), - kernel_size, spatial_sigma, range_sigma, ); @@ -969,7 +945,6 @@ fn fast_bilateral_filter_rgba_impl< fast_bilateral_filter_impl( &ref3, working_dst3.data.borrow_mut(), - kernel_size, spatial_sigma, range_sigma, ); @@ -987,7 +962,9 @@ fn fast_bilateral_filter_rgba_impl< .zip(working_dst3.data.borrow().chunks_exact(width as usize)) .for_each(|((((dst, src0), src1), src2), src3)| { for ((((dst, src0), src1), src2), src3) in dst - .chunks_exact_mut(4) + .as_chunks_mut::<4>() + .0 + .iter_mut() .zip(src0.iter()) .zip(src1.iter()) .zip(src2.iter()) @@ -1021,7 +998,6 @@ fn fast_bilateral_filter_rgba_impl< pub fn fast_bilateral_filter( src: &BlurImage, dst: &mut BlurImageMut, - kernel_size: u32, spatial_sigma: f32, range_sigma: f32, threading_policy: ThreadingPolicy, @@ -1030,7 +1006,6 @@ pub fn fast_bilateral_filter( dst.check_layout(Some(src))?; src.size_matches_mut(dst)?; let channels = src.channels; - assert_ne!(kernel_size & 1, 0, "kernel_size must be odd"); let thread_count = threading_policy.thread_count(src.width, src.height) as u32; let pool = rayon::ThreadPoolBuilder::new() @@ -1040,34 +1015,13 @@ pub fn fast_bilateral_filter( match channels { FastBlurChannels::Plane => { - fast_bilateral_filter_plane_impl( - src, - dst, - kernel_size, - spatial_sigma, - range_sigma, - &pool, - )?; + fast_bilateral_filter_plane_impl(src, dst, spatial_sigma, range_sigma, &pool)?; } FastBlurChannels::Channels3 => { - fast_bilateral_filter_rgb_impl( - src, - dst, - kernel_size, - spatial_sigma, - range_sigma, - &pool, - )?; + fast_bilateral_filter_rgb_impl(src, dst, spatial_sigma, range_sigma, &pool)?; } FastBlurChannels::Channels4 => { - fast_bilateral_filter_rgba_impl( - src, - dst, - kernel_size, - spatial_sigma, - range_sigma, - &pool, - )?; + fast_bilateral_filter_rgba_impl(src, dst, spatial_sigma, range_sigma, &pool)?; } } Ok(()) @@ -1075,7 +1029,7 @@ pub fn fast_bilateral_filter( /// Performs fast bilateral filter on the up to 16-bit image /// -/// This is fast bilateral approximation, note this behaviour significantly differs from OpenCV. +/// This is fast bilateral approximation, note this behavior differs from OpenCV. /// This method has high convergence and will completely blur an image very fast with increasing spatial sigma /// By the nature of this filter the more spatial sigma are the faster method is. /// @@ -1093,7 +1047,6 @@ pub fn fast_bilateral_filter( pub fn fast_bilateral_filter_u16( src: &BlurImage, dst: &mut BlurImageMut, - kernel_size: u32, spatial_sigma: f32, range_sigma: f32, threading_policy: ThreadingPolicy, @@ -1102,7 +1055,6 @@ pub fn fast_bilateral_filter_u16( dst.check_layout(Some(src))?; src.size_matches_mut(dst)?; let channels = src.channels; - assert_ne!(kernel_size & 1, 0, "kernel_size must be odd"); let thread_count = threading_policy.thread_count(src.width, src.height) as u32; let pool = rayon::ThreadPoolBuilder::new() @@ -1112,34 +1064,13 @@ pub fn fast_bilateral_filter_u16( match channels { FastBlurChannels::Plane => { - fast_bilateral_filter_plane_impl( - src, - dst, - kernel_size, - spatial_sigma, - range_sigma, - &pool, - )?; + fast_bilateral_filter_plane_impl(src, dst, spatial_sigma, range_sigma, &pool)?; } FastBlurChannels::Channels3 => { - fast_bilateral_filter_rgb_impl( - src, - dst, - kernel_size, - spatial_sigma, - range_sigma, - &pool, - )?; + fast_bilateral_filter_rgb_impl(src, dst, spatial_sigma, range_sigma, &pool)?; } FastBlurChannels::Channels4 => { - fast_bilateral_filter_rgba_impl( - src, - dst, - kernel_size, - spatial_sigma, - range_sigma, - &pool, - )?; + fast_bilateral_filter_rgba_impl(src, dst, spatial_sigma, range_sigma, &pool)?; } } Ok(()) @@ -1147,7 +1078,7 @@ pub fn fast_bilateral_filter_u16( /// Performs fast bilateral filter on the f32 image /// -/// This is fast bilateral approximation, note this behaviour significantly differs from OpenCV. +/// This is fast bilateral approximation, note this behavior differs from OpenCV. /// This method has high convergence and will completely blur an image very fast with increasing spatial sigma /// By the nature of this filter the more spatial sigma are the faster method is. /// @@ -1165,7 +1096,6 @@ pub fn fast_bilateral_filter_u16( pub fn fast_bilateral_filter_f32( src: &BlurImage, dst: &mut BlurImageMut, - kernel_size: u32, spatial_sigma: f32, range_sigma: f32, threading_policy: ThreadingPolicy, @@ -1181,37 +1111,15 @@ pub fn fast_bilateral_filter_f32( .build() .unwrap(); - assert_ne!(kernel_size & 1, 0, "kernel_size must be odd"); match channels { FastBlurChannels::Plane => { - fast_bilateral_filter_plane_impl( - src, - dst, - kernel_size, - spatial_sigma, - range_sigma, - &pool, - )?; + fast_bilateral_filter_plane_impl(src, dst, spatial_sigma, range_sigma, &pool)?; } FastBlurChannels::Channels3 => { - fast_bilateral_filter_rgb_impl( - src, - dst, - kernel_size, - spatial_sigma, - range_sigma, - &pool, - )?; + fast_bilateral_filter_rgb_impl(src, dst, spatial_sigma, range_sigma, &pool)?; } FastBlurChannels::Channels4 => { - fast_bilateral_filter_rgba_impl( - src, - dst, - kernel_size, - spatial_sigma, - range_sigma, - &pool, - )?; + fast_bilateral_filter_rgba_impl(src, dst, spatial_sigma, range_sigma, &pool)?; } } Ok(()) diff --git a/src/fast_bilateral_image.rs b/src/fast_bilateral_image.rs index a7434a73..0e77d835 100644 --- a/src/fast_bilateral_image.rs +++ b/src/fast_bilateral_image.rs @@ -53,14 +53,10 @@ use image::{ #[must_use] pub fn fast_bilateral_filter_image( image: DynamicImage, - kernel_size: u32, spatial_sigma: f32, range_sigma: f32, threading_policy: ThreadingPolicy, ) -> Option { - if kernel_size & 1 == 0 { - return None; - } match image { DynamicImage::ImageLuma8(gray) => { let mut new_image = @@ -72,7 +68,6 @@ pub fn fast_bilateral_filter_image( fast_bilateral_filter( &blur_image, &mut new_image, - kernel_size, spatial_sigma, range_sigma, threading_policy, @@ -111,7 +106,6 @@ pub fn fast_bilateral_filter_image( fast_bilateral_filter_gray_alpha_impl( &captured_image, &mut new_image, - kernel_size, spatial_sigma, range_sigma, threading_policy, @@ -142,7 +136,6 @@ pub fn fast_bilateral_filter_image( fast_bilateral_filter( &blur_image, &mut new_image, - kernel_size, spatial_sigma, range_sigma, threading_policy, @@ -173,7 +166,6 @@ pub fn fast_bilateral_filter_image( fast_bilateral_filter( &blur_image, &mut new_image, - kernel_size, spatial_sigma, range_sigma, threading_policy, @@ -200,7 +192,6 @@ pub fn fast_bilateral_filter_image( fast_bilateral_filter_u16( &blur_image, &mut new_image, - kernel_size, spatial_sigma, range_sigma, threading_policy, @@ -239,7 +230,6 @@ pub fn fast_bilateral_filter_image( fast_bilateral_filter_gray_alpha_impl( &captured_image, &mut new_image, - kernel_size, spatial_sigma, range_sigma, threading_policy, @@ -270,7 +260,6 @@ pub fn fast_bilateral_filter_image( fast_bilateral_filter_u16( &blur_image, &mut new_image, - kernel_size, spatial_sigma, range_sigma, threading_policy, @@ -301,7 +290,6 @@ pub fn fast_bilateral_filter_image( fast_bilateral_filter_u16( &blur_image, &mut new_image, - kernel_size, spatial_sigma, range_sigma, threading_policy, @@ -332,7 +320,6 @@ pub fn fast_bilateral_filter_image( fast_bilateral_filter_f32( &blur_image, &mut new_image, - kernel_size, spatial_sigma, range_sigma, threading_policy, @@ -363,7 +350,6 @@ pub fn fast_bilateral_filter_image( fast_bilateral_filter_f32( &blur_image, &mut new_image, - kernel_size, spatial_sigma, range_sigma, threading_policy, diff --git a/src/filter1d/arena.rs b/src/filter1d/arena.rs index 07e783db..5a4a328d 100644 --- a/src/filter1d/arena.rs +++ b/src/filter1d/arena.rs @@ -199,7 +199,13 @@ where let new_stride = new_width * CN; let offset = pads.pad_top * new_stride + pads.pad_left * CN; - copy_roi(&mut padded_image[offset..], image, new_stride, old_stride); + copy_roi( + &mut padded_image[offset..], + image, + new_stride, + old_stride, + width * CN, + ); let filling_ranges = [ (0..pads.pad_top, 0..new_width), // Top outer @@ -227,7 +233,7 @@ where for (j, dst) in ranges .1 .clone() - .zip(dst.chunks_exact_mut(CN).skip(ranges.1.start)) + .zip(dst.as_chunks_mut::().0.iter_mut().skip(ranges.1.start)) { let y = clamp_edge!( edge_modes.vertical, @@ -263,7 +269,7 @@ where for (j, dst) in ranges .1 .clone() - .zip(dst.chunks_exact_mut(CN).skip(ranges.1.start)) + .zip(dst.as_chunks_mut::().0.iter_mut().skip(ranges.1.start)) { let y = clamp_edge!( edge_modes.vertical, @@ -299,7 +305,7 @@ where for (j, dst) in ranges .1 .clone() - .zip(dst.chunks_exact_mut(CN).skip(ranges.1.start)) + .zip(dst.as_chunks_mut::().0.iter_mut().skip(ranges.1.start)) { let y = i as i64 - pad_h as i64; let x = clamp_edge!( @@ -334,7 +340,7 @@ where for (_, dst) in ranges .1 .clone() - .zip(dst.chunks_exact_mut(CN).skip(ranges.1.start)) + .zip(dst.as_chunks_mut::().0.iter_mut().skip(ranges.1.start)) { for (y, dst) in dst.iter_mut().enumerate() { *dst = scalar[y].as_(); diff --git a/src/filter1d/arena_roi.rs b/src/filter1d/arena_roi.rs index 65044610..c8eb14b7 100644 --- a/src/filter1d/arena_roi.rs +++ b/src/filter1d/arena_roi.rs @@ -32,14 +32,16 @@ /// Copies ROI from one image to another #[allow(clippy::type_complexity)] #[inline(always)] -pub fn copy_roi(arena: &mut [T], roi: &[T], arena_stride: usize, stride: usize) +pub fn copy_roi(arena: &mut [T], roi: &[T], arena_stride: usize, stride: usize, size: usize) where T: Copy, { arena - .chunks_exact_mut(arena_stride) + .chunks_mut(arena_stride) .zip(roi.chunks(stride)) .for_each(|(dst, src)| { + let dst = &mut dst[..size]; + let src = &src[..size]; for (dst, src) in dst.iter_mut().zip(src.iter()) { *dst = *src; }