mod offsets;
use ndarray::{Array3, ArrayRef3, ArrayView3, ArrayViewMut3};
use crate::Mask;
use offsets::Offsets;
pub fn binary_erosion(
mask: &ArrayRef3<bool>,
kernel: &ArrayRef3<bool>,
iterations: usize,
) -> Mask
{
mask.as_slice_memory_order()
.expect("Morphological operations can only be called on arrays with contiguous memory.");
let mut last_indices = (iterations > 1).then_some(vec![]);
let mut eroded = mask.to_owned();
let mut offsets = Offsets::new(mask, kernel.view(), false);
erode(mask.view(), &mut eroded.view_mut(), &mut offsets, &mut last_indices);
if let Some(mut last_indices) = last_indices {
for it in 1..iterations {
if last_indices.is_empty() {
break;
}
let save_next_indices = it < iterations - 1;
next_it(&mut eroded, &mut offsets, &mut last_indices, save_next_indices, true, false);
}
}
eroded
}
pub fn binary_dilation(
mask: &ArrayRef3<bool>,
kernel: &ArrayRef3<bool>,
iterations: usize,
) -> Mask
{
mask.as_slice_memory_order()
.expect("Morphological operations can only be called on arrays with contiguous memory.");
let mut last_indices = (iterations > 1).then_some(vec![]);
let mut dilated = mask.to_owned();
let mut offsets = Offsets::new(mask, kernel.view(), true);
dilate(mask.view(), &mut dilated, &mut offsets, &mut last_indices);
if let Some(mut last_indices) = last_indices {
for it in 1..iterations {
if last_indices.is_empty() {
break;
}
let save_next_indices = it < iterations - 1;
next_it(&mut dilated, &mut offsets, &mut last_indices, save_next_indices, false, true);
}
}
dilated
}
pub fn binary_opening(
mask: &ArrayRef3<bool>,
kernel: &ArrayRef3<bool>,
iterations: usize,
) -> Mask
{
let eroded = binary_erosion(mask, kernel, iterations);
binary_dilation(&eroded, kernel, iterations)
}
pub fn binary_closing(
mask: &ArrayRef3<bool>,
kernel: &ArrayRef3<bool>,
iterations: usize,
) -> Mask
{
let dilated = binary_dilation(mask, kernel, iterations);
binary_erosion(&dilated, kernel, iterations)
}
fn erode(
mask: ArrayView3<bool>,
out: &mut ArrayViewMut3<bool>,
offsets: &mut Offsets,
last_indices: &mut Option<Vec<isize>>,
) {
let mask = mask.as_slice_memory_order().unwrap();
let out = out.as_slice_memory_order_mut().unwrap();
let ooi_offset = mask.len() as isize;
let mut i = 0;
for (&m, o) in mask.iter().zip(out) {
if m {
for &offset in offsets.range() {
if offset == ooi_offset {
break;
} else {
if !mask[(i + offset) as usize] {
*o = false;
if let Some(last_indices) = last_indices {
last_indices.push(i);
}
break;
}
}
}
}
offsets.next();
i += 1;
}
}
fn dilate(
mask: ArrayView3<bool>,
out: &mut Array3<bool>,
offsets: &mut Offsets,
last_indices: &mut Option<Vec<isize>>,
) {
let mask = mask.as_slice_memory_order().unwrap();
let out = out.as_slice_memory_order_mut().unwrap();
let ooi_offset = mask.len() as isize;
let mut i = 0;
for (&m, o) in mask.iter().zip(out) {
if !m {
for &offset in offsets.range() {
if offset == ooi_offset {
break;
} else {
if mask[(i + offset) as usize] {
*o = true;
if let Some(last_indices) = last_indices {
last_indices.push(i);
}
break;
}
}
}
}
offsets.next();
i += 1;
}
}
fn next_it(
out: &mut Array3<bool>,
offsets: &mut Offsets,
last_indices: &mut Vec<isize>,
save_next_indices: bool,
b1: bool,
b2: bool,
) {
let out = out.as_slice_memory_order_mut().unwrap();
let ooi_offset = out.len() as isize;
let mut new_indices = vec![];
for &i in &*last_indices {
offsets.move_to(i);
for &offset in offsets.range() {
if offset == ooi_offset {
break;
} else {
let out = &mut out[(i + offset) as usize];
if save_next_indices && *out == b1 {
new_indices.push(i + offset);
}
*out = b2;
}
}
}
*last_indices = new_indices;
}