1use std::os::raw::c_char;
5
6use machine_vision_formats::pixel_format::Mono8;
7
8pub trait ArrayDealloc {
10 unsafe fn array_dealloc(zarray_ptr: *mut apriltag_sys::zarray);
17}
18
19#[repr(transparent)]
21pub struct Zarray<T: ArrayDealloc> {
22 inner: *mut apriltag_sys::zarray_t,
23 marker: std::marker::PhantomData<T>,
24}
25
26impl<T: ArrayDealloc> Zarray<T> {
27 unsafe fn from_raw(inner: *mut apriltag_sys::zarray_t) -> Zarray<T> {
28 unsafe {
29 assert!(!inner.is_null());
30 assert!((*inner).el_sz == std::mem::size_of::<T>());
31 Self {
35 inner,
36 marker: std::marker::PhantomData,
37 }
38 }
39 }
40
41 pub fn len(&self) -> usize {
43 unsafe {
44 let ptr = *self.inner;
45 ptr.size.try_into().unwrap()
46 }
47 }
48
49 pub fn is_empty(&self) -> bool {
50 self.len() == 0
51 }
52
53 pub fn as_slice(&self) -> &[T] {
55 unsafe {
56 let ptr = *self.inner;
57 if self.is_empty() {
58 &[]
59 } else {
60 std::slice::from_raw_parts(ptr.data as *const T, self.len())
61 }
62 }
63 }
64}
65
66impl<T: ArrayDealloc> Drop for Zarray<T> {
67 fn drop(&mut self) {
68 if !self.inner.is_null() {
69 unsafe { T::array_dealloc(self.inner) };
71 self.inner = std::ptr::null::<apriltag_sys::zarray_t>() as *mut _;
72 }
73 }
74}
75
76pub trait ImageU8 {
77 fn inner(&self) -> &apriltag_sys::image_u8;
78
79 fn inner_mut(&mut self) -> &mut apriltag_sys::image_u8;
80
81 fn data(&self) -> &[u8];
82 fn data_mut(&mut self) -> &mut [u8];
83
84 fn width(&self) -> i32 {
85 self.inner().width
86 }
87
88 fn height(&self) -> i32 {
89 self.inner().height
90 }
91
92 fn stride(&self) -> i32 {
93 self.inner().stride
94 }
95}
96
97pub struct ImageU8Owned {
98 inner: apriltag_sys::image_u8,
99 data: Vec<u8>,
100}
101
102impl ImageU8Owned {
103 pub fn new(width: i32, height: i32, stride: i32, mut data: Vec<u8>) -> Option<Self> {
104 let min_size = (height as usize - 1) * stride as usize + width as usize;
105 if data.len() >= min_size {
106 let inner = apriltag_sys::image_u8 {
107 width,
108 height,
109 stride,
110 buf: data.as_mut_ptr(),
111 };
112
113 Some(Self { inner, data })
114 } else {
115 None
116 }
117 }
118}
119
120impl From<ImageU8Owned> for Vec<u8> {
121 fn from(val: ImageU8Owned) -> Self {
122 val.data
123 }
124}
125
126impl ImageU8 for ImageU8Owned {
127 fn inner(&self) -> &apriltag_sys::image_u8 {
128 &self.inner
129 }
130
131 fn inner_mut(&mut self) -> &mut apriltag_sys::image_u8 {
132 &mut self.inner
133 }
134
135 fn data(&self) -> &[u8] {
136 &self.data
137 }
138
139 fn data_mut(&mut self) -> &mut [u8] {
140 &mut self.data
141 }
142}
143
144pub struct ImageU8Borrowed<'a> {
145 inner: apriltag_sys::image_u8,
146 data_lifetime: std::marker::PhantomData<&'a [u8]>,
147}
148
149impl<'a> ImageU8Borrowed<'a> {
150 pub fn new(width: i32, height: i32, stride: i32, data: &'a [u8]) -> Option<Self> {
151 let min_size = (height as usize - 1) * stride as usize + width as usize;
152 if data.len() >= min_size {
153 let inner = apriltag_sys::image_u8 {
154 width,
155 height,
156 stride,
157 buf: data.as_ptr() as *mut u8,
158 };
159
160 Some(Self {
161 inner,
162 data_lifetime: std::marker::PhantomData,
163 })
164 } else {
165 None
166 }
167 }
168 pub fn view(im: &dyn machine_vision_formats::AsImageStride<Mono8>) -> Self {
169 let inner = apriltag_sys::image_u8 {
170 width: im.width().try_into().unwrap(),
171 height: im.height().try_into().unwrap(),
172 stride: im.stride().try_into().unwrap(),
173 buf: im.buffer_ref().data.as_ptr() as *mut u8,
174 };
175
176 Self {
177 inner,
178 data_lifetime: std::marker::PhantomData,
179 }
180 }
181}
182
183impl<'a> ImageU8 for ImageU8Borrowed<'a> {
184 fn inner(&self) -> &apriltag_sys::image_u8 {
185 &self.inner
186 }
187
188 fn inner_mut(&mut self) -> &mut apriltag_sys::image_u8 {
189 &mut self.inner
190 }
191
192 fn data(&self) -> &[u8] {
193 let len = self.inner.height as usize * self.inner.stride as usize;
194 unsafe { std::slice::from_raw_parts(self.inner.buf, len) }
195 }
196
197 fn data_mut(&mut self) -> &mut [u8] {
198 let len = self.inner.height as usize * self.inner.stride as usize;
199 unsafe { std::slice::from_raw_parts_mut(self.inner.buf, len) }
200 }
201}
202
203#[derive(Debug)]
205pub struct Detector {
206 td: *mut apriltag_sys::apriltag_detector,
207 families: Vec<Family>,
208}
209
210unsafe impl Send for Detector {}
211
212impl Default for Detector {
213 fn default() -> Self {
214 Self::new()
215 }
216}
217
218impl Detector {
219 pub fn new() -> Self {
221 let td: *mut apriltag_sys::apriltag_detector =
222 unsafe { apriltag_sys::apriltag_detector_create() };
223 Self {
224 td,
225 families: vec![],
226 }
227 }
228
229 pub fn add_family(&mut self, family: Family) {
240 let bits = family.family_type().recommended_correction_bits();
245 self.add_family_bits(family, bits)
246 }
247
248 pub fn add_family_bits(&mut self, family: Family, bits: i32) {
252 unsafe { apriltag_sys::apriltag_detector_add_family_bits(self.td, family.0, bits) };
253 self.families.push(family)
254 }
255
256 pub fn clear_families(&mut self) {
258 unsafe { apriltag_sys::apriltag_detector_clear_families(self.td) };
259 self.families.clear()
260 }
261
262 pub fn detect(&self, im: &apriltag_sys::image_u8) -> Zarray<Detection> {
264 let detections: *mut apriltag_sys::zarray_t = unsafe {
265 let im_orig = im as *const apriltag_sys::image_u8;
266 apriltag_sys::apriltag_detector_detect(self.td, im_orig as *mut _)
267 };
268 let result: Zarray<Detection> = unsafe { Zarray::from_raw(detections) };
269
270 for det in result.as_slice() {
271 debug_assert_eq!(det.center()[0], det.h()[2]);
272 debug_assert_eq!(det.center()[1], det.h()[5]);
273 }
274 result
275 }
276}
277
278impl Drop for Detector {
279 fn drop(&mut self) {
280 if !self.td.is_null() {
281 unsafe { apriltag_sys::apriltag_detector_destroy(self.td) };
282 self.td = std::ptr::null::<apriltag_sys::apriltag_detector>() as *mut _;
283 }
284 }
285}
286
287impl std::convert::AsMut<apriltag_sys::apriltag_detector> for Detector {
288 fn as_mut(&mut self) -> &mut apriltag_sys::apriltag_detector {
289 unsafe { &mut *self.td }
290 }
291}
292
293#[repr(transparent)]
294#[derive(Debug)]
295pub struct Family(*mut apriltag_sys::apriltag_family_t);
296
297unsafe impl Send for Family {}
298
299impl Family {
300 pub fn new_tag_16h5() -> Self {
302 let tf: *mut apriltag_sys::apriltag_family_t = unsafe { apriltag_sys::tag16h5_create() };
303 Self(tf)
304 }
305
306 pub fn new_tag_25h9() -> Self {
308 let tf: *mut apriltag_sys::apriltag_family_t = unsafe { apriltag_sys::tag25h9_create() };
309 Self(tf)
310 }
311
312 pub fn new_tag_36h11() -> Self {
314 let tf: *mut apriltag_sys::apriltag_family_t = unsafe { apriltag_sys::tag36h11_create() };
315 Self(tf)
316 }
317
318 pub fn new_tag_circle_21h7() -> Self {
320 let tf: *mut apriltag_sys::apriltag_family_t =
321 unsafe { apriltag_sys::tagCircle21h7_create() };
322 Self(tf)
323 }
324
325 pub fn new_tag_circle_49h12() -> Self {
327 let tf: *mut apriltag_sys::apriltag_family_t =
328 unsafe { apriltag_sys::tagCircle49h12_create() };
329 Self(tf)
330 }
331
332 pub fn new_tag_custom_48h12() -> Self {
334 let tf: *mut apriltag_sys::apriltag_family_t =
335 unsafe { apriltag_sys::tagCustom48h12_create() };
336 Self(tf)
337 }
338
339 pub fn new_tag_standard_41h12() -> Self {
341 let tf: *mut apriltag_sys::apriltag_family_t =
342 unsafe { apriltag_sys::tagStandard41h12_create() };
343 Self(tf)
344 }
345
346 pub fn new_tag_standard_52h13() -> Self {
348 let tf: *mut apriltag_sys::apriltag_family_t =
349 unsafe { apriltag_sys::tagStandard52h13_create() };
350 Self(tf)
351 }
352
353 pub fn family_type(&self) -> FamilyType {
354 let name = unsafe { (*self.0).name };
355 FamilyType::from_name(name)
356 }
357}
358
359impl Drop for Family {
360 fn drop(&mut self) {
361 if !self.0.is_null() {
362 use FamilyType::*;
363 match self.family_type() {
364 Family16H5 => unsafe { apriltag_sys::tag16h5_destroy(self.0) },
365 Family25H9 => unsafe { apriltag_sys::tag25h9_destroy(self.0) },
366 Family36H11 => unsafe { apriltag_sys::tag36h11_destroy(self.0) },
367 FamilyCircle21h7 => unsafe { apriltag_sys::tagCircle21h7_destroy(self.0) },
368 FamilyCircle49H12 => unsafe { apriltag_sys::tagCircle49h12_destroy(self.0) },
369 FamilyCustom48h12 => unsafe { apriltag_sys::tagCustom48h12_destroy(self.0) },
370 Family41H12 => unsafe { apriltag_sys::tagStandard41h12_destroy(self.0) },
371 Family52H13 => unsafe { apriltag_sys::tagStandard52h13_destroy(self.0) },
372 }
373 self.0 = std::ptr::null::<apriltag_sys::apriltag_family_t>() as *mut _;
374 }
375 }
376}
377
378#[derive(Debug)]
379pub enum FamilyType {
380 Family16H5,
381 Family25H9,
382 Family36H11,
383 FamilyCircle21h7,
384 FamilyCircle49H12,
385 FamilyCustom48h12,
386 Family41H12,
387 Family52H13,
388}
389
390impl FamilyType {
391 fn from_name(name: *mut c_char) -> Self {
392 let slice = unsafe { std::ffi::CStr::from_ptr(name) };
393
394 use FamilyType::*;
395 match slice.to_bytes() {
396 b"tag16h5" => Family16H5,
397 b"tag25h9" => Family25H9,
398 b"tag36h11" => Family36H11,
399 b"tagCircle21h7" => FamilyCircle21h7,
400 b"tagCircle49h12" => FamilyCircle49H12,
401 b"tagCustom48h12" => FamilyCustom48h12,
402 b"tagStandard41h12" => Family41H12,
403 b"tagStandard52h13" => Family52H13,
404 other => match std::str::from_utf8(other) {
405 Ok(name) => panic!("unknown tag: {}", name),
406 Err(_) => panic!("unknown non-utf8 tag: {:?}", other),
407 },
408 }
409 }
410
411 pub fn to_str(&self) -> &str {
412 use FamilyType::*;
413 match self {
414 Family16H5 => "tag16h5",
415 Family25H9 => "tag25h9",
416 Family36H11 => "tag36h11",
417 FamilyCircle21h7 => "tagCircle21h7",
418 FamilyCircle49H12 => "tagCircle49h12",
419 FamilyCustom48h12 => "tagCustom48h12",
420 Family41H12 => "tagStandard41h12",
421 Family52H13 => "tagStandard52h13",
422 }
423 }
424
425 pub fn recommended_correction_bits(&self) -> i32 {
441 use FamilyType::*;
442 match self {
443 Family16H5 => 0,
446 Family25H9 => 0,
447 FamilyCircle21h7 => 1,
448 Family36H11 | Family41H12 | FamilyCircle49H12 | FamilyCustom48h12 | Family52H13 => 2,
450 }
451 }
452}
453
454#[repr(transparent)]
455pub struct Detection(*mut apriltag_sys::apriltag_detection_t);
456
457impl Detection {
458 pub fn id(&self) -> i32 {
459 unsafe { (*self.0).id }
460 }
461 pub fn family_type(&self) -> FamilyType {
462 let fam_ptr = unsafe { (*self.0).family };
463 let name = unsafe { (*fam_ptr).name };
464 FamilyType::from_name(name)
465 }
466 pub fn hamming(&self) -> i32 {
467 unsafe { (*self.0).hamming }
468 }
469 pub fn decision_margin(&self) -> f32 {
470 unsafe { (*self.0).decision_margin }
471 }
472 pub fn h(&self) -> &[f64] {
473 unsafe { std::slice::from_raw_parts((*(*self.0).H).data, 9) }
474 }
475 pub fn center(&self) -> &[f64; 2] {
476 unsafe { &(*self.0).c }
477 }
478}
479
480impl std::fmt::Debug for Detection {
481 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
482 f.debug_struct("Detection")
483 .field("id", &self.id())
484 .field("hamming", &self.hamming())
485 .field("decision_margin", &self.decision_margin())
486 .field("center", &self.center())
487 .field("h", &self.h())
488 .finish_non_exhaustive()
489 }
490}
491
492impl ArrayDealloc for Detection {
493 unsafe fn array_dealloc(zarray_ptr: *mut apriltag_sys::zarray) {
494 unsafe {
495 apriltag_sys::apriltag_detections_destroy(zarray_ptr);
496 }
497 }
498}
499
500#[cfg(test)]
501mod test {
502 use crate::*;
503
504 #[test]
505 fn family_types() {
506 let f = Family::new_tag_16h5();
507 let ft = f.family_type();
508 let s = ft.to_str();
509 assert_eq!(s, "tag16h5");
510
511 let f = Family::new_tag_25h9();
512 let ft = f.family_type();
513 let s = ft.to_str();
514 assert_eq!(s, "tag25h9");
515
516 let f = Family::new_tag_36h11();
517 let ft = f.family_type();
518 let s = ft.to_str();
519 assert_eq!(s, "tag36h11");
520
521 let f = Family::new_tag_circle_21h7();
522 let ft = f.family_type();
523 let s = ft.to_str();
524 assert_eq!(s, "tagCircle21h7");
525
526 let f = Family::new_tag_circle_49h12();
527 let ft = f.family_type();
528 let s = ft.to_str();
529 assert_eq!(s, "tagCircle49h12");
530
531 let f = Family::new_tag_custom_48h12();
532 let ft = f.family_type();
533 let s = ft.to_str();
534 assert_eq!(s, "tagCustom48h12");
535
536 let f = Family::new_tag_standard_41h12();
537 let ft = f.family_type();
538 let s = ft.to_str();
539 assert_eq!(s, "tagStandard41h12");
540
541 let f = Family::new_tag_standard_52h13();
542 let ft = f.family_type();
543 let s = ft.to_str();
544 assert_eq!(s, "tagStandard52h13");
545 }
546
547 #[test]
548 fn correction_bits_per_family() {
549 use FamilyType::*;
550 assert_eq!(Family36H11.recommended_correction_bits(), 2);
552 assert_eq!(Family41H12.recommended_correction_bits(), 2);
553 assert_eq!(FamilyCircle49H12.recommended_correction_bits(), 2);
554 assert_eq!(FamilyCustom48h12.recommended_correction_bits(), 2);
555 assert_eq!(Family52H13.recommended_correction_bits(), 2);
556 assert_eq!(Family16H5.recommended_correction_bits(), 0);
559 assert_eq!(Family25H9.recommended_correction_bits(), 0);
560 assert_eq!(FamilyCircle21h7.recommended_correction_bits(), 1);
561 }
562}