use crate::core::{Pix, PixelDepth};
use crate::morph::{MorphError, MorphResult};
fn check_binary(pix: &Pix) -> MorphResult<()> {
if pix.depth() != PixelDepth::Bit1 {
return Err(MorphError::UnsupportedDepth {
expected: "1-bpp binary",
actual: pix.depth().bits(),
});
}
Ok(())
}
pub fn dilate_brick_dwa(pix: &Pix, hsize: u32, vsize: u32) -> MorphResult<Pix> {
check_binary(pix)?;
if hsize == 0 || vsize == 0 {
return Err(MorphError::InvalidSel(
"hsize and vsize must be > 0".to_string(),
));
}
if hsize == 1 && vsize == 1 {
return Ok(pix.clone());
}
let mut result = if hsize > 1 {
dilate_horizontal_dwa(pix, hsize)?
} else {
pix.clone()
};
if vsize > 1 {
result = dilate_vertical_dwa(&result, vsize)?;
}
Ok(result)
}
pub fn erode_brick_dwa(pix: &Pix, hsize: u32, vsize: u32) -> MorphResult<Pix> {
check_binary(pix)?;
if hsize == 0 || vsize == 0 {
return Err(MorphError::InvalidSel(
"hsize and vsize must be > 0".to_string(),
));
}
if hsize == 1 && vsize == 1 {
return Ok(pix.clone());
}
let mut result = if hsize > 1 {
erode_horizontal_dwa(pix, hsize)?
} else {
pix.clone()
};
if vsize > 1 {
result = erode_vertical_dwa(&result, vsize)?;
}
Ok(result)
}
pub fn open_brick_dwa(pix: &Pix, hsize: u32, vsize: u32) -> MorphResult<Pix> {
let eroded = erode_brick_dwa(pix, hsize, vsize)?;
dilate_brick_dwa(&eroded, hsize, vsize)
}
pub fn close_brick_dwa(pix: &Pix, hsize: u32, vsize: u32) -> MorphResult<Pix> {
let dilated = dilate_brick_dwa(pix, hsize, vsize)?;
erode_brick_dwa(&dilated, hsize, vsize)
}
fn shift_row(row: &[u32], wpl: usize, shift: i32, out: &mut [u32]) {
let abs_shift = shift.unsigned_abs() as usize;
let word_offset = abs_shift / 32;
let bit_shift = (abs_shift % 32) as u32;
for i in 0..wpl {
if shift > 0 {
let src_idx = i + word_offset;
let hi = if src_idx < wpl { row[src_idx] } else { 0 };
let lo = if src_idx + 1 < wpl {
row[src_idx + 1]
} else {
0
};
out[i] = if bit_shift == 0 {
hi
} else {
(hi << bit_shift) | (lo >> (32 - bit_shift))
};
} else if shift < 0 {
let hi = if i > word_offset {
row[i - word_offset - 1]
} else {
0
};
let lo = if i >= word_offset {
row[i - word_offset]
} else {
0
};
out[i] = if bit_shift == 0 {
lo
} else {
(lo >> bit_shift) | (hi << (32 - bit_shift))
};
} else {
out[i] = row[i];
}
}
}
fn last_word_mask(width: u32) -> u32 {
let rem = width % 32;
if rem == 0 { !0 } else { !0u32 << (32 - rem) }
}
fn dilate_horizontal_dwa(pix: &Pix, hsize: u32) -> MorphResult<Pix> {
let w = pix.width();
let h = pix.height();
let wpl = pix.wpl() as usize;
let origin = (hsize / 2) as i32;
let left = -origin;
let right = hsize as i32 - 1 - origin;
let out_pix = Pix::new(w, h, PixelDepth::Bit1)?;
let mut out_mut = out_pix.try_into_mut().unwrap();
let src_data = pix.data();
let dst_data = out_mut.data_mut();
let mut shifted = vec![0u32; wpl];
let mask = last_word_mask(w);
for y in 0..h as usize {
let src_row = &src_data[y * wpl..(y + 1) * wpl];
let dst_row = &mut dst_data[y * wpl..(y + 1) * wpl];
for dst_word in dst_row.iter_mut() {
*dst_word = 0;
}
for d in left..=right {
shift_row(src_row, wpl, d, &mut shifted);
for i in 0..wpl {
dst_row[i] |= shifted[i];
}
}
dst_row[wpl - 1] &= mask;
}
Ok(out_mut.into())
}
fn dilate_vertical_dwa(pix: &Pix, vsize: u32) -> MorphResult<Pix> {
let w = pix.width();
let h = pix.height();
let wpl = pix.wpl();
let origin = (vsize / 2) as i32;
let top = -origin;
let bottom = vsize as i32 - 1 - origin;
let out_pix = Pix::new(w, h, PixelDepth::Bit1)?;
let mut out_mut = out_pix.try_into_mut().unwrap();
let src_data = pix.data();
let dst_data = out_mut.data_mut();
for word_idx in 0..wpl as usize {
for bit in 0..32 {
let x = word_idx * 32 + bit;
if x >= w as usize {
break;
}
let bit_mask = 1u32 << (31 - bit);
for y in 0..h as i32 {
let mut dilated = false;
for dy in top..=bottom {
let sy = y + dy;
if sy >= 0 && sy < h as i32 {
let src_word = src_data[(sy as u32 * wpl) as usize + word_idx];
if src_word & bit_mask != 0 {
dilated = true;
break;
}
}
}
if dilated {
dst_data[(y as u32 * wpl) as usize + word_idx] |= bit_mask;
}
}
}
}
Ok(out_mut.into())
}
fn erode_horizontal_dwa(pix: &Pix, hsize: u32) -> MorphResult<Pix> {
let w = pix.width();
let h = pix.height();
let wpl = pix.wpl() as usize;
let origin = (hsize / 2) as i32;
let left = -origin;
let right = hsize as i32 - 1 - origin;
let out_pix = Pix::new(w, h, PixelDepth::Bit1)?;
let mut out_mut = out_pix.try_into_mut().unwrap();
let src_data = pix.data();
let dst_data = out_mut.data_mut();
let mut shifted = vec![0u32; wpl];
let mask = last_word_mask(w);
for y in 0..h as usize {
let src_row = &src_data[y * wpl..(y + 1) * wpl];
let dst_row = &mut dst_data[y * wpl..(y + 1) * wpl];
for dst_word in dst_row.iter_mut() {
*dst_word = !0;
}
for d in left..=right {
shift_row(src_row, wpl, d, &mut shifted);
for i in 0..wpl {
dst_row[i] &= shifted[i];
}
}
dst_row[wpl - 1] &= mask;
}
Ok(out_mut.into())
}
fn erode_vertical_dwa(pix: &Pix, vsize: u32) -> MorphResult<Pix> {
let w = pix.width();
let h = pix.height();
let wpl = pix.wpl();
let origin = (vsize / 2) as i32;
let top = -origin;
let bottom = vsize as i32 - 1 - origin;
let out_pix = Pix::new(w, h, PixelDepth::Bit1)?;
let mut out_mut = out_pix.try_into_mut().unwrap();
let src_data = pix.data();
let dst_data = out_mut.data_mut();
for word_idx in 0..wpl as usize {
for bit in 0..32 {
let x = word_idx * 32 + bit;
if x >= w as usize {
break;
}
let bit_mask = 1u32 << (31 - bit);
for y in 0..h as i32 {
let mut eroded = true;
for dy in top..=bottom {
let sy = y + dy;
if sy < 0 || sy >= h as i32 {
eroded = false;
break;
}
let src_word = src_data[(sy as u32 * wpl) as usize + word_idx];
if src_word & bit_mask == 0 {
eroded = false;
break;
}
}
if eroded {
dst_data[(y as u32 * wpl) as usize + word_idx] |= bit_mask;
}
}
}
}
Ok(out_mut.into())
}
pub fn get_extended_composite_parameters(size: u32) -> (u32, u32) {
if size <= 63 {
return (0, size.max(1));
}
let n = 1 + (size - 63) / 62;
let extra = size - 63 - (n - 1) * 62 + 1;
(n, extra)
}
pub fn dilate_comp_brick_dwa(pix: &Pix, hsize: u32, vsize: u32) -> MorphResult<Pix> {
check_binary(pix)?;
validate_sizes(hsize, vsize)?;
if hsize > 63 || vsize > 63 {
return dilate_comp_brick_extend_dwa(pix, hsize, vsize);
}
if hsize == 1 && vsize == 1 {
return Ok(pix.clone());
}
composite_dwa_op(pix, hsize, vsize, DwaOp::Dilate)
}
pub fn erode_comp_brick_dwa(pix: &Pix, hsize: u32, vsize: u32) -> MorphResult<Pix> {
check_binary(pix)?;
validate_sizes(hsize, vsize)?;
if hsize > 63 || vsize > 63 {
return erode_comp_brick_extend_dwa(pix, hsize, vsize);
}
if hsize == 1 && vsize == 1 {
return Ok(pix.clone());
}
composite_dwa_op(pix, hsize, vsize, DwaOp::Erode)
}
pub fn open_comp_brick_dwa(pix: &Pix, hsize: u32, vsize: u32) -> MorphResult<Pix> {
let eroded = erode_comp_brick_dwa(pix, hsize, vsize)?;
dilate_comp_brick_dwa(&eroded, hsize, vsize)
}
pub fn close_comp_brick_dwa(pix: &Pix, hsize: u32, vsize: u32) -> MorphResult<Pix> {
let dilated = dilate_comp_brick_dwa(pix, hsize, vsize)?;
erode_comp_brick_dwa(&dilated, hsize, vsize)
}
pub fn dilate_comp_brick_extend_dwa(pix: &Pix, hsize: u32, vsize: u32) -> MorphResult<Pix> {
check_binary(pix)?;
validate_sizes(hsize, vsize)?;
if hsize < 64 && vsize < 64 {
return dilate_comp_brick_dwa(pix, hsize, vsize);
}
let result = extend_dwa_1d(pix, hsize, true, DwaOp::Dilate)?;
extend_dwa_1d(&result, vsize, false, DwaOp::Dilate)
}
pub fn erode_comp_brick_extend_dwa(pix: &Pix, hsize: u32, vsize: u32) -> MorphResult<Pix> {
check_binary(pix)?;
validate_sizes(hsize, vsize)?;
if hsize < 64 && vsize < 64 {
return erode_comp_brick_dwa(pix, hsize, vsize);
}
let result = extend_dwa_1d(pix, hsize, true, DwaOp::Erode)?;
extend_dwa_1d(&result, vsize, false, DwaOp::Erode)
}
pub fn open_comp_brick_extend_dwa(pix: &Pix, hsize: u32, vsize: u32) -> MorphResult<Pix> {
let eroded = erode_comp_brick_extend_dwa(pix, hsize, vsize)?;
dilate_comp_brick_extend_dwa(&eroded, hsize, vsize)
}
pub fn close_comp_brick_extend_dwa(pix: &Pix, hsize: u32, vsize: u32) -> MorphResult<Pix> {
let dilated = dilate_comp_brick_extend_dwa(pix, hsize, vsize)?;
erode_comp_brick_extend_dwa(&dilated, hsize, vsize)
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum DwaOp {
Dilate,
Erode,
}
fn validate_sizes(hsize: u32, vsize: u32) -> MorphResult<()> {
if hsize == 0 || vsize == 0 {
return Err(MorphError::InvalidSel(
"hsize and vsize must be > 0".to_string(),
));
}
Ok(())
}
fn apply_1d_dwa(pix: &Pix, size: u32, horizontal: bool, op: DwaOp) -> MorphResult<Pix> {
let (h, v) = if horizontal { (size, 1) } else { (1, size) };
match op {
DwaOp::Dilate => dilate_brick_dwa(pix, h, v),
DwaOp::Erode => erode_brick_dwa(pix, h, v),
}
}
fn composite_dwa_op(pix: &Pix, hsize: u32, vsize: u32, op: DwaOp) -> MorphResult<Pix> {
let mut result = pix.clone();
if hsize > 1 {
let (s1, s2) = crate::morph::binary::select_composable_sizes(hsize);
result = apply_1d_dwa(&result, s1, true, op)?;
if s2 > 1 {
result = comb_dwa(&result, s2, s1, true, op)?;
}
}
if vsize > 1 {
let (s1, s2) = crate::morph::binary::select_composable_sizes(vsize);
result = apply_1d_dwa(&result, s1, false, op)?;
if s2 > 1 {
result = comb_dwa(&result, s2, s1, false, op)?;
}
}
Ok(result)
}
fn comb_dwa(pix: &Pix, n: u32, spacing: u32, horizontal: bool, op: DwaOp) -> MorphResult<Pix> {
if n <= 1 {
return Ok(pix.clone());
}
let origin = ((n - 1) * spacing) as i32 / 2;
if horizontal {
comb_horizontal_dwa(pix, n, spacing, origin, op)
} else {
comb_vertical_dwa(pix, n, spacing, origin, op)
}
}
fn comb_horizontal_dwa(
pix: &Pix,
n: u32,
spacing: u32,
origin: i32,
op: DwaOp,
) -> MorphResult<Pix> {
let w = pix.width();
let h = pix.height();
let wpl = pix.wpl() as usize;
let out = Pix::new(w, h, PixelDepth::Bit1)?;
let mut out_mut = out.try_into_mut().unwrap();
let src = pix.data();
let dst = out_mut.data_mut();
let mut shifted = vec![0u32; wpl];
let mask = last_word_mask(w);
let init = if op == DwaOp::Dilate { 0u32 } else { !0u32 };
for y in 0..h as usize {
let src_row = &src[y * wpl..(y + 1) * wpl];
let dst_row = &mut dst[y * wpl..(y + 1) * wpl];
dst_row.fill(init);
for k in 0..n {
let d = (k * spacing) as i32 - origin;
shift_row(src_row, wpl, d, &mut shifted);
for i in 0..wpl {
if op == DwaOp::Dilate {
dst_row[i] |= shifted[i];
} else {
dst_row[i] &= shifted[i];
}
}
}
dst_row[wpl - 1] &= mask;
}
Ok(out_mut.into())
}
fn comb_vertical_dwa(pix: &Pix, n: u32, spacing: u32, origin: i32, op: DwaOp) -> MorphResult<Pix> {
let w = pix.width();
let h = pix.height();
let wpl = pix.wpl();
let out = Pix::new(w, h, PixelDepth::Bit1)?;
let mut out_mut = out.try_into_mut().unwrap();
let src = pix.data();
let dst = out_mut.data_mut();
for wi in 0..wpl as usize {
for bit in 0..32u32 {
let x = wi * 32 + bit as usize;
if x >= w as usize {
break;
}
let bm = 1u32 << (31 - bit);
for y in 0..h as i32 {
let mut acc = op == DwaOp::Erode; for k in 0..n {
let sy = y + (k * spacing) as i32 - origin;
if sy >= 0 && sy < h as i32 {
let hit = src[(sy as u32 * wpl) as usize + wi] & bm != 0;
if op == DwaOp::Dilate {
if hit {
acc = true;
break;
}
} else if !hit {
acc = false;
break;
}
} else if op == DwaOp::Erode {
acc = false;
break;
}
}
if acc {
dst[(y as u32 * wpl) as usize + wi] |= bm;
}
}
}
}
Ok(out_mut.into())
}
fn extend_dwa_1d(pix: &Pix, size: u32, horizontal: bool, op: DwaOp) -> MorphResult<Pix> {
if size == 1 {
return Ok(pix.clone());
}
if size < 64 {
return apply_1d_dwa(pix, size, horizontal, op);
}
if size == 64 {
return apply_1d_dwa(pix, 63, horizontal, op);
}
let (n, extra) = get_extended_composite_parameters(size);
let nops = if extra < 3 { n } else { n + 1 };
let mut result;
let mut temp;
if nops & 1 == 1 {
result = if extra > 2 {
apply_1d_dwa(pix, extra, horizontal, op)?
} else {
apply_1d_dwa(pix, 63, horizontal, op)?
};
for _ in 0..nops / 2 {
temp = apply_1d_dwa(&result, 63, horizontal, op)?;
result = apply_1d_dwa(&temp, 63, horizontal, op)?;
}
} else {
temp = if extra > 2 {
apply_1d_dwa(pix, extra, horizontal, op)?
} else {
apply_1d_dwa(pix, 63, horizontal, op)?
};
result = apply_1d_dwa(&temp, 63, horizontal, op)?;
for _ in 0..nops / 2 - 1 {
temp = apply_1d_dwa(&result, 63, horizontal, op)?;
result = apply_1d_dwa(&temp, 63, horizontal, op)?;
}
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_image() -> Pix {
let pix = Pix::new(10, 10, PixelDepth::Bit1).unwrap();
let mut pix_mut = pix.try_into_mut().unwrap();
for y in 3..7 {
for x in 3..7 {
pix_mut.set_pixel_unchecked(x, y, 1);
}
}
pix_mut.into()
}
fn count_foreground_pixels(pix: &Pix) -> u32 {
let mut count = 0;
for y in 0..pix.height() {
for x in 0..pix.width() {
if pix.get_pixel_unchecked(x, y) != 0 {
count += 1;
}
}
}
count
}
#[test]
fn test_dilate_identity() {
let pix = create_test_image();
let original_count = count_foreground_pixels(&pix);
let dilated = dilate_brick_dwa(&pix, 1, 1).unwrap();
let dilated_count = count_foreground_pixels(&dilated);
assert_eq!(original_count, dilated_count);
}
#[test]
fn test_erode_identity() {
let pix = create_test_image();
let original_count = count_foreground_pixels(&pix);
let eroded = erode_brick_dwa(&pix, 1, 1).unwrap();
let eroded_count = count_foreground_pixels(&eroded);
assert_eq!(original_count, eroded_count);
}
#[test]
fn test_dilate_increases_foreground() {
let pix = create_test_image();
let original_count = count_foreground_pixels(&pix);
let dilated = dilate_brick_dwa(&pix, 3, 3).unwrap();
let dilated_count = count_foreground_pixels(&dilated);
assert!(dilated_count >= original_count);
}
#[test]
fn test_erode_decreases_foreground() {
let pix = create_test_image();
let original_count = count_foreground_pixels(&pix);
let eroded = erode_brick_dwa(&pix, 3, 3).unwrap();
let eroded_count = count_foreground_pixels(&eroded);
assert!(eroded_count <= original_count);
}
#[test]
fn test_open_removes_small_objects() {
let pix = Pix::new(10, 10, PixelDepth::Bit1).unwrap();
let mut pix_mut = pix.try_into_mut().unwrap();
for y in 2..8 {
for x in 2..8 {
pix_mut.set_pixel_unchecked(x, y, 1);
}
}
pix_mut.set_pixel_unchecked(0, 0, 1);
let pix: Pix = pix_mut.into();
let original_count = count_foreground_pixels(&pix);
let opened = open_brick_dwa(&pix, 3, 3).unwrap();
let opened_count = count_foreground_pixels(&opened);
assert!(opened_count < original_count);
assert_eq!(opened.get_pixel_unchecked(0, 0), 0);
}
#[test]
fn test_close_fills_holes() {
let pix = Pix::new(10, 10, PixelDepth::Bit1).unwrap();
let mut pix_mut = pix.try_into_mut().unwrap();
for y in 2..8 {
for x in 2..8 {
pix_mut.set_pixel_unchecked(x, y, 1);
}
}
for y in 4..6 {
for x in 4..6 {
pix_mut.set_pixel_unchecked(x, y, 0);
}
}
let pix: Pix = pix_mut.into();
let original_count = count_foreground_pixels(&pix);
let closed = close_brick_dwa(&pix, 3, 3).unwrap();
let closed_count = count_foreground_pixels(&closed);
assert!(closed_count >= original_count);
}
#[test]
fn test_horizontal_only_dilate() {
let pix = create_test_image();
let dilated = dilate_brick_dwa(&pix, 5, 1).unwrap();
for x in 1..9 {
assert_eq!(
dilated.get_pixel_unchecked(x, 5),
1,
"Expected pixel at ({}, 5) to be set",
x
);
}
}
#[test]
fn test_vertical_only_dilate() {
let pix = create_test_image();
let dilated = dilate_brick_dwa(&pix, 1, 5).unwrap();
for y in 1..9 {
assert_eq!(
dilated.get_pixel_unchecked(5, y),
1,
"Expected pixel at (5, {}) to be set",
y
);
}
}
#[test]
fn test_invalid_size() {
let pix = create_test_image();
assert!(dilate_brick_dwa(&pix, 0, 1).is_err());
assert!(dilate_brick_dwa(&pix, 1, 0).is_err());
assert!(erode_brick_dwa(&pix, 0, 1).is_err());
assert!(erode_brick_dwa(&pix, 1, 0).is_err());
}
#[test]
fn test_non_binary_error() {
let pix = Pix::new(10, 10, PixelDepth::Bit8).unwrap();
assert!(dilate_brick_dwa(&pix, 3, 3).is_err());
assert!(erode_brick_dwa(&pix, 3, 3).is_err());
}
#[test]
fn test_comparison_with_regular_morph() {
use crate::morph::binary::{dilate_brick, erode_brick};
let pix = create_test_image();
let dwa_dilated = dilate_brick_dwa(&pix, 3, 3).unwrap();
let regular_dilated = dilate_brick(&pix, 3, 3).unwrap();
for y in 0..pix.height() {
for x in 0..pix.width() {
let dwa_val = dwa_dilated.get_pixel_unchecked(x, y);
let regular_val = regular_dilated.get_pixel_unchecked(x, y);
assert_eq!(dwa_val, regular_val, "Dilation mismatch at ({}, {})", x, y);
}
}
let dwa_eroded = erode_brick_dwa(&pix, 3, 3).unwrap();
let regular_eroded = erode_brick(&pix, 3, 3).unwrap();
for y in 0..pix.height() {
for x in 0..pix.width() {
let dwa_val = dwa_eroded.get_pixel_unchecked(x, y);
let regular_val = regular_eroded.get_pixel_unchecked(x, y);
assert_eq!(dwa_val, regular_val, "Erosion mismatch at ({}, {})", x, y);
}
}
}
#[test]
fn test_large_structuring_element() {
let pix = create_test_image();
let dilated = dilate_brick_dwa(&pix, 7, 7).unwrap();
let eroded = erode_brick_dwa(&pix, 7, 7).unwrap();
assert!(count_foreground_pixels(&dilated) > count_foreground_pixels(&pix));
assert!(count_foreground_pixels(&eroded) < count_foreground_pixels(&pix));
}
#[test]
fn test_get_extended_composite_parameters() {
assert_eq!(get_extended_composite_parameters(1), (0, 1));
assert_eq!(get_extended_composite_parameters(63), (0, 63));
assert_eq!(get_extended_composite_parameters(64), (1, 2));
assert_eq!(get_extended_composite_parameters(65), (1, 3));
assert_eq!(get_extended_composite_parameters(125), (2, 1));
assert_eq!(get_extended_composite_parameters(200), (3, 14));
}
#[test]
fn test_comp_dilate_identity() {
let pix = create_test_image();
let dilated = dilate_comp_brick_dwa(&pix, 1, 1).unwrap();
assert_eq!(
count_foreground_pixels(&dilated),
count_foreground_pixels(&pix)
);
}
#[test]
fn test_comp_erode_identity() {
let pix = create_test_image();
let eroded = erode_comp_brick_dwa(&pix, 1, 1).unwrap();
assert_eq!(
count_foreground_pixels(&eroded),
count_foreground_pixels(&pix)
);
}
#[test]
fn test_comp_dilate_increases_foreground() {
let pix = create_test_image();
let original = count_foreground_pixels(&pix);
let dilated = dilate_comp_brick_dwa(&pix, 3, 3).unwrap();
assert!(count_foreground_pixels(&dilated) >= original);
}
#[test]
fn test_comp_erode_decreases_foreground() {
let pix = create_test_image();
let original = count_foreground_pixels(&pix);
let eroded = erode_comp_brick_dwa(&pix, 3, 3).unwrap();
assert!(count_foreground_pixels(&eroded) <= original);
}
#[test]
fn test_comp_open_removes_small_objects() {
let pix = Pix::new(10, 10, PixelDepth::Bit1).unwrap();
let mut pm = pix.try_into_mut().unwrap();
for y in 2..8 {
for x in 2..8 {
pm.set_pixel_unchecked(x, y, 1);
}
}
pm.set_pixel_unchecked(0, 0, 1);
let pix: Pix = pm.into();
let opened = open_comp_brick_dwa(&pix, 3, 3).unwrap();
assert_eq!(opened.get_pixel_unchecked(0, 0), 0);
}
#[test]
fn test_comp_close_fills_holes() {
let pix = Pix::new(10, 10, PixelDepth::Bit1).unwrap();
let mut pm = pix.try_into_mut().unwrap();
for y in 2..8 {
for x in 2..8 {
pm.set_pixel_unchecked(x, y, 1);
}
}
for y in 4..6 {
for x in 4..6 {
pm.set_pixel_unchecked(x, y, 0);
}
}
let pix: Pix = pm.into();
let original = count_foreground_pixels(&pix);
let closed = close_comp_brick_dwa(&pix, 3, 3).unwrap();
assert!(count_foreground_pixels(&closed) >= original);
}
#[test]
fn test_extend_dilate_large_se() {
let pix = Pix::new(200, 200, PixelDepth::Bit1).unwrap();
let mut pm = pix.try_into_mut().unwrap();
for y in 90..110 {
for x in 90..110 {
pm.set_pixel_unchecked(x, y, 1);
}
}
let pix: Pix = pm.into();
let original = count_foreground_pixels(&pix);
let dilated = dilate_comp_brick_extend_dwa(&pix, 70, 70).unwrap();
assert!(count_foreground_pixels(&dilated) > original);
}
#[test]
fn test_extend_erode_large_se() {
let pix = Pix::new(200, 200, PixelDepth::Bit1).unwrap();
let mut pm = pix.try_into_mut().unwrap();
for y in 10..190 {
for x in 10..190 {
pm.set_pixel_unchecked(x, y, 1);
}
}
let pix: Pix = pm.into();
let original = count_foreground_pixels(&pix);
let eroded = erode_comp_brick_extend_dwa(&pix, 70, 70).unwrap();
assert!(count_foreground_pixels(&eroded) < original);
}
#[test]
fn test_comp_dilate_delegates_to_extend_for_large_se() {
let pix = Pix::new(200, 200, PixelDepth::Bit1).unwrap();
let mut pm = pix.try_into_mut().unwrap();
for y in 90..110 {
for x in 90..110 {
pm.set_pixel_unchecked(x, y, 1);
}
}
let pix: Pix = pm.into();
let result = dilate_comp_brick_dwa(&pix, 100, 100);
assert!(result.is_ok());
}
#[test]
fn test_extend_open_large_se() {
let pix = Pix::new(200, 200, PixelDepth::Bit1).unwrap();
let mut pm = pix.try_into_mut().unwrap();
for y in 10..190 {
for x in 10..190 {
pm.set_pixel_unchecked(x, y, 1);
}
}
pm.set_pixel_unchecked(0, 0, 1);
let pix: Pix = pm.into();
let opened = open_comp_brick_extend_dwa(&pix, 70, 70).unwrap();
assert_eq!(opened.get_pixel_unchecked(0, 0), 0);
}
#[test]
fn test_extend_close_large_se() {
let pix = Pix::new(200, 200, PixelDepth::Bit1).unwrap();
let mut pm = pix.try_into_mut().unwrap();
for y in 10..190 {
for x in 10..190 {
pm.set_pixel_unchecked(x, y, 1);
}
}
for y in 95..105 {
for x in 95..105 {
pm.set_pixel_unchecked(x, y, 0);
}
}
let pix: Pix = pm.into();
let closed = close_comp_brick_extend_dwa(&pix, 70, 70).unwrap();
assert_eq!(closed.get_pixel_unchecked(100, 100), 1);
}
}