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


apriltag
Author(s): Edwin Olson , Max Krogius
autogenerated on Mon Jun 26 2023 02:26:12