1use ndarray::{s, Array3, ArrayBase, Axis, Data, Ix3, 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<S>(labels: &ArrayBase<S, Ix3>, nb_features: usize) -> Vec<usize>
30where
31 S: Data,
32 S::Elem: LabelType,
33{
34 let mut count = vec![0; nb_features + 1];
35 Zip::from(labels).for_each(|&l| {
36 count[l.to_usize().unwrap()] += 1;
37 });
38 count
39}
40
41pub fn most_frequent_label<S>(
48 labels: &ArrayBase<S, Ix3>,
49 nb_features: usize,
50) -> Option<(S::Elem, usize)>
51where
52 S: Data,
53 S::Elem: LabelType,
54{
55 let hist = label_histogram(labels, nb_features);
56 let (max, max_index) =
57 hist[1..].iter().enumerate().fold((0, 0), |acc, (i, &nb)| acc.max((nb, i)));
58 (max > 0).then(|| (S::Elem::from_usize(max_index + 1).unwrap(), max))
59}
60
61pub fn largest_connected_components<S>(
69 mask: &ArrayBase<S, Ix3>,
70 structure: &ArrayBase<S, Ix3>,
71) -> Option<Mask>
72where
73 S: Data<Elem = bool>,
74{
75 let (labels, nb_features) = label::<_, u16>(mask, structure);
76 let (right_label, _) = most_frequent_label(&labels, nb_features)?;
77 Some(labels.mapv(|l| l == right_label))
78}
79
80pub fn label<S, O>(data: &ArrayBase<S, Ix3>, structure: &ArrayBase<S, Ix3>) -> (Array3<O>, usize)
102where
103 S: Data<Elem = bool>,
104 O: LabelType,
105{
106 assert!(structure.shape() == &[3, 3, 3], "`structure` must be size 3 in all dimensions");
107 assert!(structure == structure.slice(s![..;-1, ..;-1, ..;-1]), "`structure is not symmetric");
108
109 let len = data.dim().2;
110 let mut line_buffer = vec![O::background(); len + 2];
111 let mut neighbors = vec![O::background(); len + 2];
112
113 let mut next_region = O::foreground() + O::one();
114 let mut equivalences: Vec<_> =
115 (0..next_region.to_usize().unwrap()).map(|x| O::from_usize(x).unwrap()).collect();
116
117 let nb_neighbors = structure.len() / (3 * 2);
122 let kernel_data: Vec<([bool; 3], [isize; 2])> = structure
123 .lanes(Axis(2))
124 .into_iter()
125 .zip(0isize..)
126 .take(nb_neighbors)
128 .filter(|(lane, _)| lane.iter().any(|x| *x))
130 .map(|(lane, i)| {
131 let kernel = [lane[0], lane[1], lane[2]];
132 let y = i / 3;
134 let x = i - y * 3;
135 (kernel, [y, x])
136 })
137 .collect();
138
139 let use_previous = structure[(1, 1, 0)];
140 let width = data.dim().0 as isize;
141 let height = data.dim().1 as isize;
142 let mut labels = Array3::from_elem(data.dim(), O::background());
143 Zip::indexed(data.lanes(Axis(2))).for_each(|idx, data| {
144 for (&v, b) in data.iter().zip(&mut line_buffer[1..]) {
145 *b = if !v { O::background() } else { O::foreground() }
146 }
147
148 let mut needs_self_labeling = true;
149 for (i, (kernel, coordinates)) in kernel_data.iter().enumerate() {
150 if let Some((x, y)) = is_valid(&[idx.0, idx.1], coordinates, &[width, height]) {
152 for (&v, b) in labels.slice(s![x, y, ..]).iter().zip(&mut neighbors[1..]) {
154 *b = v;
155 }
156
157 let label_unlabeled = i == kernel_data.len() - 1;
158 next_region = label_line_with_neighbor(
159 &mut line_buffer,
160 &neighbors,
161 &mut equivalences,
162 *kernel,
163 use_previous,
164 label_unlabeled,
165 next_region,
166 );
167 if label_unlabeled {
168 needs_self_labeling = false;
169 }
170 }
171 }
172
173 if needs_self_labeling {
174 next_region = label_line_with_neighbor(
177 &mut line_buffer,
178 &neighbors,
179 &mut equivalences,
180 [false, false, false],
181 use_previous,
182 true,
183 next_region,
184 );
185 }
186
187 Zip::from(&line_buffer[1..=len])
189 .map_assign_into(labels.slice_mut(s![idx.0, idx.1, ..]), |&b| b);
190 });
191
192 let nb_features = compact_equivalences(&mut equivalences, next_region);
194 labels.mapv_inplace(|l| equivalences[l.to_usize().unwrap()]);
195
196 (labels, nb_features)
197}
198
199fn is_valid(idx: &[usize; 2], coords: &[isize; 2], dims: &[isize; 2]) -> Option<(usize, usize)> {
200 let valid = |i, c, d| -> Option<usize> {
201 let a = i as isize + (c - 1);
202 if a >= 0 && a < d {
203 Some(a as usize)
204 } else {
205 None
206 }
207 };
208 valid(idx[0], coords[0], dims[0])
210 .and_then(|x| valid(idx[1], coords[1], dims[1]).and_then(|y| Some((x, y))))
211}
212
213fn label_line_with_neighbor<O>(
214 line: &mut [O],
215 neighbors: &[O],
216 equivalences: &mut Vec<O>,
217 kernel: [bool; 3],
218 use_previous: bool,
219 label_unlabeled: bool,
220 mut next_region: O,
221) -> O
222where
223 O: LabelType,
224{
225 let mut previous = line[0];
226 for (n, l) in neighbors.windows(3).zip(&mut line[1..]) {
227 if *l != O::background() {
228 for (&n, &k) in n.iter().zip(&kernel) {
229 if k {
230 *l = take_label_or_merge(*l, n, equivalences);
231 }
232 }
233 if label_unlabeled {
234 if use_previous {
235 *l = take_label_or_merge(*l, previous, equivalences);
236 }
237 if *l == O::foreground() {
239 *l = next_region;
240 equivalences.push(next_region);
241 assert!(next_region < O::max_value(), "Overflow when assigning label");
242 next_region += O::one();
243 }
244 }
245 }
246 previous = *l;
247 }
248 next_region
249}
250
251fn take_label_or_merge<O>(current: O, neighbor: O, equivalences: &mut [O]) -> O
253where
254 O: LabelType,
255{
256 if neighbor == O::background() {
257 current
258 } else if current == O::foreground() {
259 neighbor } else if current != neighbor {
261 mark_for_merge(neighbor, current, equivalences)
262 } else {
263 current
264 }
265}
266
267fn mark_for_merge<O>(mut a: O, mut b: O, equivalences: &mut [O]) -> O
269where
270 O: LabelType,
271{
272 let original_a = a;
274 while a != equivalences[a.to_usize().unwrap()] {
275 a = equivalences[a.to_usize().unwrap()];
276 }
277 let original_b = b;
278 while b != equivalences[b.to_usize().unwrap()] {
279 b = equivalences[b.to_usize().unwrap()];
280 }
281 let lowest_label = a.min(b);
282
283 equivalences[a.to_usize().unwrap()] = lowest_label;
285 equivalences[b.to_usize().unwrap()] = lowest_label;
286
287 a = original_a;
289 while a != lowest_label {
290 let a_copy = a;
291 a = equivalences[a.to_usize().unwrap()];
292 equivalences[a_copy.to_usize().unwrap()] = lowest_label;
293 }
294 b = original_b;
295 while b != lowest_label {
296 let b_copy = b;
297 b = equivalences[b.to_usize().unwrap()];
298 equivalences[b_copy.to_usize().unwrap()] = lowest_label;
299 }
300
301 lowest_label
302}
303
304fn compact_equivalences<O>(equivalences: &mut [O], next_region: O) -> usize
306where
307 O: LabelType,
308{
309 let no_labelling = next_region == O::from_usize(2).unwrap();
310 let mut dest_label = if no_labelling { 0 } else { 1 };
311 for i in 2..next_region.to_usize().unwrap() {
312 if equivalences[i] == O::from_usize(i).unwrap() {
313 equivalences[i] = O::from_usize(dest_label).unwrap();
314 dest_label = dest_label + 1;
315 } else {
316 equivalences[i] = equivalences[equivalences[i].to_usize().unwrap()];
320 }
321 }
322 if no_labelling {
323 0
324 } else {
325 equivalences.iter().max().unwrap().to_usize().unwrap()
326 }
327}