1use crate::types::{ARLabelInfo, ARMarkerInfo2, ARdouble};
41use log::debug;
42
43pub const AR_AREA_MAX: i32 = 100000;
44pub const AR_AREA_MIN: i32 = 70;
45pub const AR_SQUARE_FIT_THRESH: f64 = 0.05;
46pub const AR_CHAIN_MAX: usize = 10000;
47pub const AR_SQUARE_MAX: usize = 30;
48
49#[derive(Debug, PartialEq, Clone, Copy)]
50pub enum ImageProcMode {
51 FrameImage = 0,
52 FieldImage = 1,
53}
54
55pub fn ar_detect_marker(
87 ar_handle: &mut crate::types::ARHandle,
88 frame: &crate::types::AR2VideoBufferT,
89) -> Result<(), &'static str> {
90 ar_handle.marker_num = 0;
91
92 let luma_buff = match &frame.buff_luma {
93 Some(b) => b.as_slice(),
94 None => return Err("AR2VideoBufferT requires buff_luma to be available"),
95 };
96
97 let color_buff = match &frame.buff {
98 Some(b) => b.as_slice(),
99 None => return Err("AR2VideoBufferT requires buff to be available"),
100 };
101
102 let thresh = ar_handle.ar_labeling_thresh as u8;
103 let label_mode = if ar_handle.ar_labeling_mode == 0 {
104 crate::labeling::LabelingMode::BlackRegion
105 } else {
106 crate::labeling::LabelingMode::WhiteRegion
107 };
108
109 let labeling_proc_mode = if ar_handle.ar_image_proc_mode == 0 {
110 crate::labeling::ImageProcMode::FrameImage
111 } else {
112 crate::labeling::ImageProcMode::FieldImage
113 };
114
115 crate::labeling::ar_labeling(
116 luma_buff,
117 ar_handle.xsize,
118 ar_handle.ysize,
119 label_mode,
120 thresh,
121 labeling_proc_mode,
122 &mut ar_handle.label_info,
123 ar_handle.ar_debug != 0
124 )?;
125
126 if ar_handle.ar_debug != 0 {
127 debug!("ar_labeling found {} labels.", ar_handle.label_info.label_num);
128 }
129
130 let image_proc_mode = if ar_handle.ar_image_proc_mode == 0 {
131 ImageProcMode::FrameImage
132 } else {
133 ImageProcMode::FieldImage
134 };
135
136 ar_detect_marker2(
137 ar_handle.xsize,
138 ar_handle.ysize,
139 &mut ar_handle.label_info,
140 image_proc_mode,
141 AR_AREA_MAX,
142 AR_AREA_MIN,
143 AR_SQUARE_FIT_THRESH,
144 &mut *ar_handle.marker_info2,
145 &mut ar_handle.marker2_num,
146 )?;
147
148 if ar_handle.ar_debug != 0 {
149 debug!("ar_detect_marker2 found {} square candidates.", ar_handle.marker2_num);
150 }
151
152 if ar_handle.ar_param_lt.is_null() {
153 return Err("ARParamLT is null in ARHandle");
154 }
155
156 let image_proc_mode2 = if ar_handle.ar_image_proc_mode == 0 {
157 ImageProcMode::FrameImage
158 } else {
159 ImageProcMode::FieldImage
160 };
161
162 let param_ltf = unsafe { &(*ar_handle.ar_param_lt).param_ltf };
163
164 let patt_handle_opt = if !ar_handle.patt_handle.is_null() {
165 Some(unsafe { &*ar_handle.patt_handle })
166 } else {
167 None
168 };
169
170 ar_get_marker_info(
171 color_buff,
172 ar_handle.xsize,
173 ar_handle.ysize,
174 ar_handle.ar_pixel_format,
175 &ar_handle.marker_info2[..],
176 ar_handle.marker2_num,
177 image_proc_mode2,
178 ar_handle.ar_pattern_detection_mode,
179 param_ltf,
180 ar_handle.patt_ratio,
181 patt_handle_opt,
182 &mut *ar_handle.marker_info,
183 &mut ar_handle.marker_num,
184 ar_handle.matrix_code_type,
185 )?;
186
187 if ar_handle.ar_debug != 0 {
188 debug!("ar_get_marker_info produced {} final markers.", ar_handle.marker_num);
189 }
190
191 Ok(())
192}
193
194pub fn ar_detect_marker2(
213 xsize: i32,
214 ysize: i32,
215 label_info: &mut ARLabelInfo,
216 image_proc_mode: ImageProcMode,
217 area_max: i32,
218 area_min: i32,
219 square_fit_thresh: ARdouble,
220 marker_info2: &mut [ARMarkerInfo2],
221 marker2_num: &mut i32,
222) -> Result<(), &'static str> {
223 let mut xsize_local = xsize;
224 let mut ysize_local = ysize;
225 let mut area_min_local = area_min;
226 let mut area_max_local = area_max;
227
228 if matches!(image_proc_mode, ImageProcMode::FieldImage) {
229 area_min_local /= 4;
230 area_max_local /= 4;
231 xsize_local /= 2;
232 ysize_local /= 2;
233 }
234
235 *marker2_num = 0;
236
237 let label_num = label_info.label_num as usize;
238 for i in 0..label_num {
239 if label_info.area[i] < area_min_local || label_info.area[i] > area_max_local {
240 debug!("Label {} skipped due to Area ({}) not in [{}, {}]", i, label_info.area[i], area_min_local, area_max_local);
241 continue;
242 }
243 if label_info.clip[i][0] <= 1 || label_info.clip[i][1] >= xsize_local - 2 {
244 debug!("Label {} skipped due to X-Clip bounds", i);
245 continue;
246 }
247 if label_info.clip[i][2] <= 1 || label_info.clip[i][3] >= ysize_local - 2 {
248 debug!("Label {} skipped due to Y-Clip bounds", i);
249 continue;
250 }
251
252 let mut current_marker = ARMarkerInfo2::default();
253
254 let ret = ar_get_contour(
255 &label_info.label_image,
256 xsize_local,
257 ysize_local,
258 (i + 1) as i32,
259 &label_info.clip[i],
260 &mut current_marker,
261 );
262
263 if ret.is_err() {
264 debug!("ar_get_contour failed for label {}: {:?}", i, ret.unwrap_err());
265 continue;
266 }
267
268 let ret = check_square(label_info.area[i], &mut current_marker, square_fit_thresh);
269 if ret.is_err() {
270 debug!("check_square failed for label {}: {:?}", i, ret.unwrap_err());
271 continue;
272 }
273
274 current_marker.area = label_info.area[i];
275 current_marker.pos[0] = label_info.pos[i][0];
276 current_marker.pos[1] = label_info.pos[i][1];
277
278 marker_info2[*marker2_num as usize] = current_marker;
279 *marker2_num += 1;
280 if *marker2_num as usize == marker_info2.len() {
281 break;
282 }
283 }
284
285 let num_markers = *marker2_num as usize;
287 for i in 0..num_markers {
288 for j in i + 1..num_markers {
289 if marker_info2[i].area == 0 || marker_info2[j].area == 0 {
290 continue;
291 }
292 let d = (marker_info2[i].pos[0] - marker_info2[j].pos[0]).powi(2)
293 + (marker_info2[i].pos[1] - marker_info2[j].pos[1]).powi(2);
294
295 if marker_info2[i].area > marker_info2[j].area {
296 if d < (marker_info2[i].area as ARdouble) / 4.0 {
297 marker_info2[j].area = 0;
298 }
299 } else {
300 if d < (marker_info2[j].area as ARdouble) / 4.0 {
301 marker_info2[i].area = 0;
302 }
303 }
304 }
305 }
306
307 let mut valid_count = 0;
309 for i in 0..num_markers {
310 if marker_info2[i].area > 0 {
311 if i != valid_count {
312 marker_info2[valid_count] = marker_info2[i].clone();
313 }
314 valid_count += 1;
315 }
316 }
317 *marker2_num = valid_count as i32;
318
319 if matches!(image_proc_mode, ImageProcMode::FieldImage) {
320 for i in 0..(*marker2_num as usize) {
321 let pm = &mut marker_info2[i];
322 pm.area *= 4;
323 pm.pos[0] *= 2.0;
324 pm.pos[1] *= 2.0;
325 for j in 0..pm.coord_num as usize {
326 pm.x_coord[j] *= 2;
327 pm.y_coord[j] *= 2;
328 }
329 }
330 }
331
332 Ok(())
333}
334
335fn ar_get_contour(
347 limage: &[crate::types::ARLabelingLabelType],
348 xsize: i32,
349 _ysize: i32,
350 label: i32,
351 clip: &[i32; 4],
352 marker_info2: &mut ARMarkerInfo2,
353) -> Result<(), &'static str> {
354 let xdir = [0, 1, 1, 1, 0, -1, -1, -1];
355 let ydir = [-1, -1, 0, 1, 1, 1, 0, -1];
356
357 let mut sx = -1;
358 let sy = clip[2];
359
360 let mut p_idx = (sy * xsize + clip[0]) as usize;
363 for i in clip[0]..=clip[1] {
364 if p_idx < limage.len() {
365 if limage[p_idx] == label as crate::types::ARLabelingLabelType {
366 sx = i;
367 break;
368 }
369 }
370 p_idx += 1;
371 }
372
373 if sx == -1 {
374 debug!("ar_get_contour failed. label={}. clip={:?}.", label, clip);
375 return Err("Contour start point not found");
376 }
377
378 marker_info2.coord_num = 1;
379 marker_info2.x_coord[0] = sx;
380 marker_info2.y_coord[0] = sy;
381 let mut dir = 5;
382
383 loop {
384 let last_idx = (marker_info2.coord_num - 1) as usize;
385 let p_idx = (marker_info2.y_coord[last_idx] * xsize + marker_info2.x_coord[last_idx]) as usize;
386
387 dir = (dir + 5) % 8;
388 let mut found = false;
389 for _ in 0..8 {
390 let next_idx = (p_idx as isize + ydir[dir] as isize * xsize as isize + xdir[dir] as isize) as usize;
391 if next_idx < limage.len() && limage[next_idx] > 0 {
392 found = true;
393 break;
394 }
395 dir = (dir + 1) % 8;
396 }
397
398 if !found {
399 return Err("Contour broken");
400 }
401
402 let curr_idx = marker_info2.coord_num as usize;
403 marker_info2.x_coord[curr_idx] = marker_info2.x_coord[last_idx] + xdir[dir];
404 marker_info2.y_coord[curr_idx] = marker_info2.y_coord[last_idx] + ydir[dir];
405
406 if marker_info2.x_coord[curr_idx] == sx && marker_info2.y_coord[curr_idx] == sy {
407 break;
408 }
409
410 marker_info2.coord_num += 1;
411 if marker_info2.coord_num as usize >= AR_CHAIN_MAX - 1 {
412 return Err("Contour too long");
413 }
414 }
415
416 let mut dmax = 0;
417 let mut v1 = 0;
418
419 for i in 1..marker_info2.coord_num as usize {
420 let d = (marker_info2.x_coord[i] - sx).pow(2) + (marker_info2.y_coord[i] - sy).pow(2);
421 if d > dmax {
422 dmax = d;
423 v1 = i;
424 }
425 }
426
427 let mut wx = vec![0; v1];
428 let mut wy = vec![0; v1];
429
430 for i in 0..v1 {
431 wx[i] = marker_info2.x_coord[i];
432 wy[i] = marker_info2.y_coord[i];
433 }
434
435 let coord_num = marker_info2.coord_num as usize;
436 for i in v1..coord_num {
437 marker_info2.x_coord[i - v1] = marker_info2.x_coord[i];
438 marker_info2.y_coord[i - v1] = marker_info2.y_coord[i];
439 }
440
441 let offset = coord_num - v1;
442 for i in 0..v1 {
443 marker_info2.x_coord[offset + i] = wx[i];
444 marker_info2.y_coord[offset + i] = wy[i];
445 }
446
447 let end_idx = marker_info2.coord_num as usize;
448 marker_info2.x_coord[end_idx] = marker_info2.x_coord[0];
449 marker_info2.y_coord[end_idx] = marker_info2.y_coord[0];
450 marker_info2.coord_num += 1;
451
452 Ok(())
453}
454
455fn check_square(area: i32, marker_info2: &mut ARMarkerInfo2, factor: ARdouble) -> Result<(), &'static str> {
475 let mut dmax = 0;
476 let mut v1 = 0;
477 let sx = marker_info2.x_coord[0];
478 let sy = marker_info2.y_coord[0];
479 let coord_num = marker_info2.coord_num as usize;
480
481 for i in 1..(coord_num - 1) {
482 let d = (marker_info2.x_coord[i] - sx).pow(2) + (marker_info2.y_coord[i] - sy).pow(2);
483 if d > dmax {
484 dmax = d;
485 v1 = i;
486 }
487 }
488
489 let thresh = ((area as f64) / 0.75) * 0.01 * factor;
490 let mut vertex = [0; 10];
491 vertex[0] = 0;
492 let mut wv1 = [0; 10];
493 let mut wvnum1 = 0;
494 let mut wv2 = [0; 10];
495 let mut wvnum2 = 0;
496
497 if get_vertex(&marker_info2.x_coord, &marker_info2.y_coord, 0, v1, thresh, &mut wv1, &mut wvnum1).is_err() {
498 return Err("Square check failed");
499 }
500 if get_vertex(&marker_info2.x_coord, &marker_info2.y_coord, v1, coord_num - 1, thresh, &mut wv2, &mut wvnum2).is_err() {
501 return Err("Square check failed");
502 }
503
504 if wvnum1 == 1 && wvnum2 == 1 {
505 vertex[1] = wv1[0];
506 vertex[2] = v1;
507 vertex[3] = wv2[0];
508 } else if wvnum1 > 1 && wvnum2 == 0 {
509 let v2 = v1 / 2;
510 wvnum1 = 0;
511 wvnum2 = 0;
512 if get_vertex(&marker_info2.x_coord, &marker_info2.y_coord, 0, v2, thresh, &mut wv1, &mut wvnum1).is_err() {
513 return Err("Square check failed");
514 }
515 if get_vertex(&marker_info2.x_coord, &marker_info2.y_coord, v2, v1, thresh, &mut wv2, &mut wvnum2).is_err() {
516 return Err("Square check failed");
517 }
518 if wvnum1 == 1 && wvnum2 == 1 {
519 vertex[1] = wv1[0];
520 vertex[2] = wv2[0];
521 vertex[3] = v1;
522 } else {
523 return Err("Not a square");
524 }
525 } else if wvnum1 == 0 && wvnum2 > 1 {
526 let v2 = (v1 + coord_num - 1) / 2;
527 wvnum1 = 0;
528 wvnum2 = 0;
529 if get_vertex(&marker_info2.x_coord, &marker_info2.y_coord, v1, v2, thresh, &mut wv1, &mut wvnum1).is_err() {
530 return Err("Square check failed");
531 }
532 if get_vertex(&marker_info2.x_coord, &marker_info2.y_coord, v2, coord_num - 1, thresh, &mut wv2, &mut wvnum2).is_err() {
533 return Err("Square check failed");
534 }
535 if wvnum1 == 1 && wvnum2 == 1 {
536 vertex[1] = v1;
537 vertex[2] = wv1[0];
538 vertex[3] = wv2[0];
539 } else {
540 return Err("Not a square");
541 }
542 } else {
543 return Err("Not a square");
544 }
545
546 marker_info2.vertex[0] = vertex[0] as i32;
547 marker_info2.vertex[1] = vertex[1] as i32;
548 marker_info2.vertex[2] = vertex[2] as i32;
549 marker_info2.vertex[3] = vertex[3] as i32;
550 marker_info2.vertex[4] = (coord_num - 1) as i32;
551
552 Ok(())
553}
554
555fn get_vertex(
566 x_coord: &[i32],
567 y_coord: &[i32],
568 st: usize,
569 ed: usize,
570 thresh: ARdouble,
571 vertex: &mut [usize],
572 vnum: &mut usize,
573) -> Result<(), &'static str> {
574 let a = (y_coord[ed] - y_coord[st]) as f64;
575 let b = (x_coord[st] - x_coord[ed]) as f64;
576 let c = (x_coord[ed] * y_coord[st] - y_coord[ed] * x_coord[st]) as f64;
577
578 let mut dmax = 0.0;
579 let mut v1 = st + 1;
580
581 for i in (st + 1)..ed {
582 let d = a * (x_coord[i] as f64) + b * (y_coord[i] as f64) + c;
583 if d * d > dmax {
584 dmax = d * d;
585 v1 = i;
586 }
587 }
588
589 if dmax / (a * a + b * b) > thresh {
590 if get_vertex(x_coord, y_coord, st, v1, thresh, vertex, vnum).is_err() {
591 return Err("Vertex expansion failed");
592 }
593
594 if *vnum > 5 {
595 return Err("Too many vertices");
596 }
597 vertex[*vnum] = v1;
598 *vnum += 1;
599
600 if get_vertex(x_coord, y_coord, v1, ed, thresh, vertex, vnum).is_err() {
601 return Err("Vertex expansion failed");
602 }
603 }
604
605 Ok(())
606}
607
608use crate::math::{ARMat, ARVec};
609use crate::types::{ARParamLTf, ARMarkerInfo};
610
611pub fn ar_get_line(
632 x_coord: &[i32],
633 y_coord: &[i32],
634 _coord_num: usize,
635 vertex: &[i32],
636 param_ltf: &ARParamLTf,
637 line: &mut [[ARdouble; 3]; 4],
638 v: &mut [[ARdouble; 2]; 4],
639) -> Result<(), &'static str> {
640 for i in 0..4 {
641 let w1 = ((vertex[i + 1] - vertex[i] + 1) as f64) * 0.05 + 0.5;
642 let st = (vertex[i] as f64 + w1) as usize;
643 let ed = (vertex[i + 1] as f64 - w1) as usize;
644 let n = ed - st + 1;
645
646 let mut input = ARMat::new(n as i32, 2);
647 for j in 0..n {
648 let (ix, iy) = param_ltf.observ2ideal(x_coord[st + j] as f32, y_coord[st + j] as f32)?;
649 input.m[j * 2 + 0] = ix as f64;
650 input.m[j * 2 + 1] = iy as f64;
651 }
652
653 let mut evec = ARMat::new(2, 2);
654 let mut ev = ARVec::new(2);
655 let mut mean = ARVec::new(2);
656
657 input.pca(&mut evec, &mut ev, &mut mean)?;
658
659 line[i][0] = evec.m[1];
660 line[i][1] = -evec.m[0];
661 line[i][2] = -(line[i][0] * mean.v[0] + line[i][1] * mean.v[1]);
662 }
663
664 for i in 0..4 {
665 let w1 = line[(i + 3) % 4][0] * line[i][1] - line[i][0] * line[(i + 3) % 4][1];
666 if w1.abs() < 0.0001 {
667 return Err("Lines are near parallel");
668 }
669 v[i][0] = (line[(i + 3) % 4][1] * line[i][2] - line[i][1] * line[(i + 3) % 4][2]) / w1;
670 v[i][1] = (line[i][0] * line[(i + 3) % 4][2] - line[(i + 3) % 4][0] * line[i][2]) / w1;
671 }
672
673 Ok(())
674}
675
676pub fn ar_get_marker_info(
701 image: &[u8],
702 xsize: i32,
703 ysize: i32,
704 pixel_format: crate::types::ARPixelFormat,
705 marker_info2: &[ARMarkerInfo2],
706 marker2_num: i32,
707 image_proc_mode: ImageProcMode,
708 patt_detect_mode: i32,
709 param_ltf: &ARParamLTf,
710 patt_ratio: ARdouble,
711 patt_handle_opt: Option<&crate::types::ARPattHandle>,
712 marker_info: &mut [ARMarkerInfo],
713 marker_num: &mut i32,
714 matrix_code_type: crate::types::ARMatrixCodeType,
715) -> Result<(), &'static str> {
716 let mut j = 0;
717
718 for i in 0..marker2_num as usize {
719 marker_info[j].area = marker_info2[i].area;
720
721 if let Ok((ix, iy)) = param_ltf.observ2ideal(marker_info2[i].pos[0] as f32, marker_info2[i].pos[1] as f32) {
722 marker_info[j].pos[0] = ix as f64;
723 marker_info[j].pos[1] = iy as f64;
724 } else {
725 continue;
726 }
727
728 if ar_get_line(
729 &marker_info2[i].x_coord,
730 &marker_info2[i].y_coord,
731 marker_info2[i].coord_num as usize,
732 &marker_info2[i].vertex,
733 param_ltf,
734 &mut marker_info[j].line,
735 &mut marker_info[j].vertex,
736 ).is_err() {
737 continue;
738 }
739
740 let is_matrix_mode = patt_detect_mode == crate::types::AR_MATRIX_CODE_DETECTION
742 || patt_detect_mode == crate::types::AR_TEMPLATE_MATCHING_COLOR_AND_MATRIX_CODE_DETECTION;
743
744 if is_matrix_mode {
745 let mut mc_id = -1i32;
747 let mut mc_dir = -1i32;
748 let mut mc_cf = 0.0f64;
749 let mut mc_err = 0i32;
750 match crate::matrix::ar_matrix_code_get_id(
751 image,
752 xsize,
753 ysize,
754 &marker_info[j].vertex,
755 matrix_code_type,
756 pixel_format,
757 patt_ratio,
758 &mut mc_id,
759 &mut mc_dir,
760 &mut mc_cf,
761 &mut mc_err,
762 ) {
763 Ok(()) => {
764 marker_info[j].id_matrix = mc_id;
765 marker_info[j].dir_matrix = mc_dir;
766 marker_info[j].cf_matrix = mc_cf;
767 marker_info[j].error_corrected = mc_err;
768 debug!("ar_get_marker_info: barcode id={}, dir={}, cf={:.4}", mc_id, mc_dir, mc_cf);
769 }
770 Err(e) => {
771 debug!("ar_get_marker_info: barcode decode failed: {}", e);
772 marker_info[j].id_matrix = -1;
773 marker_info[j].dir_matrix = -1;
774 marker_info[j].cf_matrix = 0.0;
775 }
776 }
777 }
778
779 if !is_matrix_mode || patt_detect_mode == crate::types::AR_TEMPLATE_MATCHING_COLOR_AND_MATRIX_CODE_DETECTION {
780 if let Some(patt_handle) = patt_handle_opt {
782 if patt_handle.patt_num > 0 {
783 let patt_size = patt_handle.patt_size;
784 let ext_patt_len = if patt_detect_mode == crate::pattern::AR_TEMPLATE_MATCHING_COLOR {
785 (patt_size * patt_size * 3) as usize
786 } else {
787 (patt_size * patt_size) as usize
788 };
789 let mut ext_patt = vec![0u8; ext_patt_len];
790
791 let res = crate::pattern::ar_patt_get_image(
792 image_proc_mode as i32,
793 patt_detect_mode,
794 patt_size,
795 patt_size * 2,
796 image,
797 xsize,
798 ysize,
799 pixel_format,
800 &marker_info[j].vertex,
801 patt_ratio,
802 &mut ext_patt,
803 );
804
805 if res.is_ok() {
806 let mut p_code = -1;
807 let mut p_dir = 0;
808 let mut p_cf = -1.0;
809 let match_res = crate::pattern::pattern_match(
810 patt_handle,
811 patt_detect_mode,
812 &ext_patt,
813 patt_size,
814 &mut p_code,
815 &mut p_dir,
816 &mut p_cf,
817 );
818
819 if match_res.is_ok() && p_code >= 0 {
820 marker_info[j].id = p_code;
821 marker_info[j].dir = p_dir;
822 marker_info[j].cf = p_cf;
823 } else {
824 marker_info[j].id = -1;
825 marker_info[j].dir = 0;
826 marker_info[j].cf = p_cf;
827 }
828 } else {
829 marker_info[j].id = -1;
830 marker_info[j].dir = 0;
831 marker_info[j].cf = -1.0;
832 }
833 } else {
834 marker_info[j].id = -1;
835 marker_info[j].dir = 0;
836 marker_info[j].cf = 0.0;
837 }
838 } else {
839 marker_info[j].id = -1;
840 marker_info[j].dir = 0;
841 marker_info[j].cf = 0.0;
842 }
843 }
844
845 j += 1;
846 }
847 *marker_num = j as i32;
848
849 Ok(())
850}
851
852#[cfg(test)]
853mod tests {
854 use super::*;
855
856 #[test]
857 fn test_ar_detect_marker2_empty() {
858 let mut label_info = ARLabelInfo::default();
859 let mut marker_info2 = vec![ARMarkerInfo2::default(); AR_SQUARE_MAX];
860 let mut marker2_num = 0;
861
862 let res = ar_detect_marker2(
863 640,
864 480,
865 &mut label_info,
866 ImageProcMode::FrameImage,
867 AR_AREA_MAX,
868 AR_AREA_MIN,
869 AR_SQUARE_FIT_THRESH,
870 &mut marker_info2,
871 &mut marker2_num,
872 );
873
874 assert!(res.is_ok());
875 assert_eq!(marker2_num, 0);
876 }
877}