webarkitlib_rs/
labeling.rs1use crate::types::{ARLabelInfo, ARdouble};
41use log::trace;
42
43pub const AR_LABELING_WORK_SIZE: usize = 1024 * 32;
44
45#[derive(Debug, PartialEq, Clone, Copy)]
46pub enum LabelingMode {
47 BlackRegion,
48 WhiteRegion,
49}
50
51#[derive(Debug, PartialEq, Clone, Copy)]
52pub enum ImageProcMode {
53 FrameImage,
54 FieldImage, }
56
57pub fn ar_labeling(
97 image: &[u8],
98 xsize: i32,
99 ysize: i32,
100 mode: LabelingMode,
101 thresh: u8,
102 proc_mode: ImageProcMode,
103 label_info: &mut ARLabelInfo,
104 _debug_mode: bool,
105) -> Result<(), &'static str> {
106
107 let is_region = |pixel: u8| -> bool {
108 match mode {
109 LabelingMode::BlackRegion => pixel <= thresh,
110 LabelingMode::WhiteRegion => pixel > thresh,
111 }
112 };
113
114 let lxsize: usize;
115 let lysize: usize;
116 let row_stride: usize;
117 let _col_stride: usize;
118
119 match proc_mode {
120 ImageProcMode::FrameImage => {
121 lxsize = xsize as usize;
122 lysize = ysize as usize;
123 row_stride = xsize as usize;
124 _col_stride = 1;
125 }
126 ImageProcMode::FieldImage => {
127 lxsize = (xsize / 2) as usize;
128 lysize = (ysize / 2) as usize;
129 row_stride = (xsize * 2) as usize; if ysize < 480 { _col_stride = 1;
132 } else {
133 _col_stride = 2; }
135 }
136 }
137
138 if image.len() < (ysize as usize * xsize as usize) {
139 return Err("Image buffer is too small for given dimensions.");
140 }
141
142 if label_info.label_image.len() < lxsize * lysize {
144 label_info.label_image.resize(lxsize * lysize, 0);
145 }
146 label_info.label_image.fill(0);
147 trace!("ar_labeling: lxsize={}, lysize={}, len={}", lxsize, lysize, label_info.label_image.len());
148
149 if label_info.work.len() < AR_LABELING_WORK_SIZE {
151 label_info.work.resize(AR_LABELING_WORK_SIZE, 0);
152 }
153 let mut wk_max: usize = 0;
154 let work = &mut label_info.work;
155 let mut work2 = vec![0i64; AR_LABELING_WORK_SIZE * 7];
157
158 for k in 0..AR_LABELING_WORK_SIZE {
160 work2[k * 7 + 3] = xsize as i64; work2[k * 7 + 5] = ysize as i64; }
163
164 let label_img = &mut label_info.label_image;
165
166 fn find(work: &mut [i32], i: i32) -> i32 {
168 let mut root = i;
169 while work[root as usize - 1] != root {
170 root = work[root as usize - 1];
171 }
172 let mut curr = i;
174 while work[curr as usize - 1] != root {
175 let next = work[curr as usize - 1];
176 work[curr as usize - 1] = root;
177 curr = next;
178 }
179 root
180 }
181
182 fn do_union(work: &mut [i32], work2: &mut [i64], m: i32, n: i32) -> i32 {
184 let root_m = find(work, m);
185 let root_n = find(work, n);
186 if root_m != root_n {
187 if root_m < root_n {
188 work[root_n as usize - 1] = root_m;
189 let m_idx = (root_m as usize - 1) * 7;
191 let n_idx = (root_n as usize - 1) * 7;
192 for i in 0..3 { work2[m_idx + i] += work2[n_idx + i]; }
193 if work2[m_idx + 3] > work2[n_idx + 3] { work2[m_idx + 3] = work2[n_idx + 3]; }
194 if work2[m_idx + 4] < work2[n_idx + 4] { work2[m_idx + 4] = work2[n_idx + 4]; }
195 if work2[m_idx + 5] > work2[n_idx + 5] { work2[m_idx + 5] = work2[n_idx + 5]; }
196 if work2[m_idx + 6] < work2[n_idx + 6] { work2[m_idx + 6] = work2[n_idx + 6]; }
197 for i in 0..7 { work2[n_idx + i] = 0; }
199 root_m
200 } else {
201 work[root_m as usize - 1] = root_n;
202 let m_idx = (root_m as usize - 1) * 7;
204 let n_idx = (root_n as usize - 1) * 7;
205 for i in 0..3 { work2[n_idx + i] += work2[m_idx + i]; }
206 if work2[n_idx + 3] > work2[m_idx + 3] { work2[n_idx + 3] = work2[m_idx + 3]; }
207 if work2[n_idx + 4] < work2[m_idx + 4] { work2[n_idx + 4] = work2[m_idx + 4]; }
208 if work2[n_idx + 5] > work2[m_idx + 5] { work2[n_idx + 5] = work2[m_idx + 5]; }
209 if work2[n_idx + 6] < work2[m_idx + 6] { work2[n_idx + 6] = work2[m_idx + 6]; }
210 for i in 0..7 { work2[m_idx + i] = 0; }
212 root_n
213 }
214 } else {
215 root_m
216 }
217 }
218
219 for j in 1..lysize - 1 {
221 for i in 1..lxsize - 1 {
222 let source_idx = match proc_mode {
223 ImageProcMode::FrameImage => j * row_stride + i,
224 ImageProcMode::FieldImage => (j * 2) * xsize as usize + (i * 2),
225 };
226 let pixel = image[source_idx];
227
228 let p_idx = j * lxsize + i;
229
230 if is_region(pixel) {
231 let left_val = label_img[p_idx - 1];
232 let up_left_val = label_img[p_idx - lxsize - 1];
233 let up_val = label_img[p_idx - lxsize];
234 let up_right_val = label_img[p_idx - lxsize + 1];
235
236 let mut neighbors = [0; 4];
237 let mut n_count = 0;
238 if left_val > 0 { neighbors[n_count] = left_val as i32; n_count += 1; }
239 if up_left_val > 0 { neighbors[n_count] = up_left_val as i32; n_count += 1; }
240 if up_val > 0 { neighbors[n_count] = up_val as i32; n_count += 1; }
241 if up_right_val > 0 { neighbors[n_count] = up_right_val as i32; n_count += 1; }
242
243 if n_count > 0 {
244 neighbors[0..n_count].sort_unstable();
246 let mut unique_count = 1;
247 for k in 1..n_count {
248 if neighbors[k] != neighbors[k-1] {
249 neighbors[unique_count] = neighbors[k];
250 unique_count += 1;
251 }
252 }
253 n_count = unique_count;
254
255 let mut final_label = neighbors[0];
256 for k in 1..n_count {
257 if neighbors[k] != final_label {
258 final_label = do_union(work, &mut work2, final_label, neighbors[k]);
259 }
260 }
261 label_img[p_idx] = final_label as crate::types::ARLabelingLabelType;
262
263 let l = (final_label as usize - 1) * 7;
264 work2[l + 0] += 1;
265 work2[l + 1] += i as i64;
266 work2[l + 2] += j as i64;
267 if work2[l + 3] > i as i64 { work2[l + 3] = i as i64; }
268 if work2[l + 4] < i as i64 { work2[l + 4] = i as i64; }
269 if work2[l + 5] > j as i64 { work2[l + 5] = j as i64; }
270 if work2[l + 6] < j as i64 { work2[l + 6] = j as i64; }
271 } else {
272 wk_max += 1;
273 if wk_max > AR_LABELING_WORK_SIZE {
274 return Err("Labeling work array overflow");
275 }
276 work[wk_max - 1] = wk_max as i32;
277 label_img[p_idx] = wk_max as crate::types::ARLabelingLabelType;
278
279 let l = (wk_max - 1) * 7;
280 work2[l + 0] = 1; work2[l + 1] = i as i64; work2[l + 2] = j as i64; work2[l + 3] = i as i64; work2[l + 4] = i as i64; work2[l + 5] = j as i64; work2[l + 6] = j as i64; if wk_max == 32 || wk_max == 41 {
289 trace!("Label {} created at ({}, {})", wk_max, i, j);
290 }
291 }
292 } else {
293 label_img[p_idx] = 0;
294 }
295 }
296 }
297
298 let mut num_labels = 0;
300 let mut label_map = vec![0; wk_max];
302
303 for i in 1..=wk_max {
305 if work[i - 1] == i as i32 { num_labels += 1;
307 label_map[i - 1] = num_labels;
308 }
309 }
310
311 for i in 1..=wk_max {
313 let root = find(work, i as i32) as usize;
314 label_map[i - 1] = label_map[root - 1];
315 }
316
317 label_info.label_num = num_labels;
318 if label_info.label_num == 0 {
319 return Ok(());
320 }
321
322 for i in 0..label_img.len() {
324 if label_img[i] > 0 {
325 label_img[i] = label_map[label_img[i] as usize - 1] as crate::types::ARLabelingLabelType;
326 }
327 }
328
329 if label_info.area.len() < num_labels as usize { label_info.area.resize(num_labels as usize, 0); }
334 if label_info.pos.len() < num_labels as usize { label_info.pos.resize(num_labels as usize, [0.0; 2]); }
335 if label_info.clip.len() < num_labels as usize { label_info.clip.resize(num_labels as usize, [0; 4]); }
336
337 label_info.area.fill(0);
338 for v in label_info.pos.iter_mut() { v.fill(0.0); }
339 for v in label_info.clip.iter_mut() {
340 v[0] = lxsize as i32; v[1] = 0; v[2] = lysize as i32; v[3] = 0;
341 }
342
343 for k in 1..=wk_max {
347 if work[k - 1] == k as i32 { let src = (k - 1) * 7;
349 if work2[src + 0] > 0 {
350 let dense_idx = label_map[k - 1];
351 if dense_idx > 0 {
352 let i = (dense_idx - 1) as usize;
353 label_info.area[i] = work2[src + 0] as i32;
354 label_info.pos[i][0] = work2[src + 1] as ARdouble;
355 label_info.pos[i][1] = work2[src + 2] as ARdouble;
356 label_info.clip[i][0] = work2[src + 3] as i32;
357 label_info.clip[i][1] = work2[src + 4] as i32;
358 label_info.clip[i][2] = work2[src + 5] as i32;
359 label_info.clip[i][3] = work2[src + 6] as i32;
360 }
361 }
362 }
363 }
364
365 for i in 0..num_labels as usize {
366 if label_info.area[i] > 0 {
367 label_info.pos[i][0] /= label_info.area[i] as ARdouble;
368 label_info.pos[i][1] /= label_info.area[i] as ARdouble;
369 }
370 }
371
372 Ok(())
373}
374
375#[cfg(test)]
376mod tests {
377 use super::*;
378
379 #[test]
380 fn test_ar_labeling_two_squares() {
381 let mut image = vec![255u8; 400];
385 for j in 2..7 {
386 for i in 2..7 {
387 image[j * 20 + i] = 0;
388 }
389 }
390 for j in 12..17 {
391 for i in 12..17 {
392 image[j * 20 + i] = 0;
393 }
394 }
395
396 let mut info = crate::types::ARLabelInfo::default();
397
398 ar_labeling(
399 &image,
400 20,
401 20,
402 LabelingMode::BlackRegion,
403 100,
404 ImageProcMode::FrameImage,
405 &mut info,
406 false
407 ).unwrap();
408
409 assert_eq!(info.label_num, 2);
410
411 let mut found_s1 = false;
412 let mut found_s2 = false;
413
414 for i in 0..info.label_num as usize {
415 if info.area[i] == 25 {
416 if info.clip[i][0] == 2 && info.clip[i][1] == 6 && info.clip[i][2] == 2 && info.clip[i][3] == 6 {
417 found_s1 = true;
418 } else if info.clip[i][0] == 12 && info.clip[i][1] == 16 && info.clip[i][2] == 12 && info.clip[i][3] == 16 {
419 found_s2 = true;
420 }
421 }
422 }
423
424 assert!(found_s1, "Square 1 (2,2)-(6,6) not correctly found. Area={:?}, Clip={:?}", info.area, info.clip);
425 assert!(found_s2, "Square 2 (12,12)-(16,16) not correctly found. Area={:?}, Clip={:?}", info.area, info.clip);
426 }
427}