1use ndarray::{s, Array3, ArrayRef3, Axis, Zip};
2use num_traits::{Bounded, FromPrimitive, NumAssignOps, ToPrimitive, Unsigned};
3
4use crate::Mask;
5
6pub trait LabelType:
7 Copy + FromPrimitive + ToPrimitive + Ord + Unsigned + NumAssignOps + Bounded
8{
9 fn background() -> Self;
10 fn foreground() -> Self;
11}
12
13impl<T> LabelType for T
14where
15 T: Copy + FromPrimitive + ToPrimitive + Ord + Unsigned + NumAssignOps + Bounded,
16{
17 fn background() -> Self {
18 T::zero()
19 }
20 fn foreground() -> Self {
21 T::one()
22 }
23}
24
25pub fn label_histogram<A>(labels: &ArrayRef3<A>, nb_features: usize) -> Vec<usize>
30where
31 A: LabelType,
32{
33 let mut count = vec![0; nb_features + 1];
34 Zip::from(labels).for_each(|&l| {
35 count[l.to_usize().unwrap()] += 1;
36 });
37 count
38}
39
40pub fn most_frequent_label<A>(
47 labels: &ArrayRef3<A>,
48 nb_features: usize,
49) -> Option<(A, usize)>
50where
51 A: LabelType,
52{
53 let hist = label_histogram(labels, nb_features);
54 let (max, max_index) =
55 hist[1..].iter().enumerate().fold((0, 0), |acc, (i, &nb)| acc.max((nb, i)));
56 (max > 0).then(|| (A::from_usize(max_index + 1).unwrap(), max))
57}
58
59pub fn largest_connected_components(
67 mask: &ArrayRef3<bool>,
68 structure: &ArrayRef3<bool>,
69) -> Option<Mask>
70{
71 let (labels, nb_features) = label::<u16>(mask, structure);
72 let (right_label, _) = most_frequent_label(&labels, nb_features)?;
73 Some(labels.mapv(|l| l == right_label))
74}
75
76pub fn label<O>(data: &ArrayRef3<bool>, structure: &ArrayRef3<bool>) -> (Array3<O>, usize)
98where
99 O: LabelType,
100{
101 assert!(structure.shape() == &[3, 3, 3], "`structure` must be size 3 in all dimensions");
102 assert!(structure == structure.slice(s![..;-1, ..;-1, ..;-1]), "`structure is not symmetric");
103
104 let len = data.dim().2;
105 let mut line_buffer = vec![O::background(); len + 2];
106 let mut neighbors = vec![O::background(); len + 2];
107
108 let mut next_region = O::foreground() + O::one();
109 let mut equivalences: Vec<_> =
110 (0..next_region.to_usize().unwrap()).map(|x| O::from_usize(x).unwrap()).collect();
111
112 let nb_neighbors = structure.len() / (3 * 2);
117 let kernel_data: Vec<([bool; 3], [isize; 2])> = structure
118 .lanes(Axis(2))
119 .into_iter()
120 .zip(0isize..)
121 .take(nb_neighbors)
123 .filter(|(lane, _)| lane.iter().any(|x| *x))
125 .map(|(lane, i)| {
126 let kernel = [lane[0], lane[1], lane[2]];
127 let y = i / 3;
129 let x = i - y * 3;
130 (kernel, [y, x])
131 })
132 .collect();
133
134 let use_previous = structure[(1, 1, 0)];
135 let width = data.dim().0 as isize;
136 let height = data.dim().1 as isize;
137 let mut labels = Array3::from_elem(data.dim(), O::background());
138 Zip::indexed(data.lanes(Axis(2))).for_each(|idx, data| {
139 for (&v, b) in data.iter().zip(&mut line_buffer[1..]) {
140 *b = if !v { O::background() } else { O::foreground() }
141 }
142
143 let mut needs_self_labeling = true;
144 for (i, (kernel, coordinates)) in kernel_data.iter().enumerate() {
145 if let Some((x, y)) = is_valid(&[idx.0, idx.1], coordinates, &[width, height]) {
147 for (&v, b) in labels.slice(s![x, y, ..]).iter().zip(&mut neighbors[1..]) {
149 *b = v;
150 }
151
152 let label_unlabeled = i == kernel_data.len() - 1;
153 next_region = label_line_with_neighbor(
154 &mut line_buffer,
155 &neighbors,
156 &mut equivalences,
157 *kernel,
158 use_previous,
159 label_unlabeled,
160 next_region,
161 );
162 if label_unlabeled {
163 needs_self_labeling = false;
164 }
165 }
166 }
167
168 if needs_self_labeling {
169 next_region = label_line_with_neighbor(
172 &mut line_buffer,
173 &neighbors,
174 &mut equivalences,
175 [false, false, false],
176 use_previous,
177 true,
178 next_region,
179 );
180 }
181
182 Zip::from(&line_buffer[1..=len])
184 .map_assign_into(labels.slice_mut(s![idx.0, idx.1, ..]), |&b| b);
185 });
186
187 let nb_features = compact_equivalences(&mut equivalences, next_region);
189 labels.mapv_inplace(|l| equivalences[l.to_usize().unwrap()]);
190
191 (labels, nb_features)
192}
193
194fn is_valid(idx: &[usize; 2], coords: &[isize; 2], dims: &[isize; 2]) -> Option<(usize, usize)> {
195 let valid = |i, c, d| -> Option<usize> {
196 let a = i as isize + (c - 1);
197 if a >= 0 && a < d {
198 Some(a as usize)
199 } else {
200 None
201 }
202 };
203 valid(idx[0], coords[0], dims[0])
205 .and_then(|x| valid(idx[1], coords[1], dims[1]).and_then(|y| Some((x, y))))
206}
207
208fn label_line_with_neighbor<O>(
209 line: &mut [O],
210 neighbors: &[O],
211 equivalences: &mut Vec<O>,
212 kernel: [bool; 3],
213 use_previous: bool,
214 label_unlabeled: bool,
215 mut next_region: O,
216) -> O
217where
218 O: LabelType,
219{
220 let mut previous = line[0];
221 for (n, l) in neighbors.windows(3).zip(&mut line[1..]) {
222 if *l != O::background() {
223 for (&n, &k) in n.iter().zip(&kernel) {
224 if k {
225 *l = take_label_or_merge(*l, n, equivalences);
226 }
227 }
228 if label_unlabeled {
229 if use_previous {
230 *l = take_label_or_merge(*l, previous, equivalences);
231 }
232 if *l == O::foreground() {
234 *l = next_region;
235 equivalences.push(next_region);
236 assert!(next_region < O::max_value(), "Overflow when assigning label");
237 next_region += O::one();
238 }
239 }
240 }
241 previous = *l;
242 }
243 next_region
244}
245
246fn take_label_or_merge<O>(current: O, neighbor: O, equivalences: &mut [O]) -> O
248where
249 O: LabelType,
250{
251 if neighbor == O::background() {
252 current
253 } else if current == O::foreground() {
254 neighbor } else if current != neighbor {
256 mark_for_merge(neighbor, current, equivalences)
257 } else {
258 current
259 }
260}
261
262fn mark_for_merge<O>(mut a: O, mut b: O, equivalences: &mut [O]) -> O
264where
265 O: LabelType,
266{
267 let original_a = a;
269 while a != equivalences[a.to_usize().unwrap()] {
270 a = equivalences[a.to_usize().unwrap()];
271 }
272 let original_b = b;
273 while b != equivalences[b.to_usize().unwrap()] {
274 b = equivalences[b.to_usize().unwrap()];
275 }
276 let lowest_label = a.min(b);
277
278 equivalences[a.to_usize().unwrap()] = lowest_label;
280 equivalences[b.to_usize().unwrap()] = lowest_label;
281
282 a = original_a;
284 while a != lowest_label {
285 let a_copy = a;
286 a = equivalences[a.to_usize().unwrap()];
287 equivalences[a_copy.to_usize().unwrap()] = lowest_label;
288 }
289 b = original_b;
290 while b != lowest_label {
291 let b_copy = b;
292 b = equivalences[b.to_usize().unwrap()];
293 equivalences[b_copy.to_usize().unwrap()] = lowest_label;
294 }
295
296 lowest_label
297}
298
299fn compact_equivalences<O>(equivalences: &mut [O], next_region: O) -> usize
301where
302 O: LabelType,
303{
304 let no_labelling = next_region == O::from_usize(2).unwrap();
305 let mut dest_label = if no_labelling { 0 } else { 1 };
306 for i in 2..next_region.to_usize().unwrap() {
307 if equivalences[i] == O::from_usize(i).unwrap() {
308 equivalences[i] = O::from_usize(dest_label).unwrap();
309 dest_label = dest_label + 1;
310 } else {
311 equivalences[i] = equivalences[equivalences[i].to_usize().unwrap()];
315 }
316 }
317 if no_labelling {
318 0
319 } else {
320 equivalences.iter().max().unwrap().to_usize().unwrap()
321 }
322}