apriltag_pywrap.c
Go to the documentation of this file.
1 #define NPY_NO_DEPRECATED_API NPY_API_VERSION
2 
3 #include <stdbool.h>
4 #include <Python.h>
5 #include <structmember.h>
6 #include <numpy/arrayobject.h>
7 #include <signal.h>
8 
9 #include "apriltag.h"
10 #include "tag36h10.h"
11 #include "tag36h11.h"
12 #include "tag25h9.h"
13 #include "tag16h5.h"
14 #include "tagCircle21h7.h"
15 #include "tagCircle49h12.h"
16 #include "tagCustom48h12.h"
17 #include "tagStandard41h12.h"
18 #include "tagStandard52h13.h"
19 
20 
21 #define SUPPORTED_TAG_FAMILIES(_) \
22  _(tag36h10) \
23  _(tag36h11) \
24  _(tag25h9) \
25  _(tag16h5) \
26  _(tagCircle21h7) \
27  _(tagCircle49h12) \
28  _(tagStandard41h12) \
29  _(tagStandard52h13) \
30  _(tagCustom48h12)
31 
32 #define TAG_CREATE_FAMILY(name) \
33  else if (0 == strcmp(family, #name)) self->tf = name ## _create();
34 #define TAG_SET_DESTROY_FUNC(name) \
35  else if (0 == strcmp(family, #name)) self->destroy_func = name ## _destroy;
36 #define FAMILY_STRING(name) " " #name "\n"
37 
38 
39 // Python is silly. There's some nuance about signal handling where it sets a
40 // SIGINT (ctrl-c) handler to just set a flag, and the python layer then reads
41 // this flag and does the thing. Here I'm running C code, so SIGINT would set a
42 // flag, but not quit, so I can't interrupt the solver. Thus I reset the SIGINT
43 // handler to the default, and put it back to the python-specific version when
44 // I'm done
45 #define SET_SIGINT() struct sigaction sigaction_old; \
46 do { \
47  if( 0 != sigaction(SIGINT, \
48  &(struct sigaction){ .sa_handler = SIG_DFL }, \
49  &sigaction_old) ) \
50  { \
51  PyErr_SetString(PyExc_RuntimeError, "sigaction() failed"); \
52  goto done; \
53  } \
54 } while(0)
55 #define RESET_SIGINT() do { \
56  if( 0 != sigaction(SIGINT, \
57  &sigaction_old, NULL )) \
58  PyErr_SetString(PyExc_RuntimeError, "sigaction-restore failed"); \
59 } while(0)
60 
61 #define PYMETHODDEF_ENTRY(function_prefix, name, args) {#name, \
62  (PyCFunction)function_prefix ## name, \
63  args, \
64  function_prefix ## name ## _docstring}
65 
66 typedef struct {
67  PyObject_HEAD
68 
71  void (*destroy_func)(apriltag_family_t *tf);
73 
74 
75 static PyObject *
76 apriltag_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
77 {
78  errno = 0;
79 
80  bool success = false;
81 
82  apriltag_py_t* self = (apriltag_py_t*)type->tp_alloc(type, 0);
83  if(self == NULL) goto done;
84 
85  self->tf = NULL;
86  self->td = NULL;
87 
88  const char* family = NULL;
89  int Nthreads = 1;
90  int maxhamming = 1;
91  float decimate = 2.0;
92  float blur = 0.0;
93  bool refine_edges = true;
94  bool debug = false;
95  PyObject* py_refine_edges = NULL;
96  PyObject* py_debug = NULL;
97 
98  char* keywords[] = {"family",
99  "threads",
100  "maxhamming",
101  "decimate",
102  "blur",
103  "refine_edges",
104  "debug",
105  NULL };
106 
107  if(!PyArg_ParseTupleAndKeywords( args, kwargs, "s|iiffOO",
108  keywords,
109  &family,
110  &Nthreads,
111  &maxhamming,
112  &decimate,
113  &blur,
114  &py_refine_edges,
115  &py_debug ))
116  {
117  goto done;
118  }
119 
120  if(py_refine_edges != NULL)
121  refine_edges = PyObject_IsTrue(py_refine_edges);
122  if(py_debug != NULL)
123  debug = PyObject_IsTrue(py_debug);
124 
125 
127  else
128  {
129  PyErr_Format(PyExc_RuntimeError, "Unrecognized tag family name: '%s'. Families I know about:\n%s",
131  goto done;
132  }
133 
135 
136  self->td = apriltag_detector_create();
137  if(self->td == NULL)
138  {
139  PyErr_SetString(PyExc_RuntimeError, "apriltag_detector_create() failed!");
140  goto done;
141  }
142 
143  apriltag_detector_add_family_bits(self->td, self->tf, maxhamming);
144  self->td->quad_decimate = decimate;
145  self->td->quad_sigma = blur;
146  self->td->nthreads = Nthreads;
147  self->td->refine_edges = refine_edges;
148  self->td->debug = debug;
149 
150  switch(errno){
151  case EINVAL:
152  PyErr_SetString(PyExc_RuntimeError, "Unable to add family to detector. \"maxhamming\" parameter should not exceed 3");
153  break;
154  case ENOMEM:
155  PyErr_Format(PyExc_RuntimeError, "Unable to add family to detector due to insufficient memory to allocate the tag-family decoder. Try reducing \"maxhamming\" from %d or choose an alternative tag family",maxhamming);
156  break;
157  default:
158  success = true;
159  }
160 
161  done:
162  if(!success)
163  {
164  if(self != NULL)
165  {
166  if(self->td != NULL)
167  {
168  apriltag_detector_destroy(self->td);
169  self->td = NULL;
170  }
171  if(self->tf != NULL)
172  {
173  self->destroy_func(self->tf);
174  self->tf = NULL;
175  }
176  Py_DECREF(self);
177  }
178  return NULL;
179  }
180 
181  return (PyObject*)self;
182 }
183 
184 static void apriltag_dealloc(apriltag_py_t* self)
185 {
186  if(self == NULL)
187  return;
188  if(self->td != NULL)
189  {
190  apriltag_detector_destroy(self->td);
191  self->td = NULL;
192  }
193  if(self->tf != NULL)
194  {
195  self->destroy_func(self->tf);
196  self->tf = NULL;
197  }
198 
199  Py_TYPE(self)->tp_free((PyObject*)self);
200 }
201 
202 static PyObject* apriltag_detect(apriltag_py_t* self,
203  PyObject* args)
204 {
205  errno = 0;
206 
207  PyObject* result = NULL;
208  PyArrayObject* xy_c = NULL;
209  PyArrayObject* xy_lb_rb_rt_lt = NULL;
210  PyArrayObject* image = NULL;
211  PyObject* detections_tuple = NULL;
212 
213 #ifdef _POSIX_C_SOURCE
214  SET_SIGINT();
215 #endif
216  if(!PyArg_ParseTuple( args, "O&",
217  PyArray_Converter, &image ))
218  goto done;
219 
220  npy_intp* dims = PyArray_DIMS (image);
221  npy_intp* strides = PyArray_STRIDES(image);
222  int ndims = PyArray_NDIM (image);
223  if( ndims != 2 )
224  {
225  PyErr_Format(PyExc_RuntimeError, "The input image array must have exactly 2 dims; got %d",
226  ndims);
227  goto done;
228  }
229  if( PyArray_TYPE(image) != NPY_UINT8 )
230  {
231  PyErr_SetString(PyExc_RuntimeError, "The input image array must contain 8-bit unsigned data");
232  goto done;
233  }
234  if( strides[ndims-1] != 1 )
235  {
236  PyErr_SetString(PyExc_RuntimeError, "Image rows must live in contiguous memory");
237  goto done;
238  }
239 
240 
241  image_u8_t im = {.width = dims[1],
242  .height = dims[0],
243  .stride = strides[0],
244  .buf = PyArray_DATA(image)};
245 
246  zarray_t* detections = apriltag_detector_detect(self->td, &im);
247  int N = zarray_size(detections);
248 
249  if (N == 0 && errno == EAGAIN){
250  PyErr_Format(PyExc_RuntimeError, "Unable to create %d threads for detector", self->td->nthreads);
251  goto done;
252  }
253 
254  detections_tuple = PyTuple_New(N);
255  if(detections_tuple == NULL)
256  {
257  PyErr_Format(PyExc_RuntimeError, "Error creating output tuple of size %d", N);
258  goto done;
259  }
260 
261  for (int i=0; i < N; i++)
262  {
263  xy_c = (PyArrayObject*)PyArray_SimpleNew(1, ((npy_intp[]){2}), NPY_FLOAT64);
264  if(xy_c == NULL)
265  {
266  PyErr_SetString(PyExc_RuntimeError, "Could not allocate xy_c array");
267  goto done;
268  }
269  xy_lb_rb_rt_lt = (PyArrayObject*)PyArray_SimpleNew(2, ((npy_intp[]){4,2}), NPY_FLOAT64);
270  if(xy_lb_rb_rt_lt == NULL)
271  {
272  PyErr_SetString(PyExc_RuntimeError, "Could not allocate xy_lb_rb_rt_lt array");
273  goto done;
274  }
275 
277  zarray_get(detections, i, &det);
278 
279  *(double*)PyArray_GETPTR1(xy_c, 0) = det->c[0];
280  *(double*)PyArray_GETPTR1(xy_c, 1) = det->c[1];
281 
282  for(int j=0; j<4; j++)
283  {
284  *(double*)PyArray_GETPTR2(xy_lb_rb_rt_lt, j, 0) = det->p[j][0];
285  *(double*)PyArray_GETPTR2(xy_lb_rb_rt_lt, j, 1) = det->p[j][1];
286  }
287 
288  PyTuple_SET_ITEM(detections_tuple, i,
289  Py_BuildValue("{s:i,s:f,s:i,s:N,s:N}",
290  "hamming", det->hamming,
291  "margin", det->decision_margin,
292  "id", det->id,
293  "center", xy_c,
294  "lb-rb-rt-lt", xy_lb_rb_rt_lt));
295  xy_c = NULL;
296  xy_lb_rb_rt_lt = NULL;
297  }
298  apriltag_detections_destroy(detections);
299 
300  result = detections_tuple;
301  detections_tuple = NULL;
302 
303  done:
304  Py_XDECREF(xy_c);
305  Py_XDECREF(xy_lb_rb_rt_lt);
306  Py_XDECREF(image);
307  Py_XDECREF(detections_tuple);
308 
309 #ifdef _POSIX_C_SOURCE
310  RESET_SIGINT();
311 #endif
312  return result;
313 }
314 
315 
316 #include "apriltag_detect_docstring.h"
317 #include "apriltag_py_type_docstring.h"
318 
319 static PyMethodDef apriltag_methods[] =
320  { PYMETHODDEF_ENTRY(apriltag_, detect, METH_VARARGS),
321  {NULL, NULL, 0, NULL}
322  };
323 
324 static PyTypeObject apriltagType =
325 {
326  PyVarObject_HEAD_INIT(NULL, 0)
327  .tp_name = "apriltag",
328  .tp_basicsize = sizeof(apriltag_py_t),
329  .tp_new = apriltag_new,
330  .tp_dealloc = (destructor)apriltag_dealloc,
331  .tp_methods = apriltag_methods,
332  .tp_flags = Py_TPFLAGS_DEFAULT,
333  .tp_doc = apriltag_py_type_docstring
334 };
335 
336 static PyMethodDef methods[] =
337  { {NULL, NULL, 0, NULL}
338  };
339 
340 
341 #if PY_MAJOR_VERSION == 2
342 
343 PyMODINIT_FUNC initapriltag(void)
344 {
345  if (PyType_Ready(&apriltagType) < 0)
346  return;
347 
348  PyObject* module = Py_InitModule3("apriltag", methods,
349  "AprilTags visual fiducial system detector");
350 
351  Py_INCREF(&apriltagType);
352  PyModule_AddObject(module, "apriltag", (PyObject *)&apriltagType);
353 
354  import_array();
355 }
356 
357 #else
358 
359 static struct PyModuleDef module_def =
360  {
361  PyModuleDef_HEAD_INIT,
362  "apriltag",
363  "AprilTags visual fiducial system detector",
364  -1,
365  methods,
366  0,
367  0,
368  0,
369  0
370  };
371 
372 PyMODINIT_FUNC PyInit_apriltag(void)
373 {
374  if (PyType_Ready(&apriltagType) < 0)
375  return NULL;
376 
377  PyObject* module =
378  PyModule_Create(&module_def);
379 
380  Py_INCREF(&apriltagType);
381  PyModule_AddObject(module, "apriltag", (PyObject *)&apriltagType);
382 
383  import_array();
384 
385  return module;
386 }
387 
388 #endif
RESET_SIGINT
#define RESET_SIGINT()
Definition: apriltag_pywrap.c:55
methods
static PyMethodDef methods[]
Definition: apriltag_pywrap.c:336
apriltag_detection::decision_margin
float decision_margin
Definition: apriltag.h:217
apriltag_detector_detect
zarray_t * apriltag_detector_detect(apriltag_detector_t *td, image_u8_t *im_orig)
Definition: apriltag.c:1029
PYMETHODDEF_ENTRY
#define PYMETHODDEF_ENTRY(function_prefix, name, args)
Definition: apriltag_pywrap.c:61
zarray
Definition: zarray.h:43
apriltagType
static PyTypeObject apriltagType
Definition: apriltag_pywrap.c:324
TAG_CREATE_FAMILY
#define TAG_CREATE_FAMILY(name)
Definition: apriltag_pywrap.c:32
zarray_size
static int zarray_size(const zarray_t *za)
Definition: zarray.h:130
apriltag.h
apriltag_detection::hamming
int hamming
Definition: apriltag.h:208
FAMILY_STRING
#define FAMILY_STRING(name)
Definition: apriltag_pywrap.c:36
image_u8::width
const int32_t width
Definition: image_types.h:39
apriltag_detector_create
apriltag_detector_t * apriltag_detector_create()
Definition: apriltag.c:354
tagCircle21h7.h
image_u8
Definition: image_types.h:37
apriltag_methods
static PyMethodDef apriltag_methods[]
Definition: apriltag_pywrap.c:319
apriltag_detections_destroy
void apriltag_detections_destroy(zarray_t *detections)
Definition: apriltag.c:1436
tag16h5.h
apriltag_detector
Definition: apriltag.h:127
SUPPORTED_TAG_FAMILIES
#define SUPPORTED_TAG_FAMILIES(_)
Definition: apriltag_pywrap.c:21
apriltag_family
Definition: apriltag.h:61
tagStandard52h13.h
refine_edges
static void refine_edges(apriltag_detector_t *td, image_u8_t *im_orig, struct quad *quad)
Definition: apriltag.c:743
apriltag_py_t::tf
PyObject_HEAD apriltag_family_t * tf
Definition: apriltag_pywrap.c:69
apriltag_detection::p
double p[4][2]
Definition: apriltag.h:230
tagCustom48h12.h
tag36h11.h
apriltag_detection::id
int id
Definition: apriltag.h:202
SET_SIGINT
#define SET_SIGINT()
Definition: apriltag_pywrap.c:45
tag36h10.h
tagCircle49h12.h
apriltag_detector_add_family_bits
void apriltag_detector_add_family_bits(apriltag_detector_t *td, apriltag_family_t *fam, int bits_corrected)
Definition: apriltag.c:336
apriltag_detector_destroy
void apriltag_detector_destroy(apriltag_detector_t *td)
Definition: apriltag.c:388
TAG_SET_DESTROY_FUNC
#define TAG_SET_DESTROY_FUNC(name)
Definition: apriltag_pywrap.c:34
apriltag_dealloc
static void apriltag_dealloc(apriltag_py_t *self)
Definition: apriltag_pywrap.c:184
apriltag_detect
static PyObject * apriltag_detect(apriltag_py_t *self, PyObject *args)
Definition: apriltag_pywrap.c:202
module_def
static struct PyModuleDef module_def
Definition: apriltag_pywrap.c:359
zarray_get
static void zarray_get(const zarray_t *za, int idx, void *p)
Definition: zarray.h:195
apriltag_detection
Definition: apriltag.h:196
apriltag_py_t::td
apriltag_detector_t * td
Definition: apriltag_pywrap.c:70
apriltag_new
static PyObject * apriltag_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
Definition: apriltag_pywrap.c:76
apriltag_py_t
Definition: apriltag_pywrap.c:66
PyInit_apriltag
PyMODINIT_FUNC PyInit_apriltag(void)
Definition: apriltag_pywrap.c:372
apriltag_detection::c
double c[2]
Definition: apriltag.h:226
tag25h9.h
tagStandard41h12.h


apriltag
Author(s): Edwin Olson , Max Krogius
autogenerated on Sun Apr 20 2025 02:08:19