pineapple_core/cv/
connected.rs1use std::cmp::Ordering;
5
6pub struct UnionFind {
8 parent: Vec<usize>,
9 rank: Vec<usize>,
10}
11
12impl UnionFind {
13 pub fn new(n: usize) -> Self {
15 UnionFind {
16 parent: (0..n).collect(),
17 rank: vec![1; n],
18 }
19 }
20
21 pub fn find(&mut self, x: usize) -> usize {
23 if self.parent[x] != x {
24 self.parent[x] = self.find(self.parent[x]);
26 }
27 self.parent[x]
28 }
29
30 pub fn union(&mut self, x: usize, y: usize) {
32 let root_x = self.find(x);
33 let root_y = self.find(y);
34
35 if root_x != root_y {
36 match self.rank[root_x].cmp(&self.rank[root_y]) {
37 Ordering::Greater => self.parent[root_y] = root_x,
38 Ordering::Less => self.parent[root_x] = root_y,
39 Ordering::Equal => {
40 self.parent[root_y] = root_x;
41 self.rank[root_x] += 1;
42 }
43 }
44 }
45 }
46
47 pub fn connected(&mut self, x: usize, y: usize) -> bool {
49 self.find(x) == self.find(y)
50 }
51}
52
53pub fn connected_components(width: u32, height: u32, buffer: &[u32]) -> Vec<u32> {
79 let width = width as usize;
80 let height = height as usize;
81 let size = width * height;
82
83 let mut labels = vec![0u32; size];
84 let mut next_label = 1;
85 let mut uf = UnionFind::new(size);
86
87 for y in 0..height {
89 for x in 0..width {
90 let idx = y * width + x;
91 if buffer[idx] == 0 {
92 continue;
94 }
95
96 let mut neighbors = vec![];
97
98 if x > 0 && buffer[idx - 1] > 0 {
100 neighbors.push(labels[idx - 1]);
101 }
102
103 if y > 0 && buffer[idx - width] > 0 {
105 neighbors.push(labels[idx - width]);
106 }
107
108 if x > 0 && y > 0 && buffer[idx - width - 1] > 0 {
110 neighbors.push(labels[idx - width - 1]);
111 }
112
113 if x < width - 1 && y > 0 && buffer[idx - width + 1] > 0 {
115 neighbors.push(labels[idx - width + 1]);
116 }
117
118 if neighbors.is_empty() {
119 labels[idx] = next_label;
120 next_label += 1;
121 } else {
122 let min_label = *neighbors.iter().min().unwrap();
123 labels[idx] = min_label;
124
125 for &label in &neighbors {
127 uf.union(min_label as usize, label as usize);
128 }
129 }
130 }
131 }
132
133 for label in labels.iter_mut().take(size) {
135 if label != &0 {
136 *label = uf.find(*label as usize) as u32;
137 }
138 }
139
140 labels
141}
142
143#[cfg(test)]
144mod test {
145
146 use super::*;
147
148 fn four_regions() -> (u32, u32, [u32; 9]) {
149 let mut buffer = [0u32; 9];
150
151 buffer[0] = 1u32;
152 buffer[2] = 2u32;
153 buffer[6] = 3u32;
154 buffer[8] = 3u32;
155
156 (3, 3, buffer)
157 }
158
159 fn three_regions() -> (u32, u32, [u32; 9]) {
160 let mut buffer = [0u32; 9];
161
162 buffer[0] = 1u32;
163 buffer[2] = 2u32;
164 buffer[6] = 3u32;
165 buffer[7] = 3u32;
166 buffer[8] = 3u32;
167
168 (3, 3, buffer)
169 }
170
171 fn touching_regions() -> (u32, u32, [u32; 9]) {
172 let mut buffer = [0u32; 9];
173
174 buffer[0] = 1u32;
175 buffer[2] = 2u32;
176 buffer[4] = 4u32;
177 buffer[6] = 3u32;
178 buffer[7] = 3u32;
179 buffer[8] = 3u32;
180
181 (3, 3, buffer)
182 }
183
184 #[test]
185 fn test_four_regions() {
186 let (w, h, buffer) = four_regions();
187
188 let mut labels = connected_components(w, h, &buffer);
189 labels.sort();
190 labels.dedup();
191
192 assert_eq!(labels, vec![0, 1, 2, 3, 4]);
193 }
194
195 #[test]
196 fn test_three_regions() {
197 let (w, h, buffer) = three_regions();
198
199 let mut labels = connected_components(w, h, &buffer);
200 labels.sort();
201 labels.dedup();
202
203 assert_eq!(labels, vec![0, 1, 2, 3]);
204 }
205
206 #[test]
207 fn test_middle_regions() {
208 let (w, h, buffer) = touching_regions();
209
210 let mut labels = connected_components(w, h, &buffer);
211 labels.sort();
212 labels.dedup();
213
214 assert_eq!(labels, vec![0, 1]);
215 }
216}