homography.c
Go to the documentation of this file.
1 /* Copyright (C) 2013-2016, The Regents of The University of Michigan.
2 All rights reserved.
3 
4 This software was developed in the APRIL Robotics Lab under the
5 direction of Edwin Olson, ebolson@umich.edu. This software may be
6 available under alternative licensing terms; contact the address above.
7 
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are met:
10 
11 1. Redistributions of source code must retain the above copyright notice, this
12  list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 The views and conclusions contained in the software and documentation are those
29 of the authors and should not be interpreted as representing official policies,
30 either expressed or implied, of the Regents of The University of Michigan.
31 */
32 
33 #include <math.h>
34 #include <stdio.h>
35 
36 #include "common/matd.h"
37 #include "common/zarray.h"
38 #include "common/homography.h"
39 #include "common/math_util.h"
40 
41 // correspondences is a list of float[4]s, consisting of the points x
42 // and y concatenated. We will compute a homography such that y = Hx
43 matd_t *homography_compute(zarray_t *correspondences, int flags)
44 {
45  // compute centroids of both sets of points (yields a better
46  // conditioned information matrix)
47  double x_cx = 0, x_cy = 0;
48  double y_cx = 0, y_cy = 0;
49 
50  for (int i = 0; i < zarray_size(correspondences); i++) {
51  float *c;
52  zarray_get_volatile(correspondences, i, &c);
53 
54  x_cx += c[0];
55  x_cy += c[1];
56  y_cx += c[2];
57  y_cy += c[3];
58  }
59 
60  int sz = zarray_size(correspondences);
61  x_cx /= sz;
62  x_cy /= sz;
63  y_cx /= sz;
64  y_cy /= sz;
65 
66  // NB We don't normalize scale; it seems implausible that it could
67  // possibly make any difference given the dynamic range of IEEE
68  // doubles.
69 
70  matd_t *A = matd_create(9,9);
71  for (int i = 0; i < zarray_size(correspondences); i++) {
72  float *c;
73  zarray_get_volatile(correspondences, i, &c);
74 
75  // (below world is "x", and image is "y")
76  double worldx = c[0] - x_cx;
77  double worldy = c[1] - x_cy;
78  double imagex = c[2] - y_cx;
79  double imagey = c[3] - y_cy;
80 
81  double a03 = -worldx;
82  double a04 = -worldy;
83  double a05 = -1;
84  double a06 = worldx*imagey;
85  double a07 = worldy*imagey;
86  double a08 = imagey;
87 
88  MATD_EL(A, 3, 3) += a03*a03;
89  MATD_EL(A, 3, 4) += a03*a04;
90  MATD_EL(A, 3, 5) += a03*a05;
91  MATD_EL(A, 3, 6) += a03*a06;
92  MATD_EL(A, 3, 7) += a03*a07;
93  MATD_EL(A, 3, 8) += a03*a08;
94  MATD_EL(A, 4, 4) += a04*a04;
95  MATD_EL(A, 4, 5) += a04*a05;
96  MATD_EL(A, 4, 6) += a04*a06;
97  MATD_EL(A, 4, 7) += a04*a07;
98  MATD_EL(A, 4, 8) += a04*a08;
99  MATD_EL(A, 5, 5) += a05*a05;
100  MATD_EL(A, 5, 6) += a05*a06;
101  MATD_EL(A, 5, 7) += a05*a07;
102  MATD_EL(A, 5, 8) += a05*a08;
103  MATD_EL(A, 6, 6) += a06*a06;
104  MATD_EL(A, 6, 7) += a06*a07;
105  MATD_EL(A, 6, 8) += a06*a08;
106  MATD_EL(A, 7, 7) += a07*a07;
107  MATD_EL(A, 7, 8) += a07*a08;
108  MATD_EL(A, 8, 8) += a08*a08;
109 
110  double a10 = worldx;
111  double a11 = worldy;
112  double a12 = 1;
113  double a16 = -worldx*imagex;
114  double a17 = -worldy*imagex;
115  double a18 = -imagex;
116 
117  MATD_EL(A, 0, 0) += a10*a10;
118  MATD_EL(A, 0, 1) += a10*a11;
119  MATD_EL(A, 0, 2) += a10*a12;
120  MATD_EL(A, 0, 6) += a10*a16;
121  MATD_EL(A, 0, 7) += a10*a17;
122  MATD_EL(A, 0, 8) += a10*a18;
123  MATD_EL(A, 1, 1) += a11*a11;
124  MATD_EL(A, 1, 2) += a11*a12;
125  MATD_EL(A, 1, 6) += a11*a16;
126  MATD_EL(A, 1, 7) += a11*a17;
127  MATD_EL(A, 1, 8) += a11*a18;
128  MATD_EL(A, 2, 2) += a12*a12;
129  MATD_EL(A, 2, 6) += a12*a16;
130  MATD_EL(A, 2, 7) += a12*a17;
131  MATD_EL(A, 2, 8) += a12*a18;
132  MATD_EL(A, 6, 6) += a16*a16;
133  MATD_EL(A, 6, 7) += a16*a17;
134  MATD_EL(A, 6, 8) += a16*a18;
135  MATD_EL(A, 7, 7) += a17*a17;
136  MATD_EL(A, 7, 8) += a17*a18;
137  MATD_EL(A, 8, 8) += a18*a18;
138 
139  double a20 = -worldx*imagey;
140  double a21 = -worldy*imagey;
141  double a22 = -imagey;
142  double a23 = worldx*imagex;
143  double a24 = worldy*imagex;
144  double a25 = imagex;
145 
146  MATD_EL(A, 0, 0) += a20*a20;
147  MATD_EL(A, 0, 1) += a20*a21;
148  MATD_EL(A, 0, 2) += a20*a22;
149  MATD_EL(A, 0, 3) += a20*a23;
150  MATD_EL(A, 0, 4) += a20*a24;
151  MATD_EL(A, 0, 5) += a20*a25;
152  MATD_EL(A, 1, 1) += a21*a21;
153  MATD_EL(A, 1, 2) += a21*a22;
154  MATD_EL(A, 1, 3) += a21*a23;
155  MATD_EL(A, 1, 4) += a21*a24;
156  MATD_EL(A, 1, 5) += a21*a25;
157  MATD_EL(A, 2, 2) += a22*a22;
158  MATD_EL(A, 2, 3) += a22*a23;
159  MATD_EL(A, 2, 4) += a22*a24;
160  MATD_EL(A, 2, 5) += a22*a25;
161  MATD_EL(A, 3, 3) += a23*a23;
162  MATD_EL(A, 3, 4) += a23*a24;
163  MATD_EL(A, 3, 5) += a23*a25;
164  MATD_EL(A, 4, 4) += a24*a24;
165  MATD_EL(A, 4, 5) += a24*a25;
166  MATD_EL(A, 5, 5) += a25*a25;
167  }
168 
169  // make symmetric
170  for (int i = 0; i < 9; i++)
171  for (int j = i+1; j < 9; j++)
172  MATD_EL(A, j, i) = MATD_EL(A, i, j);
173 
174  matd_t *H = matd_create(3,3);
175 
176  if (flags & HOMOGRAPHY_COMPUTE_FLAG_INVERSE) {
177  // compute singular vector by (carefully) inverting the rank-deficient matrix.
178 
179  if (1) {
180  matd_t *Ainv = matd_inverse(A);
181  double scale = 0;
182 
183  for (int i = 0; i < 9; i++)
184  scale += sq(MATD_EL(Ainv, i, 0));
185  scale = sqrt(scale);
186 
187  for (int i = 0; i < 3; i++)
188  for (int j = 0; j < 3; j++)
189  MATD_EL(H, i, j) = MATD_EL(Ainv, 3*i+j, 0) / scale;
190 
191  matd_destroy(Ainv);
192  } else {
193 
194  matd_t *b = matd_create_data(9, 1, (double[]) { 1, 0, 0, 0, 0, 0, 0, 0, 0 });
195  matd_t *Ainv = NULL;
196 
197  if (0) {
198  matd_plu_t *lu = matd_plu(A);
199  Ainv = matd_plu_solve(lu, b);
200  matd_plu_destroy(lu);
201  } else {
202  matd_chol_t *chol = matd_chol(A);
203  Ainv = matd_chol_solve(chol, b);
204  matd_chol_destroy(chol);
205  }
206 
207  double scale = 0;
208 
209  for (int i = 0; i < 9; i++)
210  scale += sq(MATD_EL(Ainv, i, 0));
211  scale = sqrt(scale);
212 
213  for (int i = 0; i < 3; i++)
214  for (int j = 0; j < 3; j++)
215  MATD_EL(H, i, j) = MATD_EL(Ainv, 3*i+j, 0) / scale;
216 
217  matd_destroy(b);
218  matd_destroy(Ainv);
219  }
220 
221  } else {
222  // compute singular vector using SVD. A bit slower, but more accurate.
224 
225  for (int i = 0; i < 3; i++)
226  for (int j = 0; j < 3; j++)
227  MATD_EL(H, i, j) = MATD_EL(svd.U, 3*i+j, 8);
228 
229  matd_destroy(svd.U);
230  matd_destroy(svd.S);
231  matd_destroy(svd.V);
232 
233  }
234 
235  matd_t *Tx = matd_identity(3);
236  MATD_EL(Tx,0,2) = -x_cx;
237  MATD_EL(Tx,1,2) = -x_cy;
238 
239  matd_t *Ty = matd_identity(3);
240  MATD_EL(Ty,0,2) = y_cx;
241  MATD_EL(Ty,1,2) = y_cy;
242 
243  matd_t *H2 = matd_op("M*M*M", Ty, H, Tx);
244 
245  matd_destroy(A);
246  matd_destroy(Tx);
247  matd_destroy(Ty);
248  matd_destroy(H);
249 
250  return H2;
251 }
252 
253 
254 // assuming that the projection matrix is:
255 // [ fx 0 cx 0 ]
256 // [ 0 fy cy 0 ]
257 // [ 0 0 1 0 ]
258 //
259 // And that the homography is equal to the projection matrix times the
260 // model matrix, recover the model matrix (which is returned). Note
261 // that the third column of the model matrix is missing in the
262 // expresison below, reflecting the fact that the homography assumes
263 // all points are at z=0 (i.e., planar) and that the element of z is
264 // thus omitted. (3x1 instead of 4x1).
265 //
266 // [ fx 0 cx 0 ] [ R00 R01 TX ] [ H00 H01 H02 ]
267 // [ 0 fy cy 0 ] [ R10 R11 TY ] = [ H10 H11 H12 ]
268 // [ 0 0 1 0 ] [ R20 R21 TZ ] = [ H20 H21 H22 ]
269 // [ 0 0 1 ]
270 //
271 // fx*R00 + cx*R20 = H00 (note, H only known up to scale; some additional adjustments required; see code.)
272 // fx*R01 + cx*R21 = H01
273 // fx*TX + cx*TZ = H02
274 // fy*R10 + cy*R20 = H10
275 // fy*R11 + cy*R21 = H11
276 // fy*TY + cy*TZ = H12
277 // R20 = H20
278 // R21 = H21
279 // TZ = H22
280 
281 matd_t *homography_to_pose(const matd_t *H, double fx, double fy, double cx, double cy)
282 {
283  // Note that every variable that we compute is proportional to the scale factor of H.
284  double R20 = MATD_EL(H, 2, 0);
285  double R21 = MATD_EL(H, 2, 1);
286  double TZ = MATD_EL(H, 2, 2);
287  double R00 = (MATD_EL(H, 0, 0) - cx*R20) / fx;
288  double R01 = (MATD_EL(H, 0, 1) - cx*R21) / fx;
289  double TX = (MATD_EL(H, 0, 2) - cx*TZ) / fx;
290  double R10 = (MATD_EL(H, 1, 0) - cy*R20) / fy;
291  double R11 = (MATD_EL(H, 1, 1) - cy*R21) / fy;
292  double TY = (MATD_EL(H, 1, 2) - cy*TZ) / fy;
293 
294  // compute the scale by requiring that the rotation columns are unit length
295  // (Use geometric average of the two length vectors we have)
296  double length1 = sqrtf(R00*R00 + R10*R10 + R20*R20);
297  double length2 = sqrtf(R01*R01 + R11*R11 + R21*R21);
298  double s = 1.0 / sqrtf(length1 * length2);
299 
300  // get sign of S by requiring the tag to be in front the camera;
301  // we assume camera looks in the -Z direction.
302  if (TZ > 0)
303  s *= -1;
304 
305  R20 *= s;
306  R21 *= s;
307  TZ *= s;
308  R00 *= s;
309  R01 *= s;
310  TX *= s;
311  R10 *= s;
312  R11 *= s;
313  TY *= s;
314 
315  // now recover [R02 R12 R22] by noting that it is the cross product of the other two columns.
316  double R02 = R10*R21 - R20*R11;
317  double R12 = R20*R01 - R00*R21;
318  double R22 = R00*R11 - R10*R01;
319 
320  // Improve rotation matrix by applying polar decomposition.
321  if (1) {
322  // do polar decomposition. This makes the rotation matrix
323  // "proper", but probably increases the reprojection error. An
324  // iterative alignment step would be superior.
325 
326  matd_t *R = matd_create_data(3, 3, (double[]) { R00, R01, R02,
327  R10, R11, R12,
328  R20, R21, R22 });
329 
330  matd_svd_t svd = matd_svd(R);
331  matd_destroy(R);
332 
333  R = matd_op("M*M'", svd.U, svd.V);
334 
335  matd_destroy(svd.U);
336  matd_destroy(svd.S);
337  matd_destroy(svd.V);
338 
339  R00 = MATD_EL(R, 0, 0);
340  R01 = MATD_EL(R, 0, 1);
341  R02 = MATD_EL(R, 0, 2);
342  R10 = MATD_EL(R, 1, 0);
343  R11 = MATD_EL(R, 1, 1);
344  R12 = MATD_EL(R, 1, 2);
345  R20 = MATD_EL(R, 2, 0);
346  R21 = MATD_EL(R, 2, 1);
347  R22 = MATD_EL(R, 2, 2);
348 
349  matd_destroy(R);
350  }
351 
352  return matd_create_data(4, 4, (double[]) { R00, R01, R02, TX,
353  R10, R11, R12, TY,
354  R20, R21, R22, TZ,
355  0, 0, 0, 1 });
356 }
357 
358 // Similar to above
359 // Recover the model view matrix assuming that the projection matrix is:
360 //
361 // [ F 0 A 0 ] (see glFrustrum)
362 // [ 0 G B 0 ]
363 // [ 0 0 C D ]
364 // [ 0 0 -1 0 ]
365 
366 matd_t *homography_to_model_view(const matd_t *H, double F, double G, double A, double B, double C, double D)
367 {
368  // Note that every variable that we compute is proportional to the scale factor of H.
369  double R20 = -MATD_EL(H, 2, 0);
370  double R21 = -MATD_EL(H, 2, 1);
371  double TZ = -MATD_EL(H, 2, 2);
372  double R00 = (MATD_EL(H, 0, 0) - A*R20) / F;
373  double R01 = (MATD_EL(H, 0, 1) - A*R21) / F;
374  double TX = (MATD_EL(H, 0, 2) - A*TZ) / F;
375  double R10 = (MATD_EL(H, 1, 0) - B*R20) / G;
376  double R11 = (MATD_EL(H, 1, 1) - B*R21) / G;
377  double TY = (MATD_EL(H, 1, 2) - B*TZ) / G;
378 
379  // compute the scale by requiring that the rotation columns are unit length
380  // (Use geometric average of the two length vectors we have)
381  double length1 = sqrtf(R00*R00 + R10*R10 + R20*R20);
382  double length2 = sqrtf(R01*R01 + R11*R11 + R21*R21);
383  double s = 1.0 / sqrtf(length1 * length2);
384 
385  // get sign of S by requiring the tag to be in front of the camera
386  // (which is Z < 0) for our conventions.
387  if (TZ > 0)
388  s *= -1;
389 
390  R20 *= s;
391  R21 *= s;
392  TZ *= s;
393  R00 *= s;
394  R01 *= s;
395  TX *= s;
396  R10 *= s;
397  R11 *= s;
398  TY *= s;
399 
400  // now recover [R02 R12 R22] by noting that it is the cross product of the other two columns.
401  double R02 = R10*R21 - R20*R11;
402  double R12 = R20*R01 - R00*R21;
403  double R22 = R00*R11 - R10*R01;
404 
405  // TODO XXX: Improve rotation matrix by applying polar decomposition.
406 
407  return matd_create_data(4, 4, (double[]) { R00, R01, R02, TX,
408  R10, R11, R12, TY,
409  R20, R21, R22, TZ,
410  0, 0, 0, 1 });
411 }
412 
413 // Only uses the upper 3x3 matrix.
414 /*
415 static void matrix_to_quat(const matd_t *R, double q[4])
416 {
417  // see: "from quaternion to matrix and back"
418 
419  // trace: get the same result if R is 4x4 or 3x3:
420  double T = MATD_EL(R, 0, 0) + MATD_EL(R, 1, 1) + MATD_EL(R, 2, 2) + 1;
421  double S = 0;
422 
423  double m0 = MATD_EL(R, 0, 0);
424  double m1 = MATD_EL(R, 1, 0);
425  double m2 = MATD_EL(R, 2, 0);
426  double m4 = MATD_EL(R, 0, 1);
427  double m5 = MATD_EL(R, 1, 1);
428  double m6 = MATD_EL(R, 2, 1);
429  double m8 = MATD_EL(R, 0, 2);
430  double m9 = MATD_EL(R, 1, 2);
431  double m10 = MATD_EL(R, 2, 2);
432 
433  if (T > 0.0000001) {
434  S = sqrtf(T) * 2;
435  q[1] = -( m9 - m6 ) / S;
436  q[2] = -( m2 - m8 ) / S;
437  q[3] = -( m4 - m1 ) / S;
438  q[0] = 0.25 * S;
439  } else if ( m0 > m5 && m0 > m10 ) { // Column 0:
440  S = sqrtf( 1.0 + m0 - m5 - m10 ) * 2;
441  q[1] = -0.25 * S;
442  q[2] = -(m4 + m1 ) / S;
443  q[3] = -(m2 + m8 ) / S;
444  q[0] = (m9 - m6 ) / S;
445  } else if ( m5 > m10 ) { // Column 1:
446  S = sqrtf( 1.0 + m5 - m0 - m10 ) * 2;
447  q[1] = -(m4 + m1 ) / S;
448  q[2] = -0.25 * S;
449  q[3] = -(m9 + m6 ) / S;
450  q[0] = (m2 - m8 ) / S;
451  } else {
452  // Column 2:
453  S = sqrtf( 1.0 + m10 - m0 - m5 ) * 2;
454  q[1] = -(m2 + m8 ) / S;
455  q[2] = -(m9 + m6 ) / S;
456  q[3] = -0.25 * S;
457  q[0] = (m4 - m1 ) / S;
458  }
459 
460  double mag2 = 0;
461  for (int i = 0; i < 4; i++)
462  mag2 += q[i]*q[i];
463  double norm = 1.0 / sqrtf(mag2);
464  for (int i = 0; i < 4; i++)
465  q[i] *= norm;
466 }
467 */
468 
469 // overwrites upper 3x3 area of matrix M. Doesn't touch any other elements of M.
470 void quat_to_matrix(const double q[4], matd_t *M)
471 {
472  double w = q[0], x = q[1], y = q[2], z = q[3];
473 
474  MATD_EL(M, 0, 0) = w*w + x*x - y*y - z*z;
475  MATD_EL(M, 0, 1) = 2*x*y - 2*w*z;
476  MATD_EL(M, 0, 2) = 2*x*z + 2*w*y;
477 
478  MATD_EL(M, 1, 0) = 2*x*y + 2*w*z;
479  MATD_EL(M, 1, 1) = w*w - x*x + y*y - z*z;
480  MATD_EL(M, 1, 2) = 2*y*z - 2*w*x;
481 
482  MATD_EL(M, 2, 0) = 2*x*z - 2*w*y;
483  MATD_EL(M, 2, 1) = 2*y*z + 2*w*x;
484  MATD_EL(M, 2, 2) = w*w - x*x - y*y + z*z;
485 }
static void zarray_get_volatile(const zarray_t *za, int idx, void *p)
Definition: zarray.h:218
void quat_to_matrix(const double q[4], matd_t *M)
Definition: homography.c:470
matd_svd_t matd_svd(matd_t *A)
Definition: matd.c:1448
void matd_destroy(matd_t *m)
Definition: matd.c:226
#define MATD_SVD_NO_WARNINGS
Definition: matd.h:383
matd_t * matd_inverse(const matd_t *a)
Definition: matd.c:485
matd_t * matd_op(const char *expr,...)
Definition: matd.c:798
matd_t * matd_plu_solve(const matd_plu_t *mlu, const matd_t *b)
Definition: matd.c:1648
static int zarray_size(const zarray_t *za)
Definition: zarray.h:136
matd_t * matd_identity(int dim)
Definition: matd.c:99
#define MATD_EL(m, row, col)
Definition: matd.h:71
matd_t * homography_compute(zarray_t *correspondences, int flags)
Definition: homography.c:43
matd_t * matd_create(int rows, int cols)
Definition: matd.c:50
void matd_plu_destroy(matd_plu_t *mlu)
Definition: matd.c:1580
#define HOMOGRAPHY_COMPUTE_FLAG_INVERSE
Definition: homography.h:134
matd_svd_t matd_svd_flags(matd_t *A, int flags)
Definition: matd.c:1453
matd_t * S
Definition: matd.h:366
static double sq(double v)
Definition: math_util.h:84
matd_t * homography_to_pose(const matd_t *H, double fx, double fy, double cx, double cy)
Definition: homography.c:281
matd_t * U
Definition: matd.h:365
matd_plu_t * matd_plu(const matd_t *a)
Definition: matd.c:1501
Definition: matd.h:51
Definition: zarray.h:49
matd_t * V
Definition: matd.h:367
matd_t * matd_create_data(int rows, int cols, const double *data)
void matd_chol_destroy(matd_chol_t *chol)
Definition: matd.c:1887
matd_t * homography_to_model_view(const matd_t *H, double F, double G, double A, double B, double C, double D)
Definition: homography.c:366
matd_chol_t * matd_chol(matd_t *A)
Definition: matd.c:1840
matd_t * matd_chol_solve(const matd_chol_t *chol, const matd_t *b)
Definition: matd.c:1938


apriltags2
Author(s): Danylo Malyuta
autogenerated on Fri Oct 19 2018 04:02:32