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 {