aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml6
-rw-r--r--src/binfile.rs27
-rw-r--r--src/label_format.rs227
-rw-r--r--src/large_label_format.rs45
-rw-r--r--src/lib.rs33
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);
+ }
+}