diff options
author | cc <cc@localhost> | 2025-08-20 22:14:13 -0700 |
---|---|---|
committer | cc <cc@localhost> | 2025-08-20 22:19:01 -0700 |
commit | 15df0fec1e368599487e1faeaf8ecebd87980781 (patch) | |
tree | 94c9aba0cf1ba340e77b0c773e6869035ad9c8aa |
Initial commit
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Cargo.lock | 7 | ||||
-rw-r--r-- | Cargo.toml | 6 | ||||
-rw-r--r-- | src/binfile.rs | 27 | ||||
-rw-r--r-- | src/label_format.rs | 227 | ||||
-rw-r--r-- | src/large_label_format.rs | 45 | ||||
-rw-r--r-- | src/lib.rs | 33 |
7 files changed, 346 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b925f0e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "label_toolkit" +version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1a7c9f2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "label_toolkit" +version = "0.0.1" +edition = "2024" + +[dependencies] diff --git a/src/binfile.rs b/src/binfile.rs new file mode 100644 index 0000000..5b0b783 --- /dev/null +++ b/src/binfile.rs @@ -0,0 +1,27 @@ +use std::io::{Error,ErrorKind}; + +pub fn dump_u32_vec(file_name: &str, data: Vec<u32>) -> Result<(), Error> { + use std::fs::File; + use std::io::Write; + if let Ok(mut file) = File::create(file_name) { + for value in data { + file.write_all(&value.to_le_bytes())?; + } + } else { + return Err(Error::new(ErrorKind::Other, "Error creating file")); + } + Ok(()) +} + +pub fn dump_u16_vec(file_name: &str, data: Vec<u16>) -> Result<(), Error> { + use std::fs::File; + use std::io::Write; + if let Ok(mut file) = File::create(file_name) { + for value in data { + file.write_all(&value.to_le_bytes())?; + } + } else { + return Err(Error::new(ErrorKind::Other, "Error creating file")); + } + Ok(()) +} diff --git a/src/label_format.rs b/src/label_format.rs new file mode 100644 index 0000000..afbb2ad --- /dev/null +++ b/src/label_format.rs @@ -0,0 +1,227 @@ +pub struct LabelFormat { + pub buffer: Vec<u16>, + pub width: usize, + pub height: usize, +} + +impl LabelFormat { + pub fn get_left(&self, x: usize, y: usize) -> Option<u16> { + if x > 0 { + return Some(self.buffer[(x-1) + y * self.width]); + } + return None; + } + + pub fn get_right(&self, x: usize, y: usize) -> Option<u16> { + if (x+1) < self.width { + return Some(self.buffer[(x+1) + y * self.width]); + } + return None; + } + + pub fn get_up(&self, x: usize, y: usize) -> Option<u16> { + if y > 0 { + return Some(self.buffer[x + (y-1) * self.width]); + } + return None; + } + + pub fn get_down(&self, x: usize, y: usize) -> Option<u16> { + if (y+1) < self.height { + return Some(self.buffer[x + (y+1) * self.width]); + } + return None; + } + + pub fn display(&self, include_space: bool) { + for y in 0..self.height { + for x in 0..self.width { + print!("{:04X}", self.buffer[x + y*self.width]); + if include_space { + print!(" "); + } + } + println!(); + } + } + + pub fn idilate(&mut self) { + let dilated = self.dilate(); + self.buffer = dilated.buffer; + } + + pub fn dilate(&self) -> LabelFormat { + let width = self.width; + let height = self.height; + let mut output_buffer = vec![0u16; width*height]; + for y in 0..height { + for x in 0..width { + let index = x + y * width; + let current_color = self.buffer[index]; + if current_color != 0 { + output_buffer[index] = current_color; + continue; + } + if let Some(color) = self.get_up(x,y) { + if color != 0 { + output_buffer[index] = color; + continue; + } + } + if let Some(color) = self.get_right(x,y) { + if color != 0 { + output_buffer[index] = color; + continue; + } + } + if let Some(color) = self.get_down(x,y) { + if color != 0 { + output_buffer[index] = color; + continue; + } + } + if let Some(color) = self.get_left(x,y) { + if color != 0 { + output_buffer[index] = color; + continue; + } + } + } + } + LabelFormat { + buffer: output_buffer, + width, + height, + } + } + + pub fn ierode(&mut self) { + let eroded = self.erode(); + self.buffer = eroded.buffer; + } + + pub fn erode(&self) -> LabelFormat { + let width = self.width; + let height = self.height; + let mut output_buffer = vec![0u16; width*height]; + for y in 0..height { + for x in 0..width { + let index = x + y * width; + let current_color = self.buffer[index]; + if current_color == 0 { + continue; + } + if let Some(color) = self.get_up(x,y) { + if color == 0 { + continue; + } + } + if let Some(color) = self.get_right(x,y) { + if color == 0 { + continue; + } + } + if let Some(color) = self.get_down(x,y) { + if color == 0 { + continue; + } + } + if let Some(color) = self.get_left(x,y) { + if color == 0 { + continue; + } + } + output_buffer[index] = current_color; + } + } + LabelFormat { + buffer: output_buffer, + width, + height, + } + } + + pub fn icloseup(&mut self, n: usize) { + for _ in 0..n { + self.ierode(); + } + for _ in 0..n { + self.idilate(); + } + } + + pub fn closeup(&self, n: usize) -> LabelFormat { + let mut x = self.erode(); + x.icloseup(n-1); + x.idilate(); + x + } + + pub fn ifill(&mut self, n: usize) { + for _ in 0..n { + self.idilate(); + } + for _ in 0..n { + self.ierode(); + } + } + + pub fn fill(&self, n: usize) -> LabelFormat { + let mut x = self.dilate(); + x.icloseup(n-1); + x.ierode(); + x + } + + pub fn dump(&self, filename: &str) -> Result<(), std::io::Error> { + crate::binfile::dump_u16_vec(filename, self.buffer.clone()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn label_morphology_test() { + const DIM: usize = 6; + let mut test_data = LabelFormat { + buffer: vec![0u16; DIM*DIM], + width: DIM, + height: DIM, + }; + + test_data.buffer[2+3*DIM] = 1; + test_data.buffer[3+3*DIM] = 1; + test_data.buffer[4+3*DIM] = 2; + test_data.display(true); + println!(); + let dilated = test_data.dilate(); + dilated.display(true); + println!(); + let restored = dilated.erode(); + restored.display(true); + } + + #[test] + fn mutable_morphology_test() { + const DIM: usize = 6; + let mut test_data = LabelFormat { + buffer: vec![0u16; DIM*DIM], + width: DIM, + height: DIM, + }; + + test_data.buffer[2+3*DIM] = 1; + test_data.buffer[3+3*DIM] = 1; + test_data.buffer[4+3*DIM] = 2; + test_data.display(true); + println!(); + test_data.idilate(); + test_data.display(true); + println!(); + test_data.ierode(); + test_data.display(true); + println!(); + } +} diff --git a/src/large_label_format.rs b/src/large_label_format.rs new file mode 100644 index 0000000..5aef6b3 --- /dev/null +++ b/src/large_label_format.rs @@ -0,0 +1,45 @@ +use crate::{LabelFormat,flood}; + +pub struct LargeLabelFormat { + pub buffer: Vec<u32>, + pub width: usize, + pub height: usize, +} + +impl std::fmt::Debug for LargeLabelFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("LargeLabelFormat") + .field("width", &self.width) + .field("height", &self.height) + .finish() + } +} + +impl LargeLabelFormat { + pub fn compress(&self) -> LabelFormat { + let mut label: u16 = 1; + let mut output_buffer: Vec<u16> = vec![0u16; self.buffer.len()]; + for y in 0..self.height { + for x in 0..self.width { + let index = x + y*self.width; + if self.buffer[index] == 0 { + continue; + } + if output_buffer[index] == 0 { + let color = self.buffer[index]; + flood(&self, &mut output_buffer, x, y, color, label); + label += 1; + } + } + } + return LabelFormat { + buffer: output_buffer, + width: self.width, + height: self.height, + }; + } + + pub fn dump(&self, filename: &str) -> Result<(), std::io::Error> { + crate::binfile::dump_u32_vec(filename, self.buffer.clone()) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..e47bca8 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,33 @@ +mod large_label_format; +mod label_format; +pub mod binfile; + +pub use large_label_format::LargeLabelFormat; +pub use label_format::LabelFormat; + +pub(crate) fn flood(source: &LargeLabelFormat, destination: &mut Vec<u16>, + x: usize, y: usize, + from_color: u32, to_color: u16) { + let width = source.width; + let destination_color = destination[x + y * width]; + if destination_color != 0 { + return; + } + let source_color = source.buffer[x + y * width]; + if source_color != from_color { + return; + } + destination[x + y * width] = to_color; + if x > 0 { + flood(source, destination, x-1, y, from_color, to_color); + } + if (x+1) < width { + flood(source, destination, x+1, y, from_color, to_color); + } + if y > 0 { + flood(source, destination, x, y-1, from_color, to_color); + } + if (y+1) < source.height { + flood(source, destination, x, y+1, from_color, to_color); + } +} |