///|
/// Shared 3x3 morphology kernel: per-channel neighborhood minimum (erode) or
/// maximum (dilate) with clamp-to-edge sampling. Alpha is preserved.
fn Image::morph3(self : Image, take_max : Bool) -> Image {
let out = Image::new(self.width, self.height)
for y in 0.. best {
best = v
}
} else if v < best {
best = v
}
}
}
out.data[base + c] = clamp_byte(best)
}
out.data[base + 3] = self.data[base + 3]
}
}
out
}
///|
/// Dilation: each channel takes the 3x3 neighborhood maximum, growing bright
/// regions by one pixel per application.
pub fn Image::dilate(self : Image) -> Image {
self.morph3(true)
}
///|
/// Erosion: each channel takes the 3x3 neighborhood minimum, shrinking bright
/// regions by one pixel per application.
pub fn Image::erode(self : Image) -> Image {
self.morph3(false)
}
///|
/// Morphological opening (erode then dilate): removes bright specks smaller
/// than the 3x3 element while roughly preserving larger shapes.
pub fn Image::morph_open(self : Image) -> Image {
self.erode().dilate()
}
///|
/// Morphological closing (dilate then erode): fills dark pinholes smaller
/// than the 3x3 element while roughly preserving larger shapes.
pub fn Image::morph_close(self : Image) -> Image {
self.dilate().erode()
}