00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <qvmath/qvepipolar.h>
00022 #include <math.h>
00023
00024
00025 QVMatrix getDLTMatrix(const QVector<QPointFMatching> &matchings)
00026 {
00027 QVMatrix A(matchings.size(),9);
00028 double *aptr = A.getWriteData();
00029
00030 foreach(QPointFMatching matching,matchings)
00031 {
00032 const QPointF sourcePoint = matching.first,
00033 destPoint = matching.second;
00034 const double x = sourcePoint.x(),
00035 y = sourcePoint.y(),
00036 x_p = destPoint.x(),
00037 y_p = destPoint.y();
00038
00039
00040 aptr[0] = x*x_p;
00041 aptr[1] = y*x_p;
00042 aptr[2] = x_p;
00043 aptr[3] = x*y_p;
00044 aptr[4] = y*y_p;
00045 aptr[5] = y_p;
00046 aptr[6] = x;
00047 aptr[7] = y;
00048 aptr[8] = 1.0;
00049 aptr += 9;
00050 }
00051 return A;
00052 }
00053
00054 QVMatrix getSquaredDLTMatrix(const QVector<QPointFMatching> &matchings, const QPointF &m0c, const QPointF &m1c, const double &scale0, const double &scale1)
00055 {
00056 double accum[36];
00057 for(int i = 0; i < 36; i++)
00058 accum[i] = 0.0;
00059
00060 const double m0c_x = m0c.x(), m0c_y = m0c.y(),
00061 m1c_x = m1c.x(), m1c_y = m1c.y();
00062
00063 for(int i = 0; i < matchings.count(); i++)
00064 {
00065 const QPointFMatching &matching = matchings[i];
00066 const QPointF &sourcePoint = matching.first,
00067 &destPoint = matching.second;
00068
00069 const double x = scale0 * (sourcePoint.x() - m0c_x),
00070 y = scale0 * (sourcePoint.y() - m0c_y),
00071 xp = scale1 * (destPoint.x() - m1c_x),
00072 yp = scale1 * (destPoint.y() - m1c_y);
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087 const double t1 = x * x,
00088 t2 = xp * xp,
00089 t4 = x * t2,
00090 t6 = t1 * xp,
00091 t8 = x * xp,
00092 t9 = y * yp,
00093 t13 = y * y,
00094 t16 = t13 * xp,
00095 t18 = y * xp,
00096 t21 = yp * yp,
00097 t23 = x * t21,
00098 t26 = x * yp;
00099
00100 accum[0] += t1 * t2;
00101 accum[1] += t4 * y;
00102 accum[2] += t4;
00103 accum[3] += t6 * yp;
00104 accum[4] += t8 * t9;
00105 accum[5] += t8 * yp;
00106 accum[6] += t6;
00107 accum[7] += t8 * y;
00108 accum[8] += t8;
00109 accum[9] += t13 * t2;
00110 accum[10] += y * t2;
00111 accum[11] += t16 * yp;
00112 accum[12] += t18 * yp;
00113 accum[13] += t16;
00114 accum[14] += t18;
00115 accum[15] += t2;
00116 accum[16] += xp * yp;
00117 accum[17] += xp;
00118 accum[18] += t1 * t21;
00119 accum[19] += t23 * y;
00120 accum[20] += t23;
00121 accum[21] += t1 * yp;
00122 accum[22] += t26 * y;
00123 accum[23] += t26;
00124 accum[24] += t13 * t21;
00125 accum[25] += y * t21;
00126 accum[26] += t13 * yp;
00127 accum[27] += t9;
00128 accum[28] += t21;
00129 accum[29] += yp;
00130 accum[30] += t1;
00131 accum[31] += x * y;
00132 accum[32] += x;
00133 accum[33] += t13;
00134 accum[34] += y;
00135 accum[35] += 1;
00136 }
00137
00138 return QVMatrix(9,9, (double[9*9]) {
00139 accum[0], accum[1], accum[2], accum[3], accum[4], accum[5], accum[6], accum[7], accum[8],
00140 accum[1], accum[9], accum[10], accum[4], accum[11], accum[12], accum[7], accum[13], accum[14],
00141 accum[2], accum[10], accum[15], accum[5], accum[12], accum[16], accum[8], accum[14], accum[17],
00142 accum[3], accum[4], accum[5], accum[18], accum[19], accum[20], accum[21], accum[22], accum[23],
00143 accum[4], accum[11], accum[12], accum[19], accum[24], accum[25], accum[22], accum[26], accum[27],
00144 accum[5], accum[12], accum[16], accum[20], accum[25], accum[28], accum[23], accum[27], accum[29],
00145 accum[6], accum[7], accum[8], accum[21], accum[22], accum[23], accum[30], accum[31], accum[32],
00146 accum[7], accum[13], accum[14], accum[22], accum[26], accum[27], accum[31], accum[33], accum[34],
00147 accum[8], accum[14], accum[17], accum[23], accum[27], accum[29], accum[32], accum[34], accum[35]
00148 }
00149 );
00150 }
00151
00152 void normalizeMatchingsForEpipolarestimation(const QVector<QPointFMatching> &matchings, QPointF &m0c, QPointF &m1c, double &scale0, double &scale1, QVMatrix &Q1, QVMatrix &Q2)
00153 {
00154
00155 m0c.rx() = 0.0; m0c.ry() = 0.0;
00156 m1c.rx() = 0.0; m1c.ry() = 0.0;
00157
00158 foreach(QPointFMatching matching, matchings)
00159 {
00160 m0c += matching.first;
00161 m1c += matching.second;
00162 }
00163
00164 const int count = matchings.count();
00165 m0c /= count;
00166 m1c /= count;
00167
00168 scale0 = 0.0;
00169 scale1 = 0.0;
00170 foreach(QPointFMatching matching, matchings)
00171 {
00172 scale0 += norm2(matching.first - m0c);
00173 scale1 += norm2(matching.second - m1c);
00174 }
00175
00176 scale0 = count * sqrt(2.) /scale0;
00177 scale1 = count * sqrt(2.) /scale1;
00178
00179
00180 Q1 = QVMatrix::cameraCalibrationMatrix(scale0, 1.0, -scale0*m0c.x(), -scale0*m0c.y()),
00181 Q2 = QVMatrix::cameraCalibrationMatrix(scale1, 1.0, -scale1*m1c.x(), -scale1*m1c.y());
00182 }
00183
00184 bool solveForFundamentalMatrix(const QVMatrix &omega, QVMatrix &F, const TQVSVD_Method svdMethod = DEFAULT_TQVSVD_METHOD)
00185 {
00186 QVVector x;
00187 solveHomogeneous(omega, x, svdMethod);
00188 const QVMatrix preF = QVMatrix(3,3, x);
00189
00190
00191 QVMatrix U, V;
00192 QVVector s;
00193 singularValueDecomposition(preF, U, s, V, svdMethod);
00194
00195 double *dataU = U.getWriteData();
00196 for(int i = 0; i < 3; i++)
00197 {
00198 dataU[3*i+0] *= s[0];
00199 dataU[3*i+1] *= s[1];
00200 dataU[3*i+2] = 0.0;
00201 }
00202
00203 F = U.dotProduct(V, false, true);
00204
00205 return (F.norm2() > MIN_FUNDAMENTAL_MATRIX_NORM);
00206 }
00207
00208 bool computeFundamentalMatrix(const QVector<QPointFMatching> &matchings, QVMatrix &F, const TQVSVD_Method svdMethod)
00209 {
00210 if (matchings.count() < 8)
00211 return false;
00212
00213 QPointF m0c, m1c;
00214 double scale0, scale1;
00215 QVMatrix Q1, Q2, preF;
00216
00217 normalizeMatchingsForEpipolarestimation(matchings, m0c, m1c, scale0, scale1, Q1, Q2);
00218
00219
00220 const QVMatrix omega = getSquaredDLTMatrix(matchings, m0c, m1c, scale0, scale1);
00221
00222 if (not solveForFundamentalMatrix(omega, preF, svdMethod))
00223 return false;
00224
00225 F = Q2.transpose() * preF * Q1;
00226
00227
00228 for(int j = 0; j < 3; j++ )
00229 for(int k = 0; k < 3; k++ )
00230 F(j,k) = float(F(j,k));
00231
00232 return true;
00233 }
00234
00235 QVVector symmetricEpipolarDistance2(const QVMatrix &F, const QVector<QPointFMatching> &matchings)
00236 {
00237
00238
00239 QVVector result(matchings.count());
00240 for(int i = 0; i < matchings.count(); i++)
00241 {
00242 const QPointF &p1 = matchings[i].first,
00243 &p2 = matchings[i].second;
00244 const QVVector l1 = F*QV3DPointF(p1.x(), p1.y(), 1.0),
00245 l2 = QV3DPointF(p2.x(), p2.y(), 1.0)*F;
00246 const double distance = qvPointLineDistance(l1, p2) + qvPointLineDistance(l2, p1);
00247
00248 result[i] = distance;
00249 }
00250 return result;
00251 }
00252
00253 double symmetricEpipolarDistance(const QVMatrix &F, const QPointFMatching &matching)
00254 {
00255 const double *f = F.getReadData();
00256
00257 const QPointF &p1 = matching.first,
00258 &p2 = matching.second;
00259 const double p1x = p1.x(), p1y = p1.y(),
00260 p2x = p2.x(), p2y = p2.y();
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285 const double t3 = f[0] * p1x + f[1] * p1y + f[2],
00286 t7 = f[3] * p1x + f[4] * p1y + f[5],
00287 t12 = t3 * t3,
00288 t13 = t7 * t7,
00289 t17 = p2x * f[0] + p2y * f[3] + f[6],
00290 t21 = p2x * f[1] + p2y * f[4] + f[7],
00291 t26 = t17 * t17,
00292 t27 = t21 * t21,
00293 cg0 = t3 * p2x + t7 * p2y + f[6] * p1x + f[7] * p1y + f[8],
00294 cg1 = t12 + t13,
00295 cg2 = t17 * p1x + t21 * p1y + p2x * f[2] + p2y * f[5] + f[8],
00296 cg3 = t26 + t27;
00297
00298 return ABS(cg0)/sqrt(cg1) + ABS(cg2)/sqrt(cg3);
00299 }
00300
00301
00302 QVVector symmetricEpipolarDistance(const QVMatrix &F, const QVector<QPointFMatching> &matchings)
00303 {
00304 const double *f = F.getReadData();
00305
00306 QVVector result(matchings.count());
00307 for(int i = 0; i < matchings.count(); i++)
00308 {
00309 const QPointF &p1 = matchings[i].first,
00310 &p2 = matchings[i].second;
00311 const double p1x = p1.x(), p1y = p1.y(),
00312 p2x = p2.x(), p2y = p2.y();
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337 const double t3 = f[0] * p1x + f[1] * p1y + f[2],
00338 t7 = f[3] * p1x + f[4] * p1y + f[5],
00339 t12 = t3 * t3,
00340 t13 = t7 * t7,
00341 t17 = p2x * f[0] + p2y * f[3] + f[6],
00342 t21 = p2x * f[1] + p2y * f[4] + f[7],
00343 t26 = t17 * t17,
00344 t27 = t21 * t21,
00345 cg0 = t3 * p2x + t7 * p2y + f[6] * p1x + f[7] * p1y + f[8],
00346 cg1 = t12 + t13,
00347 cg2 = t17 * p1x + t21 * p1y + p2x * f[2] + p2y * f[5] + f[8],
00348 cg3 = t26 + t27;
00349
00350 result[i] = ABS(cg0)/sqrt(cg1) + ABS(cg2)/sqrt(cg3);
00351 }
00352
00353 #ifdef DEBUG
00354 for(int i = 0; i < result.count(); i++)
00355 {
00356 const double distance = ABS(result[i] - symmetricEpipolarDistance(F, matchings[i])) / (ABS(result[i]) + ABS(symmetricEpipolarDistance(F, matchings[i])));
00357 Q_WARNING(distance < 1e-6);
00358 }
00359
00360 const QVVector result2 = symmetricEpipolarDistance2(F, matchings);
00361 Q_ASSERT_X(result.count() == matchings.count(), "symmetricEpipolarDistance()", "number of residuals differs from number of matchings");
00362 Q_ASSERT_X(result2.count() == result.count(), "symmetricEpipolarDistance()", "number of estimated residuals differs from number of ground-truth residuals");
00363
00364 const double max_residuals_norm = MAX(result.norm2(), result2.norm2()) / matchings.count(),
00365 relative_error = (result - result2).norm2() / max_residuals_norm;
00366
00367
00368
00369
00370
00371
00372
00373
00374 Q_WARNING_X( (relative_error < 1e-8) or (max_residuals_norm < 1e-8), "symmetricEpipolarDistance()", "relative error with ground-truth residuals is too high");
00375 #endif // DEBUG
00376
00377 return result;
00378 }
00379
00380
00381
00382 bool iterativeLocalOptimization2(const QList<QPointFMatching> &matchings, QList<QPointFMatching> &result, const double maxEE, const int minInliers)
00383 {
00384 result = matchings;
00385
00386 while(true)
00387 {
00388
00389 QVMatrix F = computeFundamentalMatrix(result);
00390 if (F == QVMatrix())
00391 return false;
00392
00393
00394 const QVVector residuals = symmetricEpipolarDistance(F, result.toVector());
00395
00396
00397 const double actualMax = residuals.max();
00398 if (actualMax < maxEE)
00399 return true;
00400
00401
00402 const double median = residuals.median();
00403 if (actualMax < 2.0*median)
00404 return false;
00405
00406
00407 QList<QPointFMatching> newMatchings;
00408 for(int i = 0; i < residuals.count(); i++)
00409 if (residuals[i] < MAX(2*median, maxEE))
00410 newMatchings << result[i];
00411
00412
00413 if (newMatchings.count() < minInliers)
00414 return false;
00415
00416
00417 result = newMatchings;
00418 }
00419
00420 return true;
00421 }
00422
00423 bool iterativeLocalOptimization(const QList<QPointFMatching> &matchings, QList<QPointFMatching> &result, const double maxEE, const int minInliers)
00424 {
00425 result = matchings;
00426
00427 while(true)
00428 {
00429
00430 QVMatrix F;
00431 if (not computeFundamentalMatrix(result.toVector(), F))
00432 return false;
00433
00434
00435 const QVVector residuals = symmetricEpipolarDistance(F, result.toVector());
00436
00437
00438 const double actualMax = residuals.max();
00439 if (actualMax < maxEE)
00440 return true;
00441
00442
00443 const double median = residuals.median();
00444
00445
00446
00447 if (actualMax < 2.0*median)
00448 return false;
00449
00450
00451
00452 QList<QPointFMatching> newMatchings;
00453 for(int i = 0; i < residuals.count(); i++)
00454 if (residuals[i] < MAX(2*median, maxEE))
00455 newMatchings << result[i];
00456
00457
00458 if (newMatchings.count() < minInliers)
00459 return false;
00460
00461
00462 result = newMatchings;
00463 }
00464
00465 return true;
00466 }
00467