use crate::core::{Pix, PixelDepth};
use crate::region::conncomp::{
ConnectivityType, find_connected_components, label_connected_components,
};
use crate::region::error::{RegionError, RegionResult};
use std::collections::HashSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SizeSelectType {
IfBoth,
IfEither,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SizeSelectRelation {
Gte,
Lte,
}
pub fn pix_select_by_size(
pixs: &Pix,
width_thresh: i32,
height_thresh: i32,
connectivity: ConnectivityType,
select_type: SizeSelectType,
relation: SizeSelectRelation,
) -> RegionResult<Pix> {
if pixs.depth() != PixelDepth::Bit1 {
return Err(RegionError::UnsupportedDepth {
expected: "1-bit",
actual: pixs.depth().bits(),
});
}
let w = pixs.width();
let h = pixs.height();
let components = find_connected_components(pixs, connectivity)?;
if components.is_empty() {
return Pix::new(w, h, PixelDepth::Bit1).map_err(RegionError::Core);
}
let labeled = label_connected_components(pixs, connectivity)?;
let mut keep_labels = HashSet::new();
for comp in &components {
let cw = comp.bounds.w; let ch = comp.bounds.h;
let keep = match (select_type, relation) {
(SizeSelectType::IfBoth, SizeSelectRelation::Gte) => {
cw >= width_thresh && ch >= height_thresh
}
(SizeSelectType::IfBoth, SizeSelectRelation::Lte) => {
cw <= width_thresh && ch <= height_thresh
}
(SizeSelectType::IfEither, SizeSelectRelation::Gte) => {
cw >= width_thresh || ch >= height_thresh
}
(SizeSelectType::IfEither, SizeSelectRelation::Lte) => {
cw <= width_thresh || ch <= height_thresh
}
};
if keep {
keep_labels.insert(comp.label);
}
}
let mut output = Pix::new(w, h, PixelDepth::Bit1)
.map_err(RegionError::Core)?
.try_into_mut()
.unwrap_or_else(|p| p.to_mut());
for y in 0..h {
for x in 0..w {
if let Some(label) = labeled.get_pixel(x, y)
&& label > 0
&& keep_labels.contains(&label)
{
let _ = output.set_pixel(x, y, 1);
}
}
}
Ok(output.into())
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_image(width: u32, height: u32, pixels: &[(u32, u32)]) -> Pix {
let pix = Pix::new(width, height, PixelDepth::Bit1).unwrap();
let mut pix_mut = pix.try_into_mut().unwrap();
for &(x, y) in pixels {
let _ = pix_mut.set_pixel(x, y, 1);
}
pix_mut.into()
}
#[test]
fn test_select_by_size_gte_both() {
let mut pixels = Vec::new();
for y in 0..3 {
for x in 0..3 {
pixels.push((x, y));
}
}
pixels.push((8, 8));
let pix = create_test_image(10, 10, &pixels);
let result = pix_select_by_size(
&pix,
2,
2,
ConnectivityType::FourWay,
SizeSelectType::IfBoth,
SizeSelectRelation::Gte,
)
.unwrap();
let comps = find_connected_components(&result, ConnectivityType::FourWay).unwrap();
assert_eq!(comps.len(), 1);
assert_eq!(comps[0].bounds.w, 3);
assert_eq!(comps[0].bounds.h, 3);
}
#[test]
fn test_select_by_size_lte_both() {
let mut pixels = Vec::new();
for y in 0..3 {
for x in 0..3 {
pixels.push((x, y));
}
}
pixels.push((8, 8));
let pix = create_test_image(10, 10, &pixels);
let result = pix_select_by_size(
&pix,
1,
1,
ConnectivityType::FourWay,
SizeSelectType::IfBoth,
SizeSelectRelation::Lte,
)
.unwrap();
let comps = find_connected_components(&result, ConnectivityType::FourWay).unwrap();
assert_eq!(comps.len(), 1);
assert_eq!(comps[0].pixel_count, 1);
}
#[test]
fn test_select_by_size_gte_either() {
let mut pixels = Vec::new();
for x in 0..4 {
pixels.push((x, 0));
}
for y in 5..9 {
pixels.push((0, y));
}
pixels.push((8, 8));
let pix = create_test_image(10, 10, &pixels);
let result = pix_select_by_size(
&pix,
3,
3,
ConnectivityType::FourWay,
SizeSelectType::IfEither,
SizeSelectRelation::Gte,
)
.unwrap();
let comps = find_connected_components(&result, ConnectivityType::FourWay).unwrap();
assert_eq!(comps.len(), 2);
}
#[test]
fn test_select_by_size_empty_image() {
let pix = create_test_image(10, 10, &[]);
let result = pix_select_by_size(
&pix,
5,
5,
ConnectivityType::FourWay,
SizeSelectType::IfBoth,
SizeSelectRelation::Gte,
)
.unwrap();
let comps = find_connected_components(&result, ConnectivityType::FourWay).unwrap();
assert!(comps.is_empty());
}
#[test]
fn test_select_by_size_wrong_depth() {
let pix = Pix::new(10, 10, PixelDepth::Bit8).unwrap();
let result = pix_select_by_size(
&pix,
5,
5,
ConnectivityType::FourWay,
SizeSelectType::IfBoth,
SizeSelectRelation::Gte,
);
assert!(result.is_err());
}
#[test]
fn test_select_preserves_dimensions() {
let pix = create_test_image(100, 80, &[(10, 10), (50, 50)]);
let result = pix_select_by_size(
&pix,
1,
1,
ConnectivityType::FourWay,
SizeSelectType::IfBoth,
SizeSelectRelation::Gte,
)
.unwrap();
assert_eq!(result.width(), 100);
assert_eq!(result.height(), 80);
assert_eq!(result.depth(), PixelDepth::Bit1);
}
}