edt/
lib.rs

1//! # edt
2//!
3//! An implementation of 2D EDT ([Euclidean distance transform](https://en.wikipedia.org/wiki/Distance_transform)) with Saito's algorithm in pure Rust
4//!
5//! There are also [other](https://crates.io/crates/distance-transform)
6//! [crates](https://crates.io/crates/dt) that implements EDT,
7//! but I would like to reinvent a wheel that has these advantages:
8//!
9//! * No dependencies (except example codes)
10//! * Intuitive to use (accepts a numerical slice and a shape)
11//!
12//! Performance was not the priority, but I would like to explore more optimizations.
13//!
14//! EDT is the basis of many algorithms, but it is hard to find in a general purpose image processing library,
15//! probably because the algorithm is not trivial to implement efficiently.
16//! This crate provides an implementation of EDT in fairly efficient algorithm presented in the literature.
17//!
18//! The algorithm used in this crate (Saito's algorithm) is O(n^3), where n is the number of pixels along one direction.
19//! Naive computation of EDT would be O(n^4), so it is certainly better than that, but there is also fast-marching based
20//! algorithm that is O(n^2).
21//!
22//!
23//! ### Fast Marching Method
24//!
25//! Fast Marching method is a strategy of algorithms that use expanding wavefront
26//! (sometimes called grassfire analogy).
27//! Tehcnically, it is a method to solve Eikonal PDE with known margin of error.
28//! It is especially useful with EDT, because it has O(n^2) complexity, which is beneficial in large images.
29//!
30//! In my specific environment with 1024 x 1024 pixels image, it has significant
31//! difference like below.
32//!
33//! * Exact EDT: 2900ms
34//! * Fast Marching EDT: 93.7ms
35//!
36//! However, it has downside that it cannot produce exact (true) EDT.
37//! That said, FMM has enough accuracy for most applications.
38//!
39//! The library has a function with progress callback that you can use to produce nice animation like below.
40//!
41//! ![Rust-logo-fmm](https://raw.githubusercontent.com/msakuta/msakuta.github.io/master/images/showcase/Rust_logo_animated.gif)
42//!
43//! The difference between exact and Fast Marching versions can be visualized like below (see
44//! [example-edt](https://github.com/msakuta/rust-edt/blob/master/examples/edt.rs))
45//! which can be run by
46//!
47//! ```bash
48//! cargo r --release --example edt -- Rust_logo.png -d
49//! ```
50//!
51//! ![Rust-logo-edt](https://raw.githubusercontent.com/msakuta/rust-edt/master/Rust_logo_diff.png)
52//!
53//!
54//! ## Usage
55//!
56//! Add dependency
57//!
58//! ```toml
59//! [dependencies]
60//! edt = "0.2.1"
61//! ```
62//!
63//! Prepare a binary image as a flattened vec.
64//! This library assumes that the input is a flat vec for 2d image.
65//! You can specify the dimensions with a tuple.
66//!
67//! ```rust
68//! let vec: Vec<bool> = vec![/*...*/];
69//! let dims = (32, 32);
70//! ```
71//!
72//! If you want to read input from an image, you can use [image](https://crates.io/crates/image) crate.
73//! Make sure to put it to your project's dependencies in that case.
74//!
75//! ```rust
76//! use image::GenericImageView;
77//! let img = image::open("Rust_logo.png").unwrap();
78//! let dims = img.dimensions();
79//! let img2 = img.into_luma8();
80//! let flat_samples = img2.as_flat_samples();
81//! let vec = flat_samples.image_slice().unwrap();
82//! ```
83//!
84//! Call edt with given shape
85//!
86//! ```rust
87//! # let vec: Vec<bool> = vec![false; 32 * 32];
88//! # let dims = (32, 32);
89//! use edt::edt;
90//!
91//! let edt_image = edt(&vec, (dims.0 as usize, dims.1 as usize), true);
92//! ```
93//!
94//! Save to a file if you want.
95//! The code below normalizes the value with maximum value to 8 bits grayscale image.
96//!
97//! ```rust
98//! # use edt::edt;
99//! # let vec: Vec<bool> = vec![false; 32 * 32];
100//! # let edt_image = edt(&vec, (32, 32), true);
101//! use image::{ImageBuffer, Luma};
102//!
103//! let max_value = edt_image.iter().map(|p| *p).reduce(f64::max).unwrap();
104//! let edt_img = edt_image
105//!     .iter()
106//!     .map(|p| (*p / max_value * 255.) as u8)
107//!     .collect();
108//!
109//! let edt_img: ImageBuffer<Luma<u8>, Vec<u8>> =
110//!     ImageBuffer::from_vec(32, 32, edt_img).unwrap();
111//!
112//! // Write the contents of this image to the Writer in PNG format.
113//! edt_img.save("edt.png").unwrap();
114//! ```
115//!
116//! See [examples](https://github.com/msakuta/rust-edt/tree/master/examples) folder for more.
117//!
118//! ## Literature
119//!
120//! ### 2D Euclidean Distance Transform Algorithms: A Comparative Survey
121//!
122//! This paper is a great summary of this field of research.
123//!
124//! doi=10.1.1.66.2644
125//!
126//! <https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.66.2644&rep=rep1&type=pdf>
127//!
128//! Section 7.7
129//!
130//!
131//! ### Saito and Toriwaki \[1994\] (Original paper)
132//!
133//! <https://www.cs.jhu.edu/~misha/ReadingSeminar/Papers/Saito94.pdf>
134//!
135//!
136//! ### An introduction to Fast Marching Method
137//!
138//! [Dijkstra and Fast Marching Algorithms (tutorial in Matlab)](https://www.numerical-tours.com/matlab/fastmarching_0_implementing/)
139
140mod exact_edt;
141mod fast_marcher;
142mod primitive_impl;
143
144/// A trait for types that can be interpreted as a bool.
145///
146/// Primitive numerical types (integers and floats) implement this trait,
147/// so you don't have to implement this by yourself.
148/// However, you could implement it for your own custom type, if you want.
149///
150/// We don't use [num](https://crates.io/crates/num) crate because it is overkill for our purposes.
151pub trait BoolLike {
152    fn as_bool(&self) -> bool;
153}
154
155pub use exact_edt::{edt, edt_sq};
156pub use fast_marcher::{edt_fmm, edt_fmm_cb, FMMCallbackData, GridPos};
157
158#[cfg(test)]
159mod test_util;