apriltag_pose.c
Go to the documentation of this file.
1 #include <math.h>
2 #include <stdio.h>
3 
4 #include "common/debug_print.h"
5 #include "apriltag_pose.h"
6 #include "common/homography.h"
7 
8 
13  matd_t* outer_product = matd_op("MM'", v, v, v, v);
14  matd_t* inner_product = matd_op("M'M", v, v);
15  matd_scale_inplace(outer_product, 1.0/inner_product->data[0]);
16  matd_destroy(inner_product);
17  return outer_product;
18 }
19 
24 {
25  assert(matd_is_scalar(a));
26  double d = a->data[0];
27  matd_destroy(a);
28  return d;
29 }
30 
43 double orthogonal_iteration(matd_t** v, matd_t** p, matd_t** t, matd_t** R, int n_points, int n_steps) {
44  matd_t* p_mean = matd_create(3, 1);
45  for (int i = 0; i < n_points; i++) {
46  matd_add_inplace(p_mean, p[i]);
47  }
48  matd_scale_inplace(p_mean, 1.0/n_points);
49 
50  matd_t** p_res = malloc(sizeof(matd_t *)*n_points);
51  for (int i = 0; i < n_points; i++) {
52  p_res[i] = matd_op("M-M", p[i], p_mean);
53  }
54 
55  // Compute M1_inv.
56  matd_t** F = malloc(sizeof(matd_t *)*n_points);
57  matd_t *avg_F = matd_create(3, 3);
58  for (int i = 0; i < n_points; i++) {
59  F[i] = calculate_F(v[i]);
60  matd_add_inplace(avg_F, F[i]);
61  }
62  matd_scale_inplace(avg_F, 1.0/n_points);
63  matd_t *I3 = matd_identity(3);
64  matd_t *M1 = matd_subtract(I3, avg_F);
65  matd_t *M1_inv = matd_inverse(M1);
66  matd_destroy(avg_F);
67  matd_destroy(M1);
68 
69  double prev_error = HUGE_VAL;
70  // Iterate.
71  for (int i = 0; i < n_steps; i++) {
72  // Calculate translation.
73  matd_t *M2 = matd_create(3, 1);
74  for (int j = 0; j < n_points; j++) {
75  matd_t* M2_update = matd_op("(M - M)*M*M", F[j], I3, *R, p[j]);
76  matd_add_inplace(M2, M2_update);
77  matd_destroy(M2_update);
78  }
79  matd_scale_inplace(M2, 1.0/n_points);
80  matd_destroy(*t);
81  *t = matd_multiply(M1_inv, M2);
82  matd_destroy(M2);
83 
84  // Calculate rotation.
85  matd_t** q = malloc(sizeof(matd_t *)*n_points);
86  matd_t* q_mean = matd_create(3, 1);
87  for (int j = 0; j < n_points; j++) {
88  q[j] = matd_op("M*(M*M+M)", F[j], *R, p[j], *t);
89  matd_add_inplace(q_mean, q[j]);
90  }
91  matd_scale_inplace(q_mean, 1.0/n_points);
92 
93  matd_t* M3 = matd_create(3, 3);
94  for (int j = 0; j < n_points; j++) {
95  matd_t *M3_update = matd_op("(M-M)*M'", q[j], q_mean, p_res[j]);
96  matd_add_inplace(M3, M3_update);
97  matd_destroy(M3_update);
98  }
99  matd_svd_t M3_svd = matd_svd(M3);
100  matd_destroy(M3);
101  matd_destroy(*R);
102  *R = matd_op("M*M'", M3_svd.U, M3_svd.V);
103  double R_det = matd_det(*R);
104  if (R_det < 0) {
105  matd_put(*R, 0, 2, - matd_get(*R, 0, 2));
106  matd_put(*R, 1, 2, - matd_get(*R, 1, 2));
107  matd_put(*R, 2, 2, - matd_get(*R, 2, 2));
108  }
109  matd_destroy(M3_svd.U);
110  matd_destroy(M3_svd.S);
111  matd_destroy(M3_svd.V);
112  matd_destroy(q_mean);
113  for (int j = 0; j < n_points; j++) {
114  matd_destroy(q[j]);
115  }
116 
117  double error = 0;
118  for (int j = 0; j < 4; j++) {
119  matd_t* err_vec = matd_op("(M-M)(MM+M)", I3, F[j], *R, p[j], *t);
120  error += matd_to_double(matd_op("M'M", err_vec, err_vec));
121  matd_destroy(err_vec);
122  }
123  prev_error = error;
124 
125  free(q);
126  }
127 
128  matd_destroy(I3);
129  matd_destroy(M1_inv);
130  for (int i = 0; i < n_points; i++) {
131  matd_destroy(p_res[i]);
132  matd_destroy(F[i]);
133  }
134  free(p_res);
135  free(F);
136  matd_destroy(p_mean);
137  return prev_error;
138 }
139 
143 double polyval(double* p, int degree, double x) {
144  double ret = 0;
145  for (int i = 0; i <= degree; i++) {
146  ret += p[i]*pow(x, i);
147  }
148  return ret;
149 }
150 
160 void solve_poly_approx(double* p, int degree, double* roots, int* n_roots) {
161  static const int MAX_ROOT = 1000;
162  if (degree == 1) {
163  if (fabs(p[0]) > MAX_ROOT*fabs(p[1])) {
164  *n_roots = 0;
165  } else {
166  roots[0] = -p[0]/p[1];
167  *n_roots = 1;
168  }
169  return;
170  }
171 
172  // Calculate roots of derivative.
173  double *p_der = malloc(sizeof(double)*degree);
174  for (int i = 0; i < degree; i++) {
175  p_der[i] = (i + 1) * p[i+1];
176  }
177 
178  double *der_roots = malloc(sizeof(double)*(degree - 1));
179  int n_der_roots;
180  solve_poly_approx(p_der, degree - 1, der_roots, &n_der_roots);
181 
182 
183  // Go through all possibilities for roots of the polynomial.
184  *n_roots = 0;
185  for (int i = 0; i <= n_der_roots; i++) {
186  double min;
187  if (i == 0) {
188  min = -MAX_ROOT;
189  } else {
190  min = der_roots[i - 1];
191  }
192 
193  double max;
194  if (i == n_der_roots) {
195  max = MAX_ROOT;
196  } else {
197  max = der_roots[i];
198  }
199 
200  if (polyval(p, degree, min)*polyval(p, degree, max) < 0) {
201  // We have a zero-crossing in this interval, use a combination of Newton' and bisection.
202  // Some thanks to Numerical Recipes in C.
203 
204  double lower;
205  double upper;
206  if (polyval(p, degree, min) < polyval(p, degree, max)) {
207  lower = min;
208  upper = max;
209  } else {
210  lower = max;
211  upper = min;
212  }
213  double root = 0.5*(lower + upper);
214  double dx_old = upper - lower;
215  double dx = dx_old;
216  double f = polyval(p, degree, root);
217  double df = polyval(p_der, degree - 1, root);
218 
219  for (int j = 0; j < 100; j++) {
220  if (((f + df*(upper - root))*(f + df*(lower - root)) > 0)
221  || (fabs(2*f) > fabs(dx_old*df))) {
222  dx_old = dx;
223  dx = 0.5*(upper - lower);
224  root = lower + dx;
225  } else {
226  dx_old = dx;
227  dx = -f/df;
228  root += dx;
229  }
230 
231  if (root == upper || root == lower) {
232  break;
233  }
234 
235  f = polyval(p, degree, root);
236  df = polyval(p_der, degree - 1, root);
237 
238  if (f > 0) {
239  upper = root;
240  } else {
241  lower = root;
242  }
243  }
244 
245  roots[(*n_roots)++] = root;
246  } else if(polyval(p, degree, max) == 0) {
247  // Double/triple root.
248  roots[(*n_roots)++] = max;
249  }
250  }
251 
252  free(der_roots);
253  free(p_der);
254 }
255 
259 matd_t* fix_pose_ambiguities(matd_t** v, matd_t** p, matd_t* t, matd_t* R, int n_points) {
260  matd_t* I3 = matd_identity(3);
261 
262  // 1. Find R_t
263  matd_t* R_t_3 = matd_vec_normalize(t);
264 
265  matd_t* e_x = matd_create(3, 1);
266  MATD_EL(e_x, 0, 0) = 1;
267  matd_t* R_t_1_tmp = matd_op("M-(M'*M)*M", e_x, e_x, R_t_3, R_t_3);
268  matd_t* R_t_1 = matd_vec_normalize(R_t_1_tmp);
269  matd_destroy(e_x);
270  matd_destroy(R_t_1_tmp);
271 
272  matd_t* R_t_2 = matd_crossproduct(R_t_3, R_t_1);
273 
274  matd_t* R_t = matd_create_data(3, 3, (double[]) {
275  MATD_EL(R_t_1, 0, 0), MATD_EL(R_t_1, 0, 1), MATD_EL(R_t_1, 0, 2),
276  MATD_EL(R_t_2, 0, 0), MATD_EL(R_t_2, 0, 1), MATD_EL(R_t_2, 0, 2),
277  MATD_EL(R_t_3, 0, 0), MATD_EL(R_t_3, 0, 1), MATD_EL(R_t_3, 0, 2)});
278  matd_destroy(R_t_1);
279  matd_destroy(R_t_2);
280  matd_destroy(R_t_3);
281 
282  // 2. Find R_z
283  matd_t* R_1_prime = matd_multiply(R_t, R);
284  double r31 = MATD_EL(R_1_prime, 2, 0);
285  double r32 = MATD_EL(R_1_prime, 2, 1);
286  double hypotenuse = sqrt(r31*r31 + r32*r32);
287  if (hypotenuse < 1e-100) {
288  r31 = 1;
289  r32 = 0;
290  hypotenuse = 1;
291  }
292  matd_t* R_z = matd_create_data(3, 3, (double[]) {
293  r31/hypotenuse, -r32/hypotenuse, 0,
294  r32/hypotenuse, r31/hypotenuse, 0,
295  0, 0, 1});
296 
297  // 3. Calculate parameters of Eos
298  matd_t* R_trans = matd_multiply(R_1_prime, R_z);
299  double sin_gamma = -MATD_EL(R_trans, 0, 1);
300  double cos_gamma = MATD_EL(R_trans, 1, 1);
301  matd_t* R_gamma = matd_create_data(3, 3, (double[]) {
302  cos_gamma, -sin_gamma, 0,
303  sin_gamma, cos_gamma, 0,
304  0, 0, 1});
305 
306  double sin_beta = -MATD_EL(R_trans, 2, 0);
307  double cos_beta = MATD_EL(R_trans, 2, 2);
308  double t_initial = atan2(sin_beta, cos_beta);
309  matd_destroy(R_trans);
310 
311  matd_t** v_trans = malloc(sizeof(matd_t *)*n_points);
312  matd_t** p_trans = malloc(sizeof(matd_t *)*n_points);
313  matd_t** F_trans = malloc(sizeof(matd_t *)*n_points);
314  matd_t* avg_F_trans = matd_create(3, 3);
315  for (int i = 0; i < n_points; i++) {
316  p_trans[i] = matd_op("M'*M", R_z, p[i]);
317  v_trans[i] = matd_op("M*M", R_t, v[i]);
318  F_trans[i] = calculate_F(v_trans[i]);
319  matd_add_inplace(avg_F_trans, F_trans[i]);
320  }
321  matd_scale_inplace(avg_F_trans, 1.0/n_points);
322 
323  matd_t* G = matd_op("(M-M)^-1", I3, avg_F_trans);
324  matd_scale_inplace(G, 1.0/n_points);
325 
326  matd_t* M1 = matd_create_data(3, 3, (double[]) {
327  0, 0, 2,
328  0, 0, 0,
329  -2, 0, 0});
330  matd_t* M2 = matd_create_data(3, 3, (double[]) {
331  -1, 0, 0,
332  0, 1, 0,
333  0, 0, -1});
334 
335  matd_t* b0 = matd_create(3, 1);
336  matd_t* b1 = matd_create(3, 1);
337  matd_t* b2 = matd_create(3, 1);
338  for (int i = 0; i < n_points; i++) {
339  matd_t* op_tmp1 = matd_op("(M-M)MM", F_trans[i], I3, R_gamma, p_trans[i]);
340  matd_t* op_tmp2 = matd_op("(M-M)MMM", F_trans[i], I3, R_gamma, M1, p_trans[i]);
341  matd_t* op_tmp3 = matd_op("(M-M)MMM", F_trans[i], I3, R_gamma, M2, p_trans[i]);
342 
343  matd_add_inplace(b0, op_tmp1);
344  matd_add_inplace(b1, op_tmp2);
345  matd_add_inplace(b2, op_tmp3);
346 
347  matd_destroy(op_tmp1);
348  matd_destroy(op_tmp2);
349  matd_destroy(op_tmp3);
350  }
351  matd_t* b0_ = matd_multiply(G, b0);
352  matd_t* b1_ = matd_multiply(G, b1);
353  matd_t* b2_ = matd_multiply(G, b2);
354 
355  double a0 = 0;
356  double a1 = 0;
357  double a2 = 0;
358  double a3 = 0;
359  double a4 = 0;
360  for (int i = 0; i < n_points; i++) {
361  matd_t* c0 = matd_op("(M-M)(MM+M)", I3, F_trans[i], R_gamma, p_trans[i], b0_);
362  matd_t* c1 = matd_op("(M-M)(MMM+M)", I3, F_trans[i], R_gamma, M1, p_trans[i], b1_);
363  matd_t* c2 = matd_op("(M-M)(MMM+M)", I3, F_trans[i], R_gamma, M2, p_trans[i], b2_);
364 
365  a0 += matd_to_double(matd_op("M'M", c0, c0));
366  a1 += matd_to_double(matd_op("2M'M", c0, c1));
367  a2 += matd_to_double(matd_op("M'M+2M'M", c1, c1, c0, c2));
368  a3 += matd_to_double(matd_op("2M'M", c1, c2));
369  a4 += matd_to_double(matd_op("M'M", c2, c2));
370 
371  matd_destroy(c0);
372  matd_destroy(c1);
373  matd_destroy(c2);
374  }
375 
376  matd_destroy(b0);
377  matd_destroy(b1);
378  matd_destroy(b2);
379  matd_destroy(b0_);
380  matd_destroy(b1_);
381  matd_destroy(b2_);
382 
383  for (int i = 0; i < n_points; i++) {
384  matd_destroy(p_trans[i]);
385  matd_destroy(v_trans[i]);
386  matd_destroy(F_trans[i]);
387  }
388  free(p_trans);
389  free(v_trans);
390  free(F_trans);
391  matd_destroy(avg_F_trans);
392  matd_destroy(G);
393 
394 
395  // 4. Solve for minima of Eos.
396  double p0 = a1;
397  double p1 = 2*a2 - 4*a0;
398  double p2 = 3*a3 - 3*a1;
399  double p3 = 4*a4 - 2*a2;
400  double p4 = -a3;
401 
402  double roots[4];
403  int n_roots;
404  solve_poly_approx((double []) {p0, p1, p2, p3, p4}, 4, roots, &n_roots);
405 
406  double minima[4];
407  int n_minima = 0;
408  for (int i = 0; i < n_roots; i++) {
409  double t1 = roots[i];
410  double t2 = t1*t1;
411  double t3 = t1*t2;
412  double t4 = t1*t3;
413  double t5 = t1*t4;
414  // Check extrema is a minima.
415  if (a2 - 2*a0 + (3*a3 - 6*a1)*t1 + (6*a4 - 8*a2 + 10*a0)*t2 + (-8*a3 + 6*a1)*t3 + (-6*a4 + 3*a2)*t4 + a3*t5 >= 0) {
416  // And that it corresponds to an angle different than the known minimum.
417  double t_cur = 2*atan(roots[i]);
418  // We only care about finding a second local minima which is qualitatively
419  // different than the first.
420  if (fabs(t_cur - t_initial) > 0.1) {
421  minima[n_minima++] = roots[i];
422  }
423  }
424  }
425 
426  // 5. Get poses for minima.
427  matd_t* ret = NULL;
428  if (n_minima == 1) {
429  double t_cur = minima[0];
430  matd_t* R_beta = matd_copy(M2);
431  matd_scale_inplace(R_beta, t_cur);
432  matd_add_inplace(R_beta, M1);
433  matd_scale_inplace(R_beta, t_cur);
434  matd_add_inplace(R_beta, I3);
435  matd_scale_inplace(R_beta, 1/(1 + t_cur*t_cur));
436  ret = matd_op("M'MMM'", R_t, R_gamma, R_beta, R_z);
437  matd_destroy(R_beta);
438  } else if (n_minima > 1) {
439  // This can happen if our prior pose estimate was not very good.
440  debug_print("Error, more than one new minimum found.\n");
441  }
442  matd_destroy(I3);
443  matd_destroy(M1);
444  matd_destroy(M2);
445  matd_destroy(R_t);
446  matd_destroy(R_gamma);
447  matd_destroy(R_z);
448  matd_destroy(R_1_prime);
449  return ret;
450 }
451 
456  double scale = info->tagsize/2.0;
457 
458  matd_t *M_H = homography_to_pose(info->det->H, -info->fx, info->fy, info->cx, info->cy);
459  MATD_EL(M_H, 0, 3) *= scale;
460  MATD_EL(M_H, 1, 3) *= scale;
461  MATD_EL(M_H, 2, 3) *= scale;
462 
463  matd_t* fix = matd_create(4, 4);
464  MATD_EL(fix, 0, 0) = 1;
465  MATD_EL(fix, 1, 1) = -1;
466  MATD_EL(fix, 2, 2) = -1;
467  MATD_EL(fix, 3, 3) = 1;
468 
469  matd_t* initial_pose = matd_multiply(fix, M_H);
470  matd_destroy(M_H);
471  matd_destroy(fix);
472 
473  solution->R = matd_create(3, 3);
474  for (int i = 0; i < 3; i++) {
475  for (int j = 0; j < 3; j++) {
476  MATD_EL(solution->R, i, j) = MATD_EL(initial_pose, i, j);
477  }
478  }
479 
480  solution->t = matd_create(3, 1);
481  for (int i = 0; i < 3; i++) {
482  MATD_EL(solution->t, i, 0) = MATD_EL(initial_pose, i, 3);
483  }
484  matd_destroy(initial_pose);
485 }
486 
492  double* err1,
493  apriltag_pose_t* solution1,
494  double* err2,
495  apriltag_pose_t* solution2,
496  int nIters) {
497  double scale = info->tagsize/2.0;
498  matd_t* p[4] = {
499  matd_create_data(3, 1, (double[]) {-scale, scale, 0}),
500  matd_create_data(3, 1, (double[]) {scale, scale, 0}),
501  matd_create_data(3, 1, (double[]) {scale, -scale, 0}),
502  matd_create_data(3, 1, (double[]) {-scale, -scale, 0})};
503  matd_t* v[4];
504  for (int i = 0; i < 4; i++) {
505  v[i] = matd_create_data(3, 1, (double[]) {
506  (info->det->p[i][0] - info->cx)/info->fx, (info->det->p[i][1] - info->cy)/info->fy, 1});
507  }
508 
509  estimate_pose_for_tag_homography(info, solution1);
510  *err1 = orthogonal_iteration(v, p, &solution1->t, &solution1->R, 4, nIters);
511  solution2->R = fix_pose_ambiguities(v, p, solution1->t, solution1->R, 4);
512  if (solution2->R) {
513  solution2->t = matd_create(3, 1);
514  *err2 = orthogonal_iteration(v, p, &solution2->t, &solution2->R, 4, nIters);
515  } else {
516  *err2 = HUGE_VAL;
517  }
518 
519  for (int i = 0; i < 4; i++) {
520  matd_destroy(p[i]);
521  matd_destroy(v[i]);
522  }
523 }
524 
529  double err1, err2;
530  apriltag_pose_t pose1, pose2;
531  estimate_tag_pose_orthogonal_iteration(info, &err1, &pose1, &err2, &pose2, 50);
532  if (err1 <= err2) {
533  pose->R = pose1.R;
534  pose->t = pose1.t;
535  if (pose2.R) {
536  matd_destroy(pose2.t);
537  }
538  matd_destroy(pose2.R);
539  return err1;
540  } else {
541  pose->R = pose2.R;
542  pose->t = pose2.t;
543  matd_destroy(pose1.R);
544  matd_destroy(pose1.t);
545  return err2;
546  }
547 }
matd_t
Definition: matd.h:45
matd_svd
matd_svd_t matd_svd(matd_t *A)
Definition: matd.c:1461
solve_poly_approx
void solve_poly_approx(double *p, int degree, double *roots, int *n_roots)
Definition: apriltag_pose.c:160
matd_create
matd_t * matd_create(int rows, int cols)
Definition: matd.c:46
fix_pose_ambiguities
matd_t * fix_pose_ambiguities(matd_t **v, matd_t **p, matd_t *t, matd_t *R, int n_points)
Definition: apriltag_pose.c:259
matd_put
void matd_put(matd_t *m, unsigned int row, unsigned int col, TYPE value)
Definition: matd.c:121
matd_subtract
matd_t * matd_subtract(const matd_t *a, const matd_t *b)
Definition: matd.c:331
homography_to_pose
matd_t * homography_to_pose(const matd_t *H, double fx, double fy, double cx, double cy)
Definition: homography.c:275
apriltag_detection_info_t::det
apriltag_detection_t * det
Definition: apriltag_pose.h:12
matd_is_scalar
static int matd_is_scalar(const matd_t *a)
Definition: matd.h:249
apriltag_detection::H
matd_t * H
Definition: apriltag.h:223
matd_vec_normalize
matd_t * matd_vec_normalize(const matd_t *a)
Definition: matd.c:924
matd_multiply
matd_t * matd_multiply(const matd_t *a, const matd_t *b)
Definition: matd.c:230
matd_svd_t::U
matd_t * U
Definition: matd.h:358
apriltag_pose_t
Definition: apriltag_pose.h:25
matd_crossproduct
matd_t * matd_crossproduct(const matd_t *a, const matd_t *b)
Definition: matd.c:941
calculate_F
matd_t * calculate_F(matd_t *v)
Definition: apriltag_pose.c:12
matd_destroy
void matd_destroy(matd_t *m)
Definition: matd.c:220
matd_det
double matd_det(const matd_t *a)
Definition: matd.c:420
matd_create_data
matd_t * matd_create_data(int rows, int cols, const TYPE *data)
Definition: matd.c:73
matd_t::data
double * data
Definition: matd.h:48
estimate_tag_pose_orthogonal_iteration
void estimate_tag_pose_orthogonal_iteration(apriltag_detection_info_t *info, double *err1, apriltag_pose_t *solution1, double *err2, apriltag_pose_t *solution2, int nIters)
Definition: apriltag_pose.c:490
matd_inverse
matd_t * matd_inverse(const matd_t *x)
Definition: matd.c:480
apriltag_pose_t::t
matd_t * t
Definition: apriltag_pose.h:27
estimate_pose_for_tag_homography
void estimate_pose_for_tag_homography(apriltag_detection_info_t *info, apriltag_pose_t *solution)
Definition: apriltag_pose.c:455
orthogonal_iteration
double orthogonal_iteration(matd_t **v, matd_t **p, matd_t **t, matd_t **R, int n_points, int n_steps)
Definition: apriltag_pose.c:43
matd_add_inplace
void matd_add_inplace(matd_t *a, const matd_t *b)
Definition: matd.c:311
apriltag_pose_t::R
matd_t * R
Definition: apriltag_pose.h:26
estimate_tag_pose
double estimate_tag_pose(apriltag_detection_info_t *info, apriltag_pose_t *pose)
Definition: apriltag_pose.c:528
MATD_EL
#define MATD_EL(m, row, col)
Definition: matd.h:64
apriltag_detection_info_t::fy
double fy
Definition: apriltag_pose.h:15
apriltag_detection_info_t
Definition: apriltag_pose.h:11
apriltag_detection::p
double p[4][2]
Definition: apriltag.h:230
polyval
double polyval(double *p, int degree, double x)
Definition: apriltag_pose.c:143
matd_copy
matd_t * matd_copy(const matd_t *m)
Definition: matd.c:152
matd_op
matd_t * matd_op(const char *expr,...)
Definition: matd.c:793
apriltag_detection_info_t::fx
double fx
Definition: apriltag_pose.h:14
matd_svd_t::V
matd_t * V
Definition: matd.h:360
debug_print
#define debug_print(fmt,...)
Definition: debug_print.h:39
apriltag_detection_info_t::cx
double cx
Definition: apriltag_pose.h:16
homography.h
matd_scale_inplace
void matd_scale_inplace(matd_t *a, double s)
Definition: matd.c:274
matd_svd_t
Definition: matd.h:356
apriltag_detection_info_t::cy
double cy
Definition: apriltag_pose.h:17
matd_get
TYPE matd_get(const matd_t *m, unsigned int row, unsigned int col)
Definition: matd.c:110
debug_print.h
matd_svd_t::S
matd_t * S
Definition: matd.h:359
matd_to_double
double matd_to_double(matd_t *a)
Definition: apriltag_pose.c:23
matd_identity
matd_t * matd_identity(int dim)
Definition: matd.c:97
apriltag_detection_info_t::tagsize
double tagsize
Definition: apriltag_pose.h:13
apriltag_pose.h


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