1010#include <signal.h>
1111
1212#include "apriltag.h"
13+ #include "apriltag_pose.h"
1314#include "tag36h10.h"
1415#include "tag36h11.h"
1516#include "tag25h9.h"
@@ -227,6 +228,7 @@ static PyObject* apriltag_detect(apriltag_py_t* self,
227228 PyObject * result = NULL ;
228229 PyArrayObject * xy_c = NULL ;
229230 PyArrayObject * xy_lb_rb_rt_lt = NULL ;
231+ PyArrayObject * homography = NULL ;
230232 PyArrayObject * image = NULL ;
231233 PyObject * detections_tuple = NULL ;
232234
@@ -311,13 +313,32 @@ static PyObject* apriltag_detect(apriltag_py_t* self,
311313 * (double * )PyArray_GETPTR2 (xy_lb_rb_rt_lt , j , 1 ) = det -> p [j ][1 ];
312314 }
313315
316+ // Add homography matrix (3x3)
317+ homography = (PyArrayObject * )PyArray_SimpleNew (2 , ((npy_intp []){3 ,3 }), NPY_FLOAT64 );
318+ if (homography == NULL )
319+ {
320+ Py_DECREF (xy_c );
321+ Py_DECREF (xy_lb_rb_rt_lt );
322+ PyErr_SetString (PyExc_RuntimeError , "Could not allocate homography array" );
323+ goto done ;
324+ }
325+
326+ for (int j = 0 ; j < 3 ; j ++ )
327+ {
328+ for (int k = 0 ; k < 3 ; k ++ )
329+ {
330+ * (double * )PyArray_GETPTR2 (homography , j , k ) = MATD_EL (det -> H , j , k );
331+ }
332+ }
333+
314334 PyTuple_SET_ITEM (detections_tuple , i ,
315- Py_BuildValue ("{s:i,s:f,s:i,s:N,s:N}" ,
335+ Py_BuildValue ("{s:i,s:f,s:i,s:N,s:N,s:N }" ,
316336 "hamming" , det -> hamming ,
317337 "margin" , det -> decision_margin ,
318338 "id" , det -> id ,
319339 "center" , xy_c ,
320- "lb-rb-rt-lt" , xy_lb_rb_rt_lt ));
340+ "lb-rb-rt-lt" , xy_lb_rb_rt_lt ,
341+ "homography" , homography ));
321342 xy_c = NULL ;
322343 xy_lb_rb_rt_lt = NULL ;
323344 }
@@ -338,12 +359,148 @@ static PyObject* apriltag_detect(apriltag_py_t* self,
338359 return result ;
339360}
340361
362+ static PyObject * apriltag_estimate_tag_pose (apriltag_py_t * self ,
363+ PyObject * args )
364+ {
365+ PyObject * result = NULL ;
366+ PyObject * detection_dict = NULL ;
367+ PyArrayObject * R_array = NULL ;
368+ PyArrayObject * t_array = NULL ;
369+ matd_t * H_matrix = NULL ;
370+ double tagsize , fx , fy , cx , cy ;
371+
372+ if (!PyArg_ParseTuple (args , "Oddddd" ,
373+ & detection_dict ,
374+ & tagsize ,
375+ & fx , & fy , & cx , & cy ))
376+ return NULL ;
377+
378+ if (!PyDict_Check (detection_dict ))
379+ {
380+ PyErr_SetString (PyExc_TypeError , "First argument must be a detection dictionary" );
381+ return NULL ;
382+ }
383+
384+ // Extract detection information from the dictionary
385+ PyObject * py_id = PyDict_GetItemString (detection_dict , "id" );
386+ PyObject * py_hamming = PyDict_GetItemString (detection_dict , "hamming" );
387+ PyObject * py_margin = PyDict_GetItemString (detection_dict , "margin" );
388+ PyObject * py_center = PyDict_GetItemString (detection_dict , "center" );
389+ PyObject * py_corners = PyDict_GetItemString (detection_dict , "lb-rb-rt-lt" );
390+ PyObject * py_homography = PyDict_GetItemString (detection_dict , "homography" );
391+
392+ if (!py_id || !py_hamming || !py_margin || !py_center || !py_corners || !py_homography )
393+ {
394+ PyErr_SetString (PyExc_ValueError ,
395+ "Detection dictionary is missing required fields. "
396+ "Make sure you're using a detection from the updated detect() method that includes 'homography'." );
397+ return NULL ;
398+ }
399+
400+ // Create a temporary detection structure
401+ apriltag_detection_t det ;
402+ det .family = self -> tf ;
403+ det .id = PyLong_AsLong (py_id );
404+ det .hamming = PyLong_AsLong (py_hamming );
405+ det .decision_margin = PyFloat_AsDouble (py_margin );
406+
407+ // Extract center
408+ PyArrayObject * center_array = (PyArrayObject * )py_center ;
409+ det .c [0 ] = * (double * )PyArray_GETPTR1 (center_array , 0 );
410+ det .c [1 ] = * (double * )PyArray_GETPTR1 (center_array , 1 );
411+
412+ // Extract corners
413+ PyArrayObject * corners_array = (PyArrayObject * )py_corners ;
414+ for (int i = 0 ; i < 4 ; i ++ )
415+ {
416+ det .p [i ][0 ] = * (double * )PyArray_GETPTR2 (corners_array , i , 0 );
417+ det .p [i ][1 ] = * (double * )PyArray_GETPTR2 (corners_array , i , 1 );
418+ }
419+
420+ // Extract and copy homography matrix
421+ PyArrayObject * homography_array = (PyArrayObject * )py_homography ;
422+ H_matrix = matd_create (3 , 3 );
423+ if (!H_matrix )
424+ {
425+ PyErr_SetString (PyExc_RuntimeError , "Could not allocate homography matrix" );
426+ return NULL ;
427+ }
428+
429+ for (int i = 0 ; i < 3 ; i ++ )
430+ {
431+ for (int j = 0 ; j < 3 ; j ++ )
432+ {
433+ MATD_EL (H_matrix , i , j ) = * (double * )PyArray_GETPTR2 (homography_array , i , j );
434+ }
435+ }
436+ det .H = H_matrix ;
437+
438+ // Setup detection info
439+ apriltag_detection_info_t info ;
440+ info .det = & det ;
441+ info .tagsize = tagsize ;
442+ info .fx = fx ;
443+ info .fy = fy ;
444+ info .cx = cx ;
445+ info .cy = cy ;
446+
447+ // Estimate pose
448+ apriltag_pose_t pose ;
449+ double error = estimate_tag_pose (& info , & pose );
450+
451+ // Create numpy arrays for R and t
452+ R_array = (PyArrayObject * )PyArray_SimpleNew (2 , ((npy_intp []){3 , 3 }), NPY_FLOAT64 );
453+ t_array = (PyArrayObject * )PyArray_SimpleNew (2 , ((npy_intp []){3 , 1 }), NPY_FLOAT64 );
454+
455+ if (!R_array || !t_array )
456+ {
457+ PyErr_SetString (PyExc_RuntimeError , "Could not allocate output arrays" );
458+ goto cleanup ;
459+ }
460+
461+ // Copy rotation matrix
462+ for (int i = 0 ; i < 3 ; i ++ )
463+ {
464+ for (int j = 0 ; j < 3 ; j ++ )
465+ {
466+ * (double * )PyArray_GETPTR2 (R_array , i , j ) = MATD_EL (pose .R , i , j );
467+ }
468+ }
469+
470+ // Copy translation vector
471+ for (int i = 0 ; i < 3 ; i ++ )
472+ {
473+ * (double * )PyArray_GETPTR2 (t_array , i , 0 ) = MATD_EL (pose .t , i , 0 );
474+ }
475+
476+ result = Py_BuildValue ("{s:N,s:N,s:d}" ,
477+ "R" , R_array ,
478+ "t" , t_array ,
479+ "error" , error );
480+ R_array = NULL ;
481+ t_array = NULL ;
482+
483+ cleanup :
484+ if (H_matrix )
485+ matd_destroy (H_matrix );
486+ if (pose .R )
487+ matd_destroy (pose .R );
488+ if (pose .t )
489+ matd_destroy (pose .t );
490+ Py_XDECREF (R_array );
491+ Py_XDECREF (t_array );
492+
493+ return result ;
494+ }
495+
341496
342497#include "apriltag_detect_docstring.h"
343498#include "apriltag_py_type_docstring.h"
499+ #include "apriltag_estimate_tag_pose_docstring.h"
344500
345501static PyMethodDef apriltag_methods [] =
346502 { PYMETHODDEF_ENTRY (apriltag_ , detect , METH_VARARGS ),
503+ PYMETHODDEF_ENTRY (apriltag_ , estimate_tag_pose , METH_VARARGS ),
347504 {NULL , NULL , 0 , NULL }
348505 };
349506
0 commit comments