use crate::error::Result;
use ndarray::Array2;
pub trait PostProcessor: Send + Sync {
fn process_mask(&self, mask: &Array2<u8>) -> Result<Array2<u8>>;
fn name(&self) -> &'static str;
fn description(&self) -> &'static str;
fn validate_parameters(&self) -> Result<()> {
Ok(())
}
}
pub struct CompositePostProcessor {
processors: Vec<Box<dyn PostProcessor>>,
}
impl CompositePostProcessor {
pub fn new() -> Self {
Self {
processors: Vec::new(),
}
}
pub fn add_processor(mut self, processor: Box<dyn PostProcessor>) -> Self {
self.processors.push(processor);
self
}
pub fn len(&self) -> usize {
self.processors.len()
}
pub fn is_empty(&self) -> bool {
self.processors.is_empty()
}
}
impl Default for CompositePostProcessor {
fn default() -> Self {
Self::new()
}
}
impl PostProcessor for CompositePostProcessor {
fn process_mask(&self, mask: &Array2<u8>) -> Result<Array2<u8>> {
let mut result = mask.clone();
for processor in &self.processors {
result = processor.process_mask(&result)?;
}
Ok(result)
}
fn name(&self) -> &'static str {
"Composite"
}
fn description(&self) -> &'static str {
"Applies multiple post-processors in sequence"
}
fn validate_parameters(&self) -> Result<()> {
for processor in &self.processors {
processor.validate_parameters()?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct PixelBufferProcessor {
radius: usize,
kernel: Array2<bool>,
}
impl PixelBufferProcessor {
pub fn new(radius: usize) -> Self {
let kernel = create_circular_kernel(radius);
Self { radius, kernel }
}
pub fn radius(&self) -> usize {
self.radius
}
fn create_kernel(&self) -> &Array2<bool> {
&self.kernel
}
}
impl PostProcessor for PixelBufferProcessor {
fn process_mask(&self, mask: &Array2<u8>) -> Result<Array2<u8>> {
if self.radius == 0 {
return Ok(mask.clone());
}
let (height, width) = mask.dim();
let mut result = Array2::zeros((height, width));
let kernel = self.create_kernel();
let (kernel_height, kernel_width) = kernel.dim();
let kernel_center_y = kernel_height / 2;
let kernel_center_x = kernel_width / 2;
for y in 0..height {
for x in 0..width {
let mut should_mask = false;
for ky in 0..kernel_height {
for kx in 0..kernel_width {
if !kernel[[ky, kx]] {
continue;
}
let img_y = y as i32 + ky as i32 - kernel_center_y as i32;
let img_x = x as i32 + kx as i32 - kernel_center_x as i32;
if img_y >= 0
&& img_y < height as i32
&& img_x >= 0
&& img_x < width as i32
&& mask[[img_y as usize, img_x as usize]] > 0
{
should_mask = true;
break;
}
}
if should_mask {
break;
}
}
result[[y, x]] = if should_mask { 1 } else { 0 };
}
}
Ok(result)
}
fn name(&self) -> &'static str {
"PixelBuffer"
}
fn description(&self) -> &'static str {
"Dilates the mask by a specified pixel radius"
}
fn validate_parameters(&self) -> Result<()> {
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct MetashapeConverter;
impl MetashapeConverter {
pub fn new() -> Self {
Self
}
}
impl Default for MetashapeConverter {
fn default() -> Self {
Self::new()
}
}
impl PostProcessor for MetashapeConverter {
fn process_mask(&self, mask: &Array2<u8>) -> Result<Array2<u8>> {
let result = mask.map(|&pixel| if pixel > 0 { 0 } else { 255 });
Ok(result)
}
fn name(&self) -> &'static str {
"Metashape"
}
fn description(&self) -> &'static str {
"Converts mask to Metashape format (inverted, 0-255 range)"
}
}
fn create_circular_kernel(radius: usize) -> Array2<bool> {
let size = 2 * radius + 1;
let mut kernel = Array2::from_elem((size, size), false);
let center = radius as i32;
for y in 0..size {
for x in 0..size {
let dy = y as i32 - center;
let dx = x as i32 - center;
let distance_sq = dx * dx + dy * dy;
if distance_sq <= (radius as i32) * (radius as i32) {
kernel[[y, x]] = true;
}
}
}
kernel
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_circular_kernel() {
let kernel = create_circular_kernel(1);
assert_eq!(kernel.dim(), (3, 3));
assert!(kernel[[1, 1]]);
assert!(kernel[[0, 1]]);
assert!(kernel[[1, 0]]);
assert!(kernel[[2, 1]]);
assert!(kernel[[1, 2]]);
assert!(!kernel[[0, 0]]);
assert!(!kernel[[2, 2]]);
}
#[test]
fn test_pixel_buffer_processor() {
let mut mask = ndarray::Array2::zeros((5, 5));
mask[[2, 2]] = 1;
let processor = PixelBufferProcessor::new(1);
let result = processor.process_mask(&mask).unwrap();
assert_eq!(result[[2, 2]], 1);
assert_eq!(result[[1, 2]], 1);
assert_eq!(result[[3, 2]], 1);
assert_eq!(result[[2, 1]], 1);
assert_eq!(result[[2, 3]], 1);
assert_eq!(result[[0, 0]], 0);
assert_eq!(result[[4, 4]], 0);
}
#[test]
fn test_metashape_converter() {
let mut mask = ndarray::Array2::zeros((3, 3));
mask[[1, 1]] = 1;
let converter = MetashapeConverter::new();
let result = converter.process_mask(&mask).unwrap();
assert_eq!(result[[1, 1]], 0);
assert_eq!(result[[0, 0]], 255);
assert_eq!(result[[2, 2]], 255);
}
#[test]
fn test_composite_processor() {
let mut mask = ndarray::Array2::zeros((5, 5));
mask[[2, 2]] = 1;
let processor = CompositePostProcessor::new()
.add_processor(Box::new(PixelBufferProcessor::new(1)))
.add_processor(Box::new(MetashapeConverter::new()));
let result = processor.process_mask(&mask).unwrap();
assert_eq!(result[[2, 2]], 0); assert_eq!(result[[1, 2]], 0); assert_eq!(result[[0, 0]], 255); }
}