diff --git a/.gitignore b/.gitignore index 4f2361a..7461a85 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ /converted_rgb.png .idea target -app/target \ No newline at end of file +app/target +dilated.jpg \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index d0cbfc3..56ed199 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -261,6 +261,7 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", + "libloading", ] [[package]] @@ -429,6 +430,7 @@ name = "fast_morphology" version = "0.1.0" dependencies = [ "colorutils-rs", + "image", "num-traits", "rayon", ] @@ -678,6 +680,16 @@ dependencies = [ "once_cell", ] +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets", +] + [[package]] name = "libm" version = "0.2.8" diff --git a/Cargo.toml b/Cargo.toml index 4ce48cf..1b18400 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,4 +18,9 @@ exclude = ["*.jpg", "*.png"] [dependencies] colorutils-rs = "0.5.12" num-traits = "0.2.19" -rayon = "1.10.0" \ No newline at end of file +rayon = "1.10.0" +image = { version = "0.25.0", optional = true, default-features = false } + +[features] +default = [] +image = ["dep:image"] \ No newline at end of file diff --git a/README.md b/README.md index fb245b9..cf9e480 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,25 @@ dilate_rgb( ).unwrap(); ``` +#### Usage with image crate + +```rust +let img = ImageReader::open("./assets/fruits.jpg") + .unwrap() + .decode() + .unwrap(); +let new_image = morphology_image( + img, + MorphOp::Dilate, + &structuring_element, + KernelShape::new(se_size, se_size), + BorderMode::default(), + MorphologyThreadingPolicy::default(), +) +.unwrap(); +new_image.save("dilated.jpg").unwrap(); +``` + ## Results Here is some examply bokeh effect @@ -24,6 +43,13 @@ Here is some examply bokeh effect

+And erosion + +

+ + +

+ # Benchmarking If you wish to run benchmarks then diff --git a/app/Cargo.toml b/app/Cargo.toml index 9da90f9..0118c3e 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -5,9 +5,9 @@ edition = "2021" [dependencies] image = "0.25.2" -fast_morphology = {path = "../"} +fast_morphology = {path = "../", features = ["image"]} imageproc = "0.25.0" -opencv = {version = "0.93.0", features = ["imgproc"]} +opencv = {version = "0.93.0", features = ["imgproc", "clang-runtime"]} [dev-dependencies] criterion = {version = "0.5.1", features = ["html_reports"]} diff --git a/app/benches/dilation/main.rs b/app/benches/dilation/main.rs index f41276d..22c7a59 100644 --- a/app/benches/dilation/main.rs +++ b/app/benches/dilation/main.rs @@ -321,18 +321,18 @@ pub fn criterion_benchmark(c: &mut Criterion) { exec_bench_rgb(c, 10); exec_bench_rgb(c, 20); exec_bench_rgb(c, 30); - // - // exec_bench_rgba(c, 4); - // exec_bench_rgba(c, 7); - // exec_bench_rgba(c, 10); - // exec_bench_rgba(c, 20); - // exec_bench_rgba(c, 30); - // exec_bench_gray(c, 4); - // exec_bench_gray(c, 7); - // exec_bench_gray(c, 10); - // exec_bench_gray(c, 20); - // exec_bench_gray(c, 30); + exec_bench_rgba(c, 4); + exec_bench_rgba(c, 7); + exec_bench_rgba(c, 10); + exec_bench_rgba(c, 20); + exec_bench_rgba(c, 30); + + exec_bench_gray(c, 4); + exec_bench_gray(c, 7); + exec_bench_gray(c, 10); + exec_bench_gray(c, 20); + exec_bench_gray(c, 30); } criterion_group!(benches, criterion_benchmark); diff --git a/app/src/main.rs b/app/src/main.rs index ff1c4cc..e750580 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -1,5 +1,6 @@ use fast_morphology::{ - dilate, dilate_rgb, dilate_rgba, BorderMode, ImageSize, KernelShape, MorphologyThreadingPolicy, + dilate, dilate_rgb, dilate_rgba, erode, erode_rgba, morphology_image, BorderMode, ImageSize, + KernelShape, MorphExOp, MorphologyThreadingPolicy, }; use image::{EncodableLayout, GenericImageView, ImageReader}; use opencv::core::{ @@ -65,7 +66,7 @@ fn gaussian_kernel(size: usize, sigma: f32) -> Vec> { } fn main() { - let radius_size = 55; + let radius_size = 10; let mut structuring_element = circle_se(radius_size); opencv::core::set_use_opencl(false).expect("Failed to disable OpenCL"); @@ -114,7 +115,7 @@ fn main() { let image_size = ImageSize::new(dimensions.0 as usize, dimensions.1 as usize); - dilate( + erode( &channel_1_src, &mut channel_1_dst, image_size, @@ -125,7 +126,7 @@ fn main() { ) .unwrap(); - dilate( + erode( &channel_2_src, &mut channel_2_dst, image_size, @@ -136,7 +137,7 @@ fn main() { ) .unwrap(); - dilate( + erode( &channel_3_src, &mut channel_3_dst, image_size, @@ -166,7 +167,7 @@ fn main() { let mut dst = vec![0u8; rgba_image.len()]; let exec_time = Instant::now(); - dilate_rgba( + erode_rgba( &rgba_image, &mut dst, image_size, @@ -207,7 +208,7 @@ fn main() { let exec_time = Instant::now(); let mut dst_mat = Mat::default(); - imgproc::dilate( + imgproc::erode( &mat, &mut dst_mat, &kernel, @@ -222,6 +223,18 @@ fn main() { println!("opencv exec time {:?}", exec_time.elapsed()); + let new_image = morphology_image( + img, + MorphExOp::Erode, + &structuring_element, + KernelShape::new(se_size, se_size), + BorderMode::default(), + MorphologyThreadingPolicy::default(), + ) + .unwrap(); + + new_image.save("dilated.jpg").unwrap(); + image::save_buffer( "converted.png", &bytes, diff --git a/assets/erosion.jpg b/assets/erosion.jpg new file mode 100644 index 0000000..0a27ffc Binary files /dev/null and b/assets/erosion.jpg differ diff --git a/src/arena.rs b/src/arena.rs index 2f5ea65..003ba2f 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -65,126 +65,84 @@ where ); } + let filling_ranges = [ + (0..pad_h, 0..new_width), // Top outer + (pad_h..(new_height - pad_h), 0..pad_w), // Left outer + ((height as usize + pad_h)..new_height, 0..new_width), // Bottom outer + ( + pad_h..(new_height - pad_h), + (width as usize + pad_w)..new_width, + ), // Bottom outer, + ]; + match border_mode { BorderMode::Clamp => { - for i in 0..pad_h { - for j in 0..pad_w { - let y = i.saturating_sub(pad_h).min(height as usize - 1); - let x = j.saturating_sub(pad_w).min(width as usize - 1); - unsafe { - let v_dst = i * new_stride + j * COMPONENTS; - let v_src = y * old_stride + x * COMPONENTS; - for i in 0..COMPONENTS { - *padded_image.get_unchecked_mut(v_dst + i) = - *image.get_unchecked(v_src + i); - } - } - } - } - - for i in (height as usize + pad_h)..new_height { - for j in (width as usize + pad_w)..new_width { - let y = i.saturating_sub(pad_h).min(height as usize - 1); - let x = j.saturating_sub(pad_w).min(width as usize - 1); - unsafe { - let v_dst = i * new_stride + j * COMPONENTS; - let v_src = y * old_stride + x * COMPONENTS; - for i in 0..COMPONENTS { - *padded_image.get_unchecked_mut(v_dst + i) = - *image.get_unchecked(v_src + i); + for ranges in filling_ranges.iter() { + for i in ranges.0.clone() { + for j in ranges.1.clone() { + let y = i.saturating_sub(pad_h).min(height as usize - 1); + let x = j.saturating_sub(pad_w).min(width as usize - 1); + unsafe { + let v_dst = i * new_stride + j * COMPONENTS; + let v_src = y * old_stride + x * COMPONENTS; + for i in 0..COMPONENTS { + *padded_image.get_unchecked_mut(v_dst + i) = + *image.get_unchecked(v_src + i); + } } } } } } BorderMode::Wrap => { - for i in 0..pad_h { - for j in 0..pad_w { - let y = (i as i64 - pad_h as i64).rem_euclid(height as i64 - 1) as usize; - let x = (j as i64 - pad_w as i64).rem_euclid(width as i64 - 1) as usize; - unsafe { - let v_dst = i * new_stride + j * COMPONENTS; - let v_src = y * old_stride + x * COMPONENTS; - for i in 0..COMPONENTS { - *padded_image.get_unchecked_mut(v_dst + i) = - *image.get_unchecked(v_src + i); - } - } - } - } - - for i in (height as usize + pad_h)..new_height { - for j in (width as usize + pad_w)..new_width { - let y = (i as i64 - pad_h as i64).rem_euclid(height as i64 - 1) as usize; - let x = (j as i64 - pad_w as i64).rem_euclid(width as i64 - 1) as usize; - unsafe { - let v_dst = i * new_stride + j * COMPONENTS; - let v_src = y * old_stride + x * COMPONENTS; - for i in 0..COMPONENTS { - *padded_image.get_unchecked_mut(v_dst + i) = - *image.get_unchecked(v_src + i); + for ranges in filling_ranges.iter() { + for i in ranges.0.clone() { + for j in ranges.1.clone() { + let y = (i as i64 - pad_h as i64).rem_euclid(height as i64 - 1) as usize; + let x = (j as i64 - pad_w as i64).rem_euclid(width as i64 - 1) as usize; + unsafe { + let v_dst = i * new_stride + j * COMPONENTS; + let v_src = y * old_stride + x * COMPONENTS; + for i in 0..COMPONENTS { + *padded_image.get_unchecked_mut(v_dst + i) = + *image.get_unchecked(v_src + i); + } } } } } } BorderMode::Reflect => { - for i in 0..pad_h { - for j in 0..pad_w { - let y = reflect_index(i as i64 - pad_h as i64, height as i64 - 1); - let x = reflect_index(j as i64 - pad_w as i64, width as i64 - 1); - unsafe { - let v_dst = i * new_stride + j * COMPONENTS; - let v_src = y * old_stride + x * COMPONENTS; - for i in 0..COMPONENTS { - *padded_image.get_unchecked_mut(v_dst + i) = - *image.get_unchecked(v_src + i); - } - } - } - } - - for i in (height as usize + pad_h)..new_height { - for j in (width as usize + pad_w)..new_width { - let y = reflect_index(i as i64 - pad_h as i64, height as i64 - 1); - let x = reflect_index(j as i64 - pad_w as i64, width as i64 - 1); - unsafe { - let v_dst = i * new_stride + j * COMPONENTS; - let v_src = y * old_stride + x * COMPONENTS; - for i in 0..COMPONENTS { - *padded_image.get_unchecked_mut(v_dst + i) = - *image.get_unchecked(v_src + i); + for ranges in filling_ranges.iter() { + for i in ranges.0.clone() { + for j in ranges.1.clone() { + let y = reflect_index(i as i64 - pad_h as i64, height as i64 - 1); + let x = reflect_index(j as i64 - pad_w as i64, width as i64 - 1); + unsafe { + let v_dst = i * new_stride + j * COMPONENTS; + let v_src = y * old_stride + x * COMPONENTS; + for i in 0..COMPONENTS { + *padded_image.get_unchecked_mut(v_dst + i) = + *image.get_unchecked(v_src + i); + } } } } } } BorderMode::Reflect101 => { - for i in 0..pad_h { - for j in 0..pad_w { - let y = reflect_index_101(i as i64 - pad_h as i64, height as i64 - 1); - let x = reflect_index_101(j as i64 - pad_w as i64, width as i64 - 1); - unsafe { - let v_dst = i * new_stride + j * COMPONENTS; - let v_src = y * old_stride + x * COMPONENTS; - for i in 0..COMPONENTS { - *padded_image.get_unchecked_mut(v_dst + i) = - *image.get_unchecked(v_src + i); - } - } - } - } - - for i in (height as usize + pad_h)..new_height { - for j in (width as usize + pad_w)..new_width { - let y = reflect_index_101(i as i64 - pad_h as i64, height as i64 - 1); - let x = reflect_index_101(j as i64 - pad_w as i64, width as i64 - 1); - unsafe { - let v_dst = i * new_stride + j * COMPONENTS; - let v_src = y * old_stride + x * COMPONENTS; - for i in 0..COMPONENTS { - *padded_image.get_unchecked_mut(v_dst + i) = - *image.get_unchecked(v_src + i); + for ranges in filling_ranges.iter() { + for i in ranges.0.clone() { + for j in ranges.1.clone() { + let y = reflect_index_101(i as i64 - pad_h as i64, height as i64 - 1); + let x = reflect_index_101(j as i64 - pad_w as i64, width as i64 - 1); + unsafe { + let v_dst = i * new_stride + j * COMPONENTS; + let v_src = y * old_stride + x * COMPONENTS; + for i in 0..COMPONENTS { + *padded_image.get_unchecked_mut(v_dst + i) = + *image.get_unchecked(v_src + i); + } } } } diff --git a/src/arena_roi.rs b/src/arena_roi.rs index c5ae022..5d64a30 100644 --- a/src/arena_roi.rs +++ b/src/arena_roi.rs @@ -118,7 +118,7 @@ where { if std::any::type_name::() == "u8" { let mut dst: &mut [u8] = unsafe { std::mem::transmute(arena) }; - let mut src = unsafe { std::mem::transmute(roi) }; + let mut src = unsafe { std::mem::transmute::<&[T], &[u8]>(roi) }; let mut _row_handle: Option usize> = None; #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] { diff --git a/src/dynamic_image.rs b/src/dynamic_image.rs new file mode 100644 index 0000000..60bcf3a --- /dev/null +++ b/src/dynamic_image.rs @@ -0,0 +1,609 @@ +/* + * Copyright (c) Radzivon Bartoshyk. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +use crate::op_type::MorphExOp; +use crate::{ + morphology, morphology_gray_alpha_u16, morphology_gray_u16, morphology_rgb, morphology_rgb_f32, + morphology_rgb_u16, morphology_rgba, morphology_rgba_f32, morphology_rgba_u16, BorderMode, + ImageSize, KernelShape, MorphologyThreadingPolicy, +}; +use image::{ + DynamicImage, GrayAlphaImage, GrayImage, ImageBuffer, Luma, LumaA, Rgb, Rgb32FImage, RgbImage, + Rgba, Rgba32FImage, RgbaImage, +}; + +/// Performs morphology on image +/// +/// # Arguments +/// +/// * `image`: Image from image crate +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morphology_image( + image: DynamicImage, + morph_op: MorphExOp, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result { + match image { + DynamicImage::ImageLuma8(plane) => { + match morph_gray_image( + plane, + morph_op, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ) { + Ok(img) => Ok(DynamicImage::from(img)), + Err(err) => Err(err), + } + } + DynamicImage::ImageLumaA8(plane_with_alpha) => { + match morph_gray_alpha_image( + plane_with_alpha, + morph_op, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ) { + Ok(img) => Ok(DynamicImage::from(img)), + Err(err) => Err(err), + } + } + DynamicImage::ImageRgb8(rgb_image) => { + match morph_rgb_image( + rgb_image, + morph_op, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ) { + Ok(img) => Ok(DynamicImage::from(img)), + Err(err) => Err(err), + } + } + DynamicImage::ImageRgba8(rgba_image) => { + match morph_rgba_image( + rgba_image, + morph_op, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ) { + Ok(img) => Ok(DynamicImage::from(img)), + Err(err) => Err(err), + } + } + DynamicImage::ImageLuma16(gray_16) => { + match morph_gray_16_image( + gray_16, + morph_op, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ) { + Ok(img) => Ok(DynamicImage::from(img)), + Err(err) => Err(err), + } + } + DynamicImage::ImageLumaA16(gray_16_with_alpha) => { + match morph_gray_alpha_16_image( + gray_16_with_alpha, + morph_op, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ) { + Ok(img) => Ok(DynamicImage::from(img)), + Err(err) => Err(err), + } + } + DynamicImage::ImageRgb16(rgb_16) => { + match morph_rgb_16_image( + rgb_16, + morph_op, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ) { + Ok(img) => Ok(DynamicImage::from(img)), + Err(err) => Err(err), + } + } + DynamicImage::ImageRgba16(rgba_16) => { + match morph_rgba_16_image( + rgba_16, + morph_op, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ) { + Ok(img) => Ok(DynamicImage::from(img)), + Err(err) => Err(err), + } + } + DynamicImage::ImageRgb32F(rgb_f32) => { + match morph_rgb_f32_image( + rgb_f32, + morph_op, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ) { + Ok(img) => Ok(DynamicImage::from(img)), + Err(err) => Err(err), + } + } + DynamicImage::ImageRgba32F(rgba_f32) => { + match morph_rgba_f32_image( + rgba_f32, + morph_op, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ) { + Ok(img) => Ok(DynamicImage::from(img)), + Err(err) => Err(err), + } + } + _ => Err("This type is not implemented.".parse().unwrap()), + } +} + +/// Performs morphology on image +/// +/// # Arguments +/// +/// * `image`: Image from image crate +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morph_gray_image( + image: GrayImage, + morph_op: MorphExOp, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result { + let bytes = image.as_raw(); + let mut dst_bytes = vec![0u8; bytes.len()]; + let size = ImageSize::new(image.dimensions().0 as usize, image.dimensions().1 as usize); + morphology( + bytes, + &mut dst_bytes, + morph_op, + size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + )?; + if let Some(img) = GrayImage::from_raw(size.width as u32, size.height as u32, dst_bytes) { + Ok(img) + } else { + Err("Can't create a Gray Image".parse().unwrap()) + } +} + +/// Performs morphology on image +/// +/// # Arguments +/// +/// * `image`: Image from image crate +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morph_rgb_image( + image: RgbImage, + morph_op: MorphExOp, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result { + let bytes = image.as_raw(); + let mut dst_bytes = vec![0u8; bytes.len()]; + let size = ImageSize::new(image.dimensions().0 as usize, image.dimensions().1 as usize); + morphology_rgb( + bytes, + &mut dst_bytes, + morph_op, + size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + )?; + if let Some(img) = RgbImage::from_raw(size.width as u32, size.height as u32, dst_bytes) { + Ok(img) + } else { + Err("Can't create a Rgb Image".parse().unwrap()) + } +} + +/// Performs morphology on image +/// +/// # Arguments +/// +/// * `image`: Image from image crate +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morph_gray_alpha_image( + image: GrayAlphaImage, + morph_op: MorphExOp, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result { + let bytes = image.as_raw(); + let mut dst_bytes = vec![0u8; bytes.len()]; + let size = ImageSize::new(image.dimensions().0 as usize, image.dimensions().1 as usize); + morphology_rgb( + bytes, + &mut dst_bytes, + morph_op, + size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + )?; + if let Some(img) = GrayAlphaImage::from_raw(size.width as u32, size.height as u32, dst_bytes) { + Ok(img) + } else { + Err("Can't create a Rgb Image".parse().unwrap()) + } +} + +/// Performs morphology on image +/// +/// # Arguments +/// +/// * `image`: Image from image crate +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morph_rgba_image( + image: RgbaImage, + morph_op: MorphExOp, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result { + let bytes = image.as_raw(); + let mut dst_bytes = vec![0u8; bytes.len()]; + let size = ImageSize::new(image.dimensions().0 as usize, image.dimensions().1 as usize); + morphology_rgba( + bytes, + &mut dst_bytes, + morph_op, + size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + )?; + if let Some(img) = RgbaImage::from_raw(size.width as u32, size.height as u32, dst_bytes) { + Ok(img) + } else { + Err("Can't create a Rgb Image".parse().unwrap()) + } +} + +/// Performs morphology on image +/// +/// # Arguments +/// +/// * `image`: Image from image crate +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morph_gray_alpha_16_image( + image: ImageBuffer, Vec>, + morph_op: MorphExOp, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result, Vec>, String> { + let bytes = image.as_raw(); + let mut dst_bytes = vec![0u16; bytes.len()]; + let size = ImageSize::new(image.dimensions().0 as usize, image.dimensions().1 as usize); + morphology_gray_alpha_u16( + bytes, + &mut dst_bytes, + morph_op, + size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + )?; + if let Some(img) = ImageBuffer::, Vec>::from_raw( + size.width as u32, + size.height as u32, + dst_bytes, + ) { + Ok(img) + } else { + Err("Can't create a Rgb Image".parse().unwrap()) + } +} + +/// Performs morphology on image +/// +/// # Arguments +/// +/// * `image`: Image from image crate +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morph_gray_16_image( + image: ImageBuffer, Vec>, + morph_op: MorphExOp, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result, Vec>, String> { + let bytes = image.as_raw(); + let mut dst_bytes = vec![0u16; bytes.len()]; + let size = ImageSize::new(image.dimensions().0 as usize, image.dimensions().1 as usize); + morphology_gray_u16( + bytes, + &mut dst_bytes, + morph_op, + size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + )?; + if let Some(img) = ImageBuffer::, Vec>::from_raw( + size.width as u32, + size.height as u32, + dst_bytes, + ) { + Ok(img) + } else { + Err("Can't create a Rgb Image".parse().unwrap()) + } +} + +/// Performs morphology on image +/// +/// # Arguments +/// +/// * `image`: Image from image crate +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morph_rgb_16_image( + image: ImageBuffer, Vec>, + morph_op: MorphExOp, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result, Vec>, String> { + let bytes = image.as_raw(); + let mut dst_bytes = vec![0u16; bytes.len()]; + let size = ImageSize::new(image.dimensions().0 as usize, image.dimensions().1 as usize); + morphology_rgb_u16( + bytes, + &mut dst_bytes, + morph_op, + size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + )?; + if let Some(img) = ImageBuffer::, Vec>::from_raw( + size.width as u32, + size.height as u32, + dst_bytes, + ) { + Ok(img) + } else { + Err("Can't create a Rgb Image".parse().unwrap()) + } +} + +/// Performs morphology on image +/// +/// # Arguments +/// +/// * `image`: Image from image crate +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morph_rgba_16_image( + image: ImageBuffer, Vec>, + morph_op: MorphExOp, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result, Vec>, String> { + let bytes = image.as_raw(); + let mut dst_bytes = vec![0u16; bytes.len()]; + let size = ImageSize::new(image.dimensions().0 as usize, image.dimensions().1 as usize); + morphology_rgba_u16( + bytes, + &mut dst_bytes, + morph_op, + size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + )?; + if let Some(img) = ImageBuffer::, Vec>::from_raw( + size.width as u32, + size.height as u32, + dst_bytes, + ) { + Ok(img) + } else { + Err("Can't create a Rgb Image".parse().unwrap()) + } +} + +/// Performs morphology on image +/// +/// # Arguments +/// +/// * `image`: Image from image crate +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morph_rgba_f32_image( + image: Rgba32FImage, + morph_op: MorphExOp, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result { + let bytes = image.as_raw(); + let mut dst_bytes = vec![0f32; bytes.len()]; + let size = ImageSize::new(image.dimensions().0 as usize, image.dimensions().1 as usize); + morphology_rgba_f32( + bytes, + &mut dst_bytes, + morph_op, + size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + )?; + if let Some(img) = Rgba32FImage::from_raw(size.width as u32, size.height as u32, dst_bytes) { + Ok(img) + } else { + Err("Can't create a Rgb Image".parse().unwrap()) + } +} + +/// Performs morphology on image +/// +/// # Arguments +/// +/// * `image`: Image from image crate +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morph_rgb_f32_image( + image: Rgb32FImage, + morph_op: MorphExOp, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result { + let bytes = image.as_raw(); + let mut dst_bytes = vec![0f32; bytes.len()]; + let size = ImageSize::new(image.dimensions().0 as usize, image.dimensions().1 as usize); + morphology_rgb_f32( + bytes, + &mut dst_bytes, + morph_op, + size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + )?; + if let Some(img) = Rgb32FImage::from_raw(size.width as u32, size.height as u32, dst_bytes) { + Ok(img) + } else { + Err("Can't create a Rgb Image".parse().unwrap()) + } +} diff --git a/src/filter.rs b/src/filter.rs index 079b576..392fb8d 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -28,12 +28,13 @@ */ use crate::filter_op_declare::{Arena, MorthOpFilterFlat2DRow}; use crate::flat_se::AnalyzedSe; -use crate::morph_base::MorphNativeOp; use crate::op_type::MorphOp; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use crate::ops::avx::MorphOpFilterAvx2DRow; #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] -use crate::ops::neon::MorphOpFilterNeon2DRow; +use crate::ops::neon::{ + MorphOpFilterNeon2DRow, MorphOpFilterNeon2DRowF32, MorphOpFilterNeon2DRowU16, +}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use crate::ops::sse::{MorphOpFilterSse2DRow, MorphOpFilterSse2DRowF32, MorphOpFilterSse2DRowU16}; use crate::ops::MorphFilterFlat2DRow; @@ -61,99 +62,153 @@ impl MorthOpFilterFlat2DRow for MorthFilterFlat2DRow { } } -impl MorthFilterFlat2DRow -where - T: Copy + 'static + MorphNativeOp, -{ - pub fn new(op: MorphOp) -> MorthFilterFlat2DRow { +pub trait Row2DFilter { + fn get_filter(op: MorphOp) -> MorthFilterFlat2DRow; +} + +impl Row2DFilter for u8 { + fn get_filter(op: MorphOp) -> MorthFilterFlat2DRow { MorthFilterFlat2DRow { handler: match op { MorphOp::Dilate => { - let mut _result: Box + Sync + Send> = + let mut _result: Box + Sync + Send> = Box::new(MorphFilterFlat2DRow::<{ MorphOp::Dilate as u8 }>::default()); - if std::any::type_name::() == "u8" { - #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] - { + #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] + { + _result = Box::new( + MorphOpFilterNeon2DRow::<{ MorphOp::Dilate as u8 }>::default(), + ); + } + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + if std::arch::is_x86_feature_detected!("sse4.1") { _result = Box::new( - MorphOpFilterNeon2DRow::<{ MorphOp::Dilate as u8 }>::default(), + MorphOpFilterSse2DRow::<{ MorphOp::Dilate as u8 }>::default(), ); } - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - { - if std::arch::is_x86_feature_detected!("sse4.1") { - _result = Box::new(MorphOpFilterSse2DRow::< - { MorphOp::Dilate as u8 }, - >::default()); - } - if std::arch::is_x86_feature_detected!("avx2") { - _result = Box::new(MorphOpFilterAvx2DRow::< - { MorphOp::Dilate as u8 }, - >::default()); - } + if std::arch::is_x86_feature_detected!("avx2") { + _result = Box::new( + MorphOpFilterAvx2DRow::<{ MorphOp::Dilate as u8 }>::default(), + ); } - } else if std::any::type_name::() == "u16" { - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - { - if std::arch::is_x86_feature_detected!("sse4.1") { - _result = Box::new(MorphOpFilterSse2DRowU16::< - { MorphOp::Dilate as u8 }, - >::default()); - } + } + _result + } + MorphOp::Erode => { + let mut _result: Box + Sync + Send> = + Box::new(MorphFilterFlat2DRow::<{ MorphOp::Erode as u8 }>::default()); + #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] + { + _result = + Box::new(MorphOpFilterNeon2DRow::<{ MorphOp::Erode as u8 }>::default()); + } + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + if std::arch::is_x86_feature_detected!("sse4.1") { + _result = Box::new( + MorphOpFilterSse2DRow::<{ MorphOp::Erode as u8 }>::default(), + ); } - } else if std::any::type_name::() == "f32" { - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - { - if std::arch::is_x86_feature_detected!("sse4.1") { - _result = Box::new(MorphOpFilterSse2DRowF32::< - { MorphOp::Dilate as u8 }, - >::default()); - } + if std::arch::is_x86_feature_detected!("avx2") { + _result = Box::new( + MorphOpFilterAvx2DRow::<{ MorphOp::Erode as u8 }>::default(), + ); + } + } + _result + } + }, + } + } +} + +impl Row2DFilter for f32 { + fn get_filter(op: MorphOp) -> MorthFilterFlat2DRow { + MorthFilterFlat2DRow { + handler: match op { + MorphOp::Dilate => { + let mut _result: Box + Sync + Send> = + Box::new(MorphFilterFlat2DRow::<{ MorphOp::Dilate as u8 }>::default()); + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + if std::arch::is_x86_feature_detected!("sse4.1") { + _result = Box::new( + MorphOpFilterSse2DRowF32::<{ MorphOp::Dilate as u8 }>::default(), + ); } } + #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] + { + _result = Box::new( + MorphOpFilterNeon2DRowF32::<{ MorphOp::Dilate as u8 }>::default(), + ); + } _result } MorphOp::Erode => { - let mut _result: Box + Sync + Send> = + let mut _result: Box + Sync + Send> = Box::new(MorphFilterFlat2DRow::<{ MorphOp::Erode as u8 }>::default()); - if std::any::type_name::() == "u8" { - #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] - { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + if std::arch::is_x86_feature_detected!("sse4.1") { _result = Box::new( - MorphOpFilterNeon2DRow::<{ MorphOp::Erode as u8 }>::default(), + MorphOpFilterSse2DRowF32::<{ MorphOp::Erode as u8 }>::default(), ); } - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - { - if std::arch::is_x86_feature_detected!("sse4.1") { - _result = Box::new( - MorphOpFilterSse2DRow::<{ MorphOp::Erode as u8 }>::default(), - ); - } - if std::arch::is_x86_feature_detected!("avx2") { - _result = Box::new( - MorphOpFilterAvx2DRow::<{ MorphOp::Erode as u8 }>::default(), - ); - } - } - } else if std::any::type_name::() == "u16" { - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - { - if std::arch::is_x86_feature_detected!("sse4.1") { - _result = Box::new(MorphOpFilterSse2DRowU16::< - { MorphOp::Erode as u8 }, - >::default()); - } + } + #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] + { + _result = Box::new( + MorphOpFilterNeon2DRowF32::<{ MorphOp::Erode as u8 }>::default(), + ); + } + _result + } + }, + } + } +} + +impl Row2DFilter for u16 { + fn get_filter(op: MorphOp) -> MorthFilterFlat2DRow { + MorthFilterFlat2DRow { + handler: match op { + MorphOp::Dilate => { + let mut _result: Box + Sync + Send> = + Box::new(MorphFilterFlat2DRow::<{ MorphOp::Dilate as u8 }>::default()); + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + if std::arch::is_x86_feature_detected!("sse4.1") { + _result = Box::new( + MorphOpFilterSse2DRowU16::<{ MorphOp::Dilate as u8 }>::default(), + ); } - } else if std::any::type_name::() == "f32" { - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - { - if std::arch::is_x86_feature_detected!("sse4.1") { - _result = Box::new(MorphOpFilterSse2DRowF32::< - { MorphOp::Erode as u8 }, - >::default()); - } + } + #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] + { + _result = Box::new( + MorphOpFilterNeon2DRowU16::<{ MorphOp::Dilate as u8 }>::default(), + ); + } + _result + } + MorphOp::Erode => { + let mut _result: Box + Sync + Send> = + Box::new(MorphFilterFlat2DRow::<{ MorphOp::Erode as u8 }>::default()); + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + if std::arch::is_x86_feature_detected!("sse4.1") { + _result = Box::new( + MorphOpFilterSse2DRowU16::<{ MorphOp::Erode as u8 }>::default(), + ); } } + #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] + { + _result = Box::new( + MorphOpFilterNeon2DRowU16::<{ MorphOp::Erode as u8 }>::default(), + ); + } _result } }, diff --git a/src/lib.rs b/src/lib.rs index 097b528..8c95bf7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,11 +26,14 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#![allow(clippy::too_many_arguments)] extern crate core; mod arena; mod arena_roi; mod border_mode; +#[cfg(feature = "image")] +mod dynamic_image; mod filter; mod filter_op_declare; mod flat_se; @@ -52,6 +55,8 @@ mod thread_policy; mod unsafe_slice; pub use border_mode::BorderMode; +#[cfg(feature = "image")] +pub use dynamic_image::*; pub use img_size::ImageSize; pub use op::dilate; pub use op::dilate_gray_alpha; @@ -61,6 +66,10 @@ pub use op::erode; pub use op::erode_gray_alpha; pub use op::erode_rgb; pub use op::erode_rgba; +pub use op::morphology; +pub use op::morphology_gray_alpha; +pub use op::morphology_rgb; +pub use op::morphology_rgba; pub use op_f32::dilate_f32; pub use op_f32::dilate_gray_alpha_f32; pub use op_f32::dilate_rgb_f32; @@ -69,6 +78,9 @@ pub use op_f32::erode_f32; pub use op_f32::erode_gray_alpha_f32; pub use op_f32::erode_rgb_f32; pub use op_f32::erode_rgba_f32; +pub use op_f32::morphology_rgb_f32; +pub use op_f32::morphology_rgba_f32; +pub use op_type::MorphExOp; pub use op_u16::dilate_gray_alpha_u16; pub use op_u16::dilate_rgb_u16; pub use op_u16::dilate_rgba_u16; @@ -77,5 +89,9 @@ pub use op_u16::erode_gray_alpha_u16; pub use op_u16::erode_rgb_u16; pub use op_u16::erode_rgba_u16; pub use op_u16::erode_u16; +pub use op_u16::morphology_gray_alpha_u16; +pub use op_u16::morphology_gray_u16; +pub use op_u16::morphology_rgb_u16; +pub use op_u16::morphology_rgba_u16; pub use structuring_element::KernelShape; pub use thread_policy::MorphologyThreadingPolicy; diff --git a/src/morph_gray_alpha.rs b/src/morph_gray_alpha.rs index ede0ac7..e07a7f7 100644 --- a/src/morph_gray_alpha.rs +++ b/src/morph_gray_alpha.rs @@ -26,6 +26,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +use crate::filter::Row2DFilter; use crate::morph_base::MorphNativeOp; use crate::op_impl::make_morphology; use crate::packing::{GrayAlphaPackable, UnpackedGrayAlpha}; @@ -41,11 +42,27 @@ pub(crate) unsafe fn make_morphology_gray_alpha( threading_policy: MorphologyThreadingPolicy, ) -> Result<(), String> where - T: GrayAlphaPackable + Copy + 'static + Sync + Send + Clone + Default + MorphNativeOp, + T: GrayAlphaPackable + + Copy + + 'static + + Sync + + Send + + Clone + + Default + + MorphNativeOp + + Row2DFilter, { + if src.len() != dst.len() || dst.len() != image_size.width * image_size.height * 2 { + return Err(format!( + "Source and Destination image slice expected to be {} but it was src {}, dst {}", + image_size.width * image_size.height * 2, + src.len(), + dst.len() + )); + } let unpacked = T::unpack(src, image_size); let mut dst_unpacked = UnpackedGrayAlpha::alloc(image_size); - if let Err(err) = make_morphology::( + make_morphology::( &unpacked.gray_channel, &mut dst_unpacked.gray_channel, image_size, @@ -53,10 +70,8 @@ where structuring_element_size, border_mode, threading_policy, - ) { - return Err(err); - } - if let Err(err) = make_morphology::( + )?; + make_morphology::( &unpacked.alpha_channel, &mut dst_unpacked.alpha_channel, image_size, @@ -64,9 +79,7 @@ where structuring_element_size, border_mode, threading_policy, - ) { - return Err(err); - } + )?; T::pack(&dst_unpacked, dst, image_size); Ok(()) } diff --git a/src/morph_rgb.rs b/src/morph_rgb.rs index c5ba9fb..9e037c0 100644 --- a/src/morph_rgb.rs +++ b/src/morph_rgb.rs @@ -26,6 +26,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +use crate::filter::Row2DFilter; use crate::morph_base::MorphNativeOp; use crate::op_impl::make_morphology; use crate::packing::{RgbPackable, UnpackedRgbImage}; @@ -41,11 +42,27 @@ pub(crate) unsafe fn make_morphology_rgb( threading_policy: MorphologyThreadingPolicy, ) -> Result<(), String> where - T: RgbPackable + Copy + 'static + Sync + Send + Clone + Default + MorphNativeOp, + T: RgbPackable + + Copy + + 'static + + Sync + + Send + + Clone + + Default + + MorphNativeOp + + Row2DFilter, { + if src.len() != dst.len() || dst.len() != image_size.width * image_size.height * 3 { + return Err(format!( + "Source and Destination image slice expected to be {} but it was src {}, dst {}", + image_size.width * image_size.height * 3, + src.len(), + dst.len() + )); + } let unpacked = T::unpack(src, image_size); let mut dst_unpacked = UnpackedRgbImage::alloc(image_size); - if let Err(err) = make_morphology::( + make_morphology::( &unpacked.r_channel, &mut dst_unpacked.r_channel, image_size, @@ -53,10 +70,8 @@ where structuring_element_size, border_mode, threading_policy, - ) { - return Err(err); - } - if let Err(err) = make_morphology::( + )?; + make_morphology::( &unpacked.g_channel, &mut dst_unpacked.g_channel, image_size, @@ -64,10 +79,8 @@ where structuring_element_size, border_mode, threading_policy, - ) { - return Err(err); - } - if let Err(err) = make_morphology::( + )?; + make_morphology::( &unpacked.b_channel, &mut dst_unpacked.b_channel, image_size, @@ -75,10 +88,7 @@ where structuring_element_size, border_mode, threading_policy, - ) { - return Err(err); - } - + )?; T::pack(&dst_unpacked, dst, image_size); Ok(()) } diff --git a/src/morph_rgba.rs b/src/morph_rgba.rs index 11883a5..6cd2971 100644 --- a/src/morph_rgba.rs +++ b/src/morph_rgba.rs @@ -26,6 +26,7 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +use crate::filter::Row2DFilter; use crate::morph_base::MorphNativeOp; use crate::op_impl::make_morphology; use crate::packing::{RgbaPackable, UnpackedRgbaImage}; @@ -41,11 +42,27 @@ pub(crate) unsafe fn make_morphology_rgba( threading_policy: MorphologyThreadingPolicy, ) -> Result<(), String> where - T: RgbaPackable + Default + Copy + Clone + Send + Sync + 'static + MorphNativeOp, + T: RgbaPackable + + Default + + Copy + + Clone + + Send + + Sync + + 'static + + MorphNativeOp + + Row2DFilter, { + if src.len() != dst.len() || dst.len() != image_size.width * image_size.height * 4 { + return Err(format!( + "Source and Destination image slice expected to be {} but it was src {}, dst {}", + image_size.width * image_size.height * 2, + src.len(), + dst.len() + )); + } let unpacked = T::unpack(src, image_size); let mut dst_unpacked = UnpackedRgbaImage::alloc(image_size); - if let Err(err) = make_morphology::( + make_morphology::( &unpacked.r_channel, &mut dst_unpacked.r_channel, image_size, @@ -53,10 +70,8 @@ where structuring_element_size, border_mode, threading_policy, - ) { - return Err(err); - } - if let Err(err) = make_morphology::( + )?; + make_morphology::( &unpacked.g_channel, &mut dst_unpacked.g_channel, image_size, @@ -64,10 +79,8 @@ where structuring_element_size, border_mode, threading_policy, - ) { - return Err(err); - } - if let Err(err) = make_morphology::( + )?; + make_morphology::( &unpacked.b_channel, &mut dst_unpacked.b_channel, image_size, @@ -75,10 +88,8 @@ where structuring_element_size, border_mode, threading_policy, - ) { - return Err(err); - } - if let Err(err) = make_morphology::( + )?; + make_morphology::( &unpacked.a_channel, &mut dst_unpacked.a_channel, image_size, @@ -86,9 +97,7 @@ where structuring_element_size, border_mode, threading_policy, - ) { - return Err(err); - } + )?; T::pack(&dst_unpacked, dst, image_size); Ok(()) diff --git a/src/op.rs b/src/op.rs index eb258db..ba69a82 100644 --- a/src/op.rs +++ b/src/op.rs @@ -31,7 +31,7 @@ use crate::morph_gray_alpha::make_morphology_gray_alpha; use crate::morph_rgb::make_morphology_rgb; use crate::morph_rgba::make_morphology_rgba; use crate::op_impl::make_morphology; -use crate::op_type::MorphOp; +use crate::op_type::{MorphExOp, MorphOp}; use crate::structuring_element::KernelShape; use crate::{ImageSize, MorphologyThreadingPolicy}; @@ -306,3 +306,183 @@ pub fn dilate_gray_alpha( ) } } + +/// Morphology a gray (planar) image +/// +/// # Arguments +/// +/// * `src`: Source image slice +/// * `dst`: Destination image slice +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morphology( + src: &[u8], + dst: &mut [u8], + morph_op: MorphExOp, + image_size: ImageSize, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result<(), String> { + match morph_op { + MorphExOp::Dilate => dilate( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + MorphExOp::Erode => erode( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + } +} + +/// Morphology a RGB 8-bit image +/// +/// # Arguments +/// +/// * `src`: Source RGB image slice +/// * `dst`: Destination RGB image slice +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morphology_rgb( + src: &[u8], + dst: &mut [u8], + morph_op: MorphExOp, + image_size: ImageSize, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result<(), String> { + match morph_op { + MorphExOp::Dilate => dilate_rgb( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + MorphExOp::Erode => erode_rgb( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + } +} + +/// Morphology a Planar image with alpha 8-bit image +/// +/// # Arguments +/// +/// * `src`: Source image slice +/// * `dst`: Destination image slice +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morphology_gray_alpha( + src: &[u8], + dst: &mut [u8], + morph_op: MorphExOp, + image_size: ImageSize, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result<(), String> { + match morph_op { + MorphExOp::Dilate => dilate_gray_alpha( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + MorphExOp::Erode => erode_gray_alpha( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + } +} + +/// Morphology a RGBA 8-bit image +/// +/// # Arguments +/// +/// * `src`: Source RGBA image slice +/// * `dst`: Destination RGBA image slice +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morphology_rgba( + src: &[u8], + dst: &mut [u8], + morph_op: MorphExOp, + image_size: ImageSize, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result<(), String> { + match morph_op { + MorphExOp::Dilate => dilate_rgba( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + MorphExOp::Erode => erode_rgba( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + } +} diff --git a/src/op_f32.rs b/src/op_f32.rs index ec42037..4673386 100644 --- a/src/op_f32.rs +++ b/src/op_f32.rs @@ -33,9 +33,9 @@ use crate::morph_rgba::make_morphology_rgba; use crate::op_impl::make_morphology; use crate::op_type::MorphOp; use crate::structuring_element::KernelShape; -use crate::{ImageSize, MorphologyThreadingPolicy}; +use crate::{ImageSize, MorphExOp, MorphologyThreadingPolicy}; -/// Dilate a gray (planar) stored in u16 image +/// Dilate a gray (planar) stored in f32 image /// /// # Arguments /// @@ -69,7 +69,7 @@ pub fn dilate_f32( } } -/// Dilate an RGB stored in u16 image +/// Dilate an RGB stored in f32 image /// /// # Arguments /// @@ -103,7 +103,7 @@ pub fn dilate_rgb_f32( } } -/// Erode a gray (planar) stored in u16 image +/// Erode a gray (planar) stored in f32 image /// /// # Arguments /// @@ -137,7 +137,7 @@ pub fn erode_f32( } } -/// Erode an RGB image stored in u16 +/// Erode an RGB image stored in f32 /// /// # Arguments /// @@ -171,7 +171,7 @@ pub fn erode_rgb_f32( } } -/// Erode an RGBA image stored in u16 +/// Erode an RGBA image stored in f32 /// /// # Arguments /// @@ -205,7 +205,7 @@ pub fn erode_rgba_f32( } } -/// Dilate an RGBA image stored in u16 +/// Dilate an RGBA image stored in f32 /// /// # Arguments /// @@ -306,3 +306,93 @@ pub fn dilate_gray_alpha_f32( ) } } + +/// Morphology an RGBA image stored in f32 +/// +/// # Arguments +/// +/// * `src`: Source slice with RGBA data +/// * `dst`: Destination slice for RGBA data +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morphology_rgba_f32( + src: &[f32], + dst: &mut [f32], + morph_op: MorphExOp, + image_size: ImageSize, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result<(), String> { + match morph_op { + MorphExOp::Dilate => dilate_rgba_f32( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + MorphExOp::Erode => erode_rgba_f32( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + } +} + +/// Morphology an RGB image stored in f32 +/// +/// # Arguments +/// +/// * `src`: Source slice with RGBA data +/// * `dst`: Destination slice for RGBA data +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morphology_rgb_f32( + src: &[f32], + dst: &mut [f32], + morph_op: MorphExOp, + image_size: ImageSize, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result<(), String> { + match morph_op { + MorphExOp::Dilate => dilate_rgb_f32( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + MorphExOp::Erode => erode_rgb_f32( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + } +} diff --git a/src/op_impl.rs b/src/op_impl.rs index d45596d..56d6f87 100644 --- a/src/op_impl.rs +++ b/src/op_impl.rs @@ -28,7 +28,7 @@ */ use crate::arena::make_arena; use crate::border_mode::BorderMode; -use crate::filter::MorthFilterFlat2DRow; +use crate::filter::Row2DFilter; use crate::filter_op_declare::MorthOpFilterFlat2DRow; use crate::morph_base::MorphNativeOp; use crate::op_type::MorphOp; @@ -48,7 +48,7 @@ pub(crate) unsafe fn make_morphology( threading_policy: MorphologyThreadingPolicy, ) -> Result<(), String> where - T: Copy + Default + 'static + Send + Sync + MorphNativeOp, + T: Copy + Default + 'static + Send + Sync + MorphNativeOp + Row2DFilter, { if src.len() != dst.len() { return Err("Source slice size and destination must match" @@ -88,7 +88,7 @@ where let op_type: MorphOp = OP_TYPE.into(); - let filter = Arc::new(MorthFilterFlat2DRow::new(op_type)); + let filter = Arc::new(T::get_filter(op_type)); let arena = make_arena::( src, diff --git a/src/op_type.rs b/src/op_type.rs index 9b0dd19..dc8697d 100644 --- a/src/op_type.rs +++ b/src/op_type.rs @@ -27,7 +27,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#[repr(u8)] +#[repr(C)] #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub(crate) enum MorphOp { Dilate = 0, @@ -43,3 +43,10 @@ impl From for MorphOp { } } } + +#[repr(C)] +#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub enum MorphExOp { + Dilate = 0, + Erode = 1, +} diff --git a/src/op_u16.rs b/src/op_u16.rs index a8c5f4c..892432f 100644 --- a/src/op_u16.rs +++ b/src/op_u16.rs @@ -33,7 +33,7 @@ use crate::morph_rgba::make_morphology_rgba; use crate::op_impl::make_morphology; use crate::op_type::MorphOp; use crate::structuring_element::KernelShape; -use crate::{ImageSize, MorphologyThreadingPolicy}; +use crate::{ImageSize, MorphExOp, MorphologyThreadingPolicy}; /// Dilate a gray (planar) stored in u16 image /// @@ -306,3 +306,183 @@ pub fn dilate_gray_alpha_u16( ) } } + +/// Morphology Gray image with alpha stored in u16 +/// +/// # Arguments +/// +/// * `src`: Source slice with Gray with alpha data +/// * `dst`: Destination slice for Gray with alpha data +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morphology_gray_alpha_u16( + src: &[u16], + dst: &mut [u16], + morph_op: MorphExOp, + image_size: ImageSize, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result<(), String> { + match morph_op { + MorphExOp::Dilate => dilate_gray_alpha_u16( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + MorphExOp::Erode => erode_gray_alpha_u16( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + } +} + +/// Morphology Gray image stored in u16 +/// +/// # Arguments +/// +/// * `src`: Source slice with Gray data +/// * `dst`: Destination slice for Gray data +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morphology_gray_u16( + src: &[u16], + dst: &mut [u16], + morph_op: MorphExOp, + image_size: ImageSize, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result<(), String> { + match morph_op { + MorphExOp::Dilate => dilate_u16( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + MorphExOp::Erode => erode_u16( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + } +} + +/// Morphology RGB image stored in u16 +/// +/// # Arguments +/// +/// * `src`: Source slice with RGB data +/// * `dst`: Destination slice for RGB data +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morphology_rgb_u16( + src: &[u16], + dst: &mut [u16], + morph_op: MorphExOp, + image_size: ImageSize, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result<(), String> { + match morph_op { + MorphExOp::Dilate => dilate_rgb_u16( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + MorphExOp::Erode => erode_rgb_u16( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + } +} + +/// Morphology RGBA image stored in u16 +/// +/// # Arguments +/// +/// * `src`: Source slice with RGBA data +/// * `dst`: Destination slice for RGBA data +/// * `morph_op`: Requested [MorphExOp] +/// * `image_size`: Image size declared by [ImageSize] +/// * `structuring_element`: 2D structuring element +/// * `structuring_element_size`: (W,H) structuring element size +/// * `border_mode`: Border handling mode, for reference see [BorderMode] +/// * `threading_policy`: Threads usage policy +/// +pub fn morphology_rgba_u16( + src: &[u16], + dst: &mut [u16], + morph_op: MorphExOp, + image_size: ImageSize, + structuring_element: &[u8], + structuring_element_size: KernelShape, + border_mode: BorderMode, + threading_policy: MorphologyThreadingPolicy, +) -> Result<(), String> { + match morph_op { + MorphExOp::Dilate => dilate_rgba_u16( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + MorphExOp::Erode => erode_rgba_u16( + src, + dst, + image_size, + structuring_element, + structuring_element_size, + border_mode, + threading_policy, + ), + } +} diff --git a/src/ops/neon/mod.rs b/src/ops/neon/mod.rs index 8deee91..22906e0 100644 --- a/src/ops/neon/mod.rs +++ b/src/ops/neon/mod.rs @@ -27,5 +27,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ mod morph_op; +mod morph_op_f32; +mod morph_op_u16; pub use morph_op::MorphOpFilterNeon2DRow; +pub use morph_op_f32::MorphOpFilterNeon2DRowF32; +pub use morph_op_u16::MorphOpFilterNeon2DRowU16; diff --git a/src/ops/neon/morph_op_f32.rs b/src/ops/neon/morph_op_f32.rs new file mode 100644 index 0000000..9064538 --- /dev/null +++ b/src/ops/neon/morph_op_f32.rs @@ -0,0 +1,166 @@ +/* + * Copyright (c) Radzivon Bartoshyk. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +use crate::filter_op_declare::{Arena, MorthOpFilterFlat2DRow}; +use crate::flat_se::AnalyzedSe; +use crate::morph_base::MorphNativeOp; +use crate::op_type::MorphOp; +use crate::unsafe_slice::UnsafeSlice; +use crate::ImageSize; +#[cfg(target_arch = "aarch64")] +use std::arch::aarch64::*; +#[cfg(target_arch = "arm")] +use std::arch::arm::*; + +#[derive(Clone)] +pub struct MorphOpFilterNeon2DRowF32 {} + +impl Default for MorphOpFilterNeon2DRowF32 { + fn default() -> Self { + MorphOpFilterNeon2DRowF32 {} + } +} + +impl MorthOpFilterFlat2DRow for MorphOpFilterNeon2DRowF32 +where + T: Copy + 'static, +{ + unsafe fn dispatch_row( + &self, + arena: &Arena, + dst: &UnsafeSlice, + image_size: ImageSize, + analyzed_se: AnalyzedSe, + y: usize, + ) { + let width = image_size.width; + + let op_type: MorphOp = OP_TYPE.into(); + let stride = width; + + let decision = match op_type { + MorphOp::Dilate => vmaxq_f32, + MorphOp::Erode => vminq_f32, + }; + + let decision_half = match op_type { + MorphOp::Dilate => vmax_f32, + MorphOp::Erode => vmin_f32, + }; + + let src: &Vec = std::mem::transmute(&arena.arena); + let dst: &UnsafeSlice = std::mem::transmute(dst); + + let dx = arena.pad_w as i32; + let dy = arena.pad_h as i32; + + let arena_stride = arena.width; + + let offsets = analyzed_se + .left_front + .element_offsets + .iter() + .map(|&x| { + src.get_unchecked( + ((x.y + dy + y as i32) as usize * arena_stride + (x.x + dx) as usize).., + ) + }) + .collect::>(); + + let length = analyzed_se.left_front.element_offsets.iter().len(); + + let mut _cx = 0usize; + + while _cx + 16 < width { + let mut rows = vld1q_f32_x4((*offsets.get_unchecked(0).get_unchecked(_cx..)).as_ptr()); + + for i in 1..length { + let new_rows = + vld1q_f32_x4((*offsets.get_unchecked(i)).get_unchecked(_cx..).as_ptr()); + rows.0 = decision(rows.0, new_rows.0); + rows.1 = decision(rows.1, new_rows.1); + rows.2 = decision(rows.2, new_rows.2); + rows.3 = decision(rows.3, new_rows.3); + } + + vst1q_f32_x4(dst.slice.as_ptr().add(y * stride + _cx) as *mut f32, rows); + + _cx += 16; + } + + while _cx + 8 < width { + let mut rows = vld1q_f32_x2((*offsets.get_unchecked(0).get_unchecked(_cx..)).as_ptr()); + + for i in 1..length { + let new_rows = + vld1q_f32_x2((*offsets.get_unchecked(i)).get_unchecked(_cx..).as_ptr()); + rows.0 = decision(rows.0, new_rows.0); + rows.1 = decision(rows.1, new_rows.1); + } + + vst1q_f32_x2(dst.slice.as_ptr().add(y * stride + _cx) as *mut f32, rows); + + _cx += 8; + } + + while _cx + 4 < width { + let mut rows = vld1q_f32((*offsets.get_unchecked(0).get_unchecked(_cx..)).as_ptr()); + + for i in 1..length { + let new_row = vld1q_f32((*offsets.get_unchecked(i)).get_unchecked(_cx..).as_ptr()); + rows = decision(rows, new_row); + } + + vst1q_f32(dst.slice.as_ptr().add(y * stride + _cx) as *mut f32, rows); + + _cx += 4; + } + + while _cx + 2 < width { + let mut rows = vld1_f32((*offsets.get_unchecked(0).get_unchecked(_cx..)).as_ptr()); + + for i in 1..length { + let new_row = vld1_f32((*offsets.get_unchecked(i)).get_unchecked(_cx..).as_ptr()); + rows = decision_half(rows, new_row); + } + + vst1_f32(dst.slice.as_ptr().add(y * stride + _cx) as *mut f32, rows); + + _cx += 2; + } + + for x in _cx..width { + let mut k0 = *(*offsets.get_unchecked(0)).get_unchecked(x); + + for i in 1..length { + k0 = k0.op::(*(*offsets.get_unchecked(i)).get_unchecked(x)); + } + dst.write(y * stride + x, k0); + } + } +} diff --git a/src/ops/neon/morph_op_u16.rs b/src/ops/neon/morph_op_u16.rs new file mode 100644 index 0000000..f905dd9 --- /dev/null +++ b/src/ops/neon/morph_op_u16.rs @@ -0,0 +1,166 @@ +/* + * Copyright (c) Radzivon Bartoshyk. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +use crate::filter_op_declare::{Arena, MorthOpFilterFlat2DRow}; +use crate::flat_se::AnalyzedSe; +use crate::morph_base::MorphNativeOp; +use crate::op_type::MorphOp; +use crate::unsafe_slice::UnsafeSlice; +use crate::ImageSize; +#[cfg(target_arch = "aarch64")] +use std::arch::aarch64::*; +#[cfg(target_arch = "arm")] +use std::arch::arm::*; + +#[derive(Clone)] +pub struct MorphOpFilterNeon2DRowU16 {} + +impl Default for MorphOpFilterNeon2DRowU16 { + fn default() -> Self { + MorphOpFilterNeon2DRowU16 {} + } +} + +impl MorthOpFilterFlat2DRow for MorphOpFilterNeon2DRowU16 +where + T: Copy + 'static, +{ + unsafe fn dispatch_row( + &self, + arena: &Arena, + dst: &UnsafeSlice, + image_size: ImageSize, + analyzed_se: AnalyzedSe, + y: usize, + ) { + let width = image_size.width; + + let op_type: MorphOp = OP_TYPE.into(); + let stride = width; + + let decision = match op_type { + MorphOp::Dilate => vmaxq_u16, + MorphOp::Erode => vminq_u16, + }; + + let decision_half = match op_type { + MorphOp::Dilate => vmax_u16, + MorphOp::Erode => vmin_u16, + }; + + let src: &Vec = std::mem::transmute(&arena.arena); + let dst: &UnsafeSlice = std::mem::transmute(dst); + + let dx = arena.pad_w as i32; + let dy = arena.pad_h as i32; + + let arena_stride = arena.width; + + let offsets = analyzed_se + .left_front + .element_offsets + .iter() + .map(|&x| { + src.get_unchecked( + ((x.y + dy + y as i32) as usize * arena_stride + (x.x + dx) as usize).., + ) + }) + .collect::>(); + + let length = analyzed_se.left_front.element_offsets.iter().len(); + + let mut _cx = 0usize; + + while _cx + 32 < width { + let mut rows = vld1q_u16_x4((*offsets.get_unchecked(0).get_unchecked(_cx..)).as_ptr()); + + for i in 1..length { + let new_rows = + vld1q_u16_x4((*offsets.get_unchecked(i)).get_unchecked(_cx..).as_ptr()); + rows.0 = decision(rows.0, new_rows.0); + rows.1 = decision(rows.1, new_rows.1); + rows.2 = decision(rows.2, new_rows.2); + rows.3 = decision(rows.3, new_rows.3); + } + + vst1q_u16_x4(dst.slice.as_ptr().add(y * stride + _cx) as *mut u16, rows); + + _cx += 32; + } + + while _cx + 16 < width { + let mut rows = vld1q_u16_x2((*offsets.get_unchecked(0).get_unchecked(_cx..)).as_ptr()); + + for i in 1..length { + let new_rows = + vld1q_u16_x2((*offsets.get_unchecked(i)).get_unchecked(_cx..).as_ptr()); + rows.0 = decision(rows.0, new_rows.0); + rows.1 = decision(rows.1, new_rows.1); + } + + vst1q_u16_x2(dst.slice.as_ptr().add(y * stride + _cx) as *mut u16, rows); + + _cx += 16; + } + + while _cx + 8 < width { + let mut rows = vld1q_u16((*offsets.get_unchecked(0).get_unchecked(_cx..)).as_ptr()); + + for i in 1..length { + let new_row = vld1q_u16((*offsets.get_unchecked(i)).get_unchecked(_cx..).as_ptr()); + rows = decision(rows, new_row); + } + + vst1q_u16(dst.slice.as_ptr().add(y * stride + _cx) as *mut u16, rows); + + _cx += 8; + } + + while _cx + 4 < width { + let mut rows = vld1_u16((*offsets.get_unchecked(0).get_unchecked(_cx..)).as_ptr()); + + for i in 1..length { + let new_row = vld1_u16((*offsets.get_unchecked(i)).get_unchecked(_cx..).as_ptr()); + rows = decision_half(rows, new_row); + } + + vst1_u16(dst.slice.as_ptr().add(y * stride + _cx) as *mut u16, rows); + + _cx += 4; + } + + for x in _cx..width { + let mut k0 = *(*offsets.get_unchecked(0)).get_unchecked(x); + + for i in 1..length { + k0 = k0.op::(*(*offsets.get_unchecked(i)).get_unchecked(x)); + } + dst.write(y * stride + x, k0); + } + } +} diff --git a/src/packing/traits.rs b/src/packing/traits.rs index 6cf629f..adc9019 100644 --- a/src/packing/traits.rs +++ b/src/packing/traits.rs @@ -79,7 +79,7 @@ pub trait RgbaPackable { impl RgbaPackable for u8 { fn pack(unpacked_rgb_image: &UnpackedRgbaImage, dst: &mut [u8], image_size: ImageSize) { - pack_rgba(&unpacked_rgb_image, dst, image_size) + pack_rgba(unpacked_rgb_image, dst, image_size) } fn unpack(src: &[u8], image_size: ImageSize) -> UnpackedRgbaImage { @@ -114,12 +114,7 @@ pub trait GrayAlphaPackable { impl GrayAlphaPackable for u8 { fn pack(unpacked_rgb_image: &UnpackedGrayAlpha, dst: &mut [u8], image_size: ImageSize) { - pack_gray_alpha_naive( - &unpacked_rgb_image, - dst, - image_size.width, - image_size.height, - ) + pack_gray_alpha_naive(unpacked_rgb_image, dst, image_size.width, image_size.height) } fn unpack(src: &[u8], image_size: ImageSize) -> UnpackedGrayAlpha { @@ -129,12 +124,7 @@ impl GrayAlphaPackable for u8 { impl GrayAlphaPackable for u16 { fn pack(unpacked_rgb_image: &UnpackedGrayAlpha, dst: &mut [u16], image_size: ImageSize) { - pack_gray_alpha_naive( - &unpacked_rgb_image, - dst, - image_size.width, - image_size.height, - ) + pack_gray_alpha_naive(unpacked_rgb_image, dst, image_size.width, image_size.height) } fn unpack(src: &[u16], image_size: ImageSize) -> UnpackedGrayAlpha { @@ -144,12 +134,7 @@ impl GrayAlphaPackable for u16 { impl GrayAlphaPackable for f32 { fn pack(unpacked_rgb_image: &UnpackedGrayAlpha, dst: &mut [f32], image_size: ImageSize) { - pack_gray_alpha_naive( - &unpacked_rgb_image, - dst, - image_size.width, - image_size.height, - ) + pack_gray_alpha_naive(unpacked_rgb_image, dst, image_size.width, image_size.height) } fn unpack(src: &[f32], image_size: ImageSize) -> UnpackedGrayAlpha {