00001 #include <stdio.h>
00002 #include <iostream>
00003 #include <assert.h>
00004
00005 #include "refine_motion_estimate.hpp"
00006
00007 #define dump(v) std::cerr << #v << " : " << (v) << "\n"
00008 #define dumpT(v) std::cerr << #v << " : " << (v).transpose() << "\n"
00009
00010
00011 #define dbg(...)
00012
00013 #define USE_ESM
00014
00015 namespace fovis
00016 {
00017
00018 static inline Eigen::Isometry3d
00019 isometryFromParams(const Eigen::Matrix<double, 6, 1>& params)
00020 {
00021 Eigen::Isometry3d result;
00022
00023 double roll = params(3), pitch = params(4), yaw = params(5);
00024 double halfroll = roll / 2;
00025 double halfpitch = pitch / 2;
00026 double halfyaw = yaw / 2;
00027 double sin_r2 = sin(halfroll);
00028 double sin_p2 = sin(halfpitch);
00029 double sin_y2 = sin(halfyaw);
00030 double cos_r2 = cos(halfroll);
00031 double cos_p2 = cos(halfpitch);
00032 double cos_y2 = cos(halfyaw);
00033
00034 Eigen::Quaterniond quat(
00035 cos_r2 * cos_p2 * cos_y2 + sin_r2 * sin_p2 * sin_y2,
00036 sin_r2 * cos_p2 * cos_y2 - cos_r2 * sin_p2 * sin_y2,
00037 cos_r2 * sin_p2 * cos_y2 + sin_r2 * cos_p2 * sin_y2,
00038 cos_r2 * cos_p2 * sin_y2 - sin_r2 * sin_p2 * cos_y2);
00039
00040 result.setIdentity();
00041 result.translate(params.head<3>());
00042 result.rotate(quat);
00043
00044 return result;
00045 }
00046
00047 static inline Eigen::Matrix<double, 6, 1>
00048 isometryToParams(const Eigen::Isometry3d& M)
00049 {
00050 Eigen::Quaterniond q(M.rotation());
00051 double roll_a = 2 * (q.w()*q.x() + q.y()*q.z());
00052 double roll_b = 1 - 2 * (q.x()*q.x() + q.y()*q.y());
00053 double pitch_sin = 2 * (q.w()*q.y() - q.z()*q.x());
00054 double yaw_a = 2 * (q.w()*q.z() + q.x()*q.y());
00055 double yaw_b = 1 - 2 * (q.y()*q.y() + q.z()*q.z());
00056
00057 Eigen::Matrix<double, 6, 1> result;
00058 result.head<3>() = M.translation();
00059 result(3) = atan2(roll_a, roll_b);
00060 result(4) = asin(pitch_sin);
00061 result(5) = atan2(yaw_a, yaw_b);
00062 return result;
00063 }
00064
00065 static void
00066 computeReprojectionError(const Eigen::Matrix<double, 4, Eigen::Dynamic>& points,
00067 const Eigen::Matrix<double, 2, Eigen::Dynamic>& ref_projections,
00068 const Eigen::Matrix<double, 3, 4>& K,
00069 const Eigen::Isometry3d& motion,
00070 Eigen::VectorXd* err,
00071 int err_offset)
00072 {
00073 int num_points = points.cols();
00074 assert(((err->size() == num_points * 2) && (err_offset == 0)) ||
00075 ((err->size() == num_points * 4) && ((err_offset == 0) || (err_offset == num_points*2))));
00076 assert(num_points == ref_projections.cols());
00077
00078 Eigen::Matrix<double, 3, 4> P = K * motion.matrix();
00079
00080 for(int pind=0; pind < num_points; pind++) {
00081 Eigen::Vector3d uvw = P * points.col(pind);
00082 double u = uvw(0) / uvw(2);
00083 double v = uvw(1) / uvw(2);
00084 (*err)(err_offset + pind*2 + 0) = u - ref_projections(0, pind);
00085 (*err)(err_offset + pind*2 + 1) = v - ref_projections(1, pind);
00086 }
00087 }
00088
00089 static void
00090 computeProjectionJacobian(const Eigen::Matrix<double, 6, 1>& params,
00091 double fx, double px, double py,
00092 const Eigen::Matrix<double, 4, Eigen::Dynamic>& points,
00093 Eigen::Matrix<double, Eigen::Dynamic, 6> *result,
00094 int result_row_offset)
00095 {
00096 double tx = params(0);
00097 double ty = params(1);
00098 double tz = params(2);
00099 double roll = params(3);
00100 double pitch = params(4);
00101 double yaw = params(5);
00102 double sr = sin(roll);
00103 double cr = cos(roll);
00104 double sp = sin(pitch);
00105 double cp = cos(pitch);
00106 double sy = sin(yaw);
00107 double cy = cos(yaw);
00108
00109
00110 Eigen::Matrix<double, 3, 4> P;
00111 P <<
00112 fx*cp*cy - px*sp, px*cp*sr - fx*(cr*sy - cy*sp*sr), fx*(sr*sy + cr*cy*sp) + px*cp*cr, fx*tx + px*tz,
00113 fx*cp*sy - py*sp, fx*(cr*cy + sp*sr*sy) + py*cp*sr, py*cp*cr - fx*(cy*sr - cr*sp*sy), fx*ty + py*tz,
00114 -sp, cp*sr, cp*cr, tz;
00115
00116
00117
00118 Eigen::Matrix<double, 6, 4> Ju, Jv, Jw;
00119 Ju << 0, 0, 0, fx,
00120 0, 0, 0, 0,
00121 0, 0, 0, px,
00122 0, fx*(sr*sy + cr*cy*sp) + px*cp*cr, fx*(cr*sy - cy*sp*sr) - px*cp*sr, 0,
00123 - px*cp - fx*cy*sp, -sr*(px*sp - fx*cp*cy), -cr*(px*sp - fx*cp*cy), 0,
00124 -fx*cp*sy, - fx*cr*cy - fx*sp*sr*sy, fx*cy*sr - fx*cr*sp*sy, 0;
00125 Jv << 0, 0, 0, 0,
00126 0, 0, 0, fx,
00127 0, 0, 0, py,
00128 0, py*cp*cr - fx*(cy*sr - cr*sp*sy), - fx*(cr*cy + sp*sr*sy) - py*cp*sr, 0,
00129 - py*cp - fx*sp*sy, -sr*(py*sp - fx*cp*sy), -cr*(py*sp - fx*cp*sy), 0,
00130 fx*cp*cy, fx*cy*sp*sr - fx*cr*sy, fx*sr*sy + fx*cr*cy*sp, 0;
00131 Jw << 0, 0, 0, 0,
00132 0, 0, 0, 0,
00133 0, 0, 0, 1,
00134 0, cp*cr, -cp*sr, 0,
00135 -cp, -sp*sr, -cr*sp, 0,
00136 0, 0, 0, 0;
00137
00138 int num_points = points.cols();
00139
00140 assert(result->rows() == num_points*4 || result->rows() == num_points*2);
00141 assert(result->cols() == 6);
00142 assert(result_row_offset == 0);
00143 int row = 0;
00144 for(int i=0; i<num_points; i++) {
00145 const Eigen::Vector4d& point = points.col(i);
00146 Eigen::Vector3d uvw = P * point;
00147 Eigen::Matrix<double, 6, 1> du = Ju * point;
00148 Eigen::Matrix<double, 6, 1> dv = Jv * point;
00149 Eigen::Matrix<double, 6, 1> dw = Jw * point;
00150
00151 double w_inv_sq = 1 / (uvw(2) * uvw(2));
00152
00153 result->row(result_row_offset + row) = (du * uvw(2) - dw * uvw(0)) * w_inv_sq;
00154 result->row(result_row_offset + row+1) = (dv * uvw(2) - dw * uvw(1)) * w_inv_sq;
00155
00156 row += 2;
00157 }
00158 }
00159
00160 Eigen::Isometry3d
00161 refineMotionEstimate(const Eigen::Matrix<double, 4, Eigen::Dynamic>& points,
00162 const Eigen::Matrix<double, 2, Eigen::Dynamic>& ref_projections,
00163 double fx, double px, double py,
00164 const Eigen::Isometry3d& initial_estimate,
00165 int max_iterations)
00166 {
00167 Eigen::Matrix<double, 3, 4> xyz_c_to_uvw_c;
00168 xyz_c_to_uvw_c << fx, 0, px, 0,
00169 0, fx, py, 0,
00170 0, 0, 1, 0;
00171
00172 Eigen::Isometry3d estimate = initial_estimate;
00173 Eigen::Matrix<double, 6, 1> estimate_vec = isometryToParams(estimate);
00174
00175 int num_points = points.cols();
00176
00177
00178 Eigen::VectorXd err(num_points*2);
00179 computeReprojectionError(points, ref_projections, xyz_c_to_uvw_c, estimate,
00180 &err, 0);
00181
00182 double initial_sse = err.cwiseProduct(err).sum();
00183 double final_sse = initial_sse;
00184 dbg("T0 : [%7.3f %7.3f %7.3f] R: [%7.3f %7.3f %7.3f] E: %f\n",
00185 estimate_vec(0), estimate_vec(1), estimate_vec(2),
00186 estimate_vec(3), estimate_vec(4), estimate_vec(5), final_sse);
00187
00188
00189
00190
00191 Eigen::Matrix<double, Eigen::Dynamic, 6> M(num_points*2, 6);
00192
00193 for(int iter_num=0; iter_num<max_iterations; iter_num++) {
00194 computeProjectionJacobian(estimate_vec, fx, px, py, points, &M, 0);
00195
00196
00197
00198
00199
00200
00201
00202 Eigen::Matrix<double, 6, 1> g = M.transpose() * err;
00203 Eigen::Matrix<double, 6, 6> MtM = M.transpose() * M;
00204
00205
00206 Eigen::Matrix<double, 6, 1> delta = - MtM.inverse() * g;
00207
00208
00209 Eigen::Matrix<double, 6, 1> next_params = estimate_vec + delta;
00210 Eigen::Isometry3d next_estimate = isometryFromParams(next_params);
00211
00212
00213 computeReprojectionError(points, ref_projections, xyz_c_to_uvw_c, next_estimate,
00214 &err, 0);
00215 double sse = err.cwiseProduct(err).sum();
00216
00217
00218
00219 if(sse > final_sse)
00220 break;
00221
00222
00223 estimate_vec = next_params;
00224 estimate = next_estimate;
00225 final_sse = sse;
00226
00227
00228 if(fabs(delta(0)) < 0.0001 &&
00229 fabs(delta(1)) < 0.0001 &&
00230 fabs(delta(2)) < 0.0001 &&
00231 fabs(delta(3)) < (0.01 * M_PI/180) &&
00232 fabs(delta(4)) < (0.01 * M_PI/180) &&
00233 fabs(delta(5)) < (0.01 * M_PI/180))
00234 break;
00235
00236 dbg("T%-2d: [%7.3f %7.3f %7.3f] R: [%7.3f %7.3f %7.3f] E: %f\n",
00237 iter_num+1,
00238 estimate_vec(0), estimate_vec(1), estimate_vec(2),
00239 estimate_vec(3), estimate_vec(4), estimate_vec(5), final_sse);
00240 }
00241 dbg("T--: [%7.3f %7.3f %7.3f] R: [%7.3f %7.3f %7.3f] E: %f\n",
00242 estimate_vec(0), estimate_vec(1), estimate_vec(2),
00243 estimate_vec(3), estimate_vec(4), estimate_vec(5), final_sse);
00244
00245 return estimate;
00246 }
00247
00248
00249
00250 static void
00251 computeReverseProjectionJacobian(const Eigen::Matrix<double, 6, 1>& params,
00252 double fx, double px, double py,
00253 const Eigen::Matrix<double, 4, Eigen::Dynamic>& points,
00254 Eigen::Matrix<double, Eigen::Dynamic, 6>* result,
00255 int result_row_offset)
00256 {
00257 double tx = params(0);
00258 double ty = params(1);
00259 double tz = params(2);
00260 double roll = params(3);
00261 double pitch = params(4);
00262 double yaw = params(5);
00263 double sr = sin(roll);
00264 double cr = cos(roll);
00265 double sp = sin(pitch);
00266 double cp = cos(pitch);
00267 double sy = sin(yaw);
00268 double cy = cos(yaw);
00269
00270
00271 Eigen::Matrix<double, 3, 4> P;
00272 P << px*(sr*sy + cr*cy*sp) + fx*cp*cy,
00273 fx*cp*sy - px*(cy*sr - cr*sp*sy),
00274 px*cp*cr - fx*sp,
00275 - px*(tx*(sr*sy + cr*cy*sp) - ty*(cy*sr - cr*sp*sy) + tz*cp*cr) - fx*(tx*cp*cy - tz*sp + ty*cp*sy),
00276
00277 py*(sr*sy + cr*cy*sp) - fx*(cr*sy - cy*sp*sr),
00278 fx*(cr*cy + sp*sr*sy) - py*(cy*sr - cr*sp*sy),
00279 cp*(py*cr + fx*sr),
00280 - py*(tx*(sr*sy + cr*cy*sp) - ty*(cy*sr - cr*sp*sy) + tz*cp*cr) - fx*(ty*(cr*cy + sp*sr*sy) - tx*(cr*sy - cy*sp*sr) + tz*cp*sr),
00281
00282 sr*sy + cr*cy*sp,
00283 cr*sp*sy - cy*sr,
00284 cp*cr,
00285 ty*(cy*sr - cr*sp*sy) - tx*(sr*sy + cr*cy*sp) - tz*cp*cr;
00286
00287
00288
00289 Eigen::Matrix<double, 6, 4> Ju, Jv, Jw;
00290 Ju << 0, 0, 0, - px*(sr*sy + cr*cy*sp) - fx*cp*cy,
00291 0, 0, 0, px*(cy*sr - cr*sp*sy) - fx*cp*sy,
00292 0, 0, 0, fx*sp - px*cp*cr,
00293 px*cr*sy - px*cy*sp*sr, - px*cr*cy - px*sp*sr*sy, -px*cp*sr, px*ty*(cr*cy + sp*sr*sy) - px*tx*(cr*sy - cy*sp*sr) + px*tz*cp*sr,
00294 -cy*(fx*sp - px*cp*cr), -sy*(fx*sp - px*cp*cr), - fx*cp - px*cr*sp, fx*(tz*cp + tx*cy*sp + ty*sp*sy) - px*(tx*cp*cr*cy - tz*cr*sp + ty*cp*cr*sy),
00295 px*(cy*sr - cr*sp*sy) - fx*cp*sy, px*(sr*sy + cr*cy*sp) + fx*cp*cy, 0, - fx*(ty*cp*cy - tx*cp*sy) - px*(tx*(cy*sr - cr*sp*sy) + ty*(sr*sy + cr*cy*sp));
00296
00297 Jv << 0, 0, 0, fx*(cr*sy - cy*sp*sr) - py*(sr*sy + cr*cy*sp),
00298 0, 0, 0, py*(cy*sr - cr*sp*sy) - fx*(cr*cy + sp*sr*sy),
00299 0, 0, 0, -cp*(py*cr + fx*sr),
00300 fx*(sr*sy + cr*cy*sp) + py*(cr*sy - cy*sp*sr), - fx*(cy*sr - cr*sp*sy) - py*(cr*cy + sp*sr*sy), cp*(fx*cr - py*sr), py*(ty*(cr*cy + sp*sr*sy) - tx*(cr*sy - cy*sp*sr) + tz*cp*sr) - fx*(tx*(sr*sy + cr*cy*sp) - ty*(cy*sr - cr*sp*sy) + tz*cp*cr),
00301 cp*cy*(py*cr + fx*sr), cp*sy*(py*cr + fx*sr), -sp*(py*cr + fx*sr), -(py*cr + fx*sr)*(tx*cp*cy - tz*sp + ty*cp*sy),
00302 py*(cy*sr - cr*sp*sy) - fx*(cr*cy + sp*sr*sy), py*(sr*sy + cr*cy*sp) - fx*(cr*sy - cy*sp*sr), 0, fx*(tx*(cr*cy + sp*sr*sy) + ty*(cr*sy - cy*sp*sr)) - py*(tx*(cy*sr - cr*sp*sy) + ty*(sr*sy + cr*cy*sp));
00303
00304 Jw << 0, 0, 0, - sr*sy - cr*cy*sp,
00305 0, 0, 0, cy*sr - cr*sp*sy,
00306 0, 0, 0, -cp*cr,
00307 cr*sy - cy*sp*sr, - cr*cy - sp*sr*sy, -cp*sr, ty*(cr*cy + sp*sr*sy) - tx*(cr*sy - cy*sp*sr) + tz*cp*sr,
00308 cp*cr*cy, cp*cr*sy, -cr*sp, -cr*(tx*cp*cy - tz*sp + ty*cp*sy),
00309 cy*sr - cr*sp*sy, sr*sy + cr*cy*sp, 0, - tx*(cy*sr - cr*sp*sy) - ty*(sr*sy + cr*cy*sp);
00310
00311 int num_points = points.cols();
00312 assert(result->rows() == num_points*4 || result->rows() == num_points*2);
00313 assert(result->cols() == 6);
00314 assert(result_row_offset == 0 || result_row_offset == (result->rows() / 2));
00315
00316 int row = 0;
00317 for(int i=0; i<num_points; i++) {
00318 const Eigen::Vector4d& point = points.col(i);
00319 Eigen::Vector3d uvw = P * point;
00320 Eigen::Matrix<double, 6, 1> du = Ju * point;
00321 Eigen::Matrix<double, 6, 1> dv = Jv * point;
00322 Eigen::Matrix<double, 6, 1> dw = Jw * point;
00323
00324 double w_inv_sq = 1 / (uvw(2) * uvw(2));
00325
00326 result->row(result_row_offset + row) = (du * uvw(2) - dw * uvw(0)) * w_inv_sq;
00327 result->row(result_row_offset + row+1) = (dv * uvw(2) - dw * uvw(1)) * w_inv_sq;
00328
00329 row += 2;
00330 }
00331 }
00332
00333 void refineMotionEstimateBidirectional(const Eigen::Matrix<double, 4, Eigen::Dynamic>& ref_points,
00334 const Eigen::Matrix<double, 2, Eigen::Dynamic>& ref_projections,
00335 const Eigen::Matrix<double, 4, Eigen::Dynamic>& target_points,
00336 const Eigen::Matrix<double, 2, Eigen::Dynamic>& target_projections,
00337 double fx,
00338 double px, double py,
00339 const Eigen::Isometry3d& initial_estimate,
00340 int max_iterations,
00341 Eigen::Isometry3d* result,
00342 Eigen::MatrixXd* result_covariance)
00343 {
00344 Eigen::Matrix<double, 3, 4> xyz_c_to_uvw_c;
00345 xyz_c_to_uvw_c << fx, 0, px, 0,
00346 0, fx, py, 0,
00347 0, 0, 1, 0;
00348
00349 Eigen::Matrix<double, 6, 1> estimate_vec = isometryToParams(initial_estimate);
00350
00351 int num_points = target_points.cols();
00352
00353
00354 Eigen::VectorXd err(num_points*4);
00355
00356 computeReprojectionError(target_points, ref_projections, xyz_c_to_uvw_c,
00357 initial_estimate, &err, 0);
00358
00359 computeReprojectionError(ref_points, target_projections, xyz_c_to_uvw_c,
00360 initial_estimate.inverse(), &err, num_points*2);
00361
00362 #ifdef USE_ESM
00363
00364 Eigen::Matrix<double, Eigen::Dynamic, 6> M_0(num_points*4, 6);
00365 Eigen::Matrix<double, 6, 1> zero_vec;
00366 zero_vec.setZero();
00367 computeProjectionJacobian(zero_vec, fx, px, py, ref_points, &M_0, 0);
00368 computeReverseProjectionJacobian(zero_vec, fx, px, py, target_points, &M_0, num_points*2);
00369 #endif
00370
00371 double initial_sse = err.cwiseProduct(err).sum();
00372 double final_sse = initial_sse;
00373 dbg("T0 : [%7.3f %7.3f %7.3f] R: [%7.3f %7.3f %7.3f] E: %f\n",
00374 estimate_vec(0), estimate_vec(1), estimate_vec(2),
00375 estimate_vec(3), estimate_vec(4), estimate_vec(5), final_sse);
00376
00377 Eigen::Matrix<double, Eigen::Dynamic, 6> M(num_points * 4, 6);
00378
00379 for(int iter_num=0; iter_num<max_iterations; iter_num++) {
00380
00381 computeProjectionJacobian(estimate_vec, fx, px, py, target_points, &M, 0);
00382
00383
00384 computeReverseProjectionJacobian(estimate_vec, fx, px, py, ref_points, &M, num_points*2);
00385
00386 #ifdef USE_ESM
00387
00388
00389 M += M_0;
00390 Eigen::Matrix<double, 6, 1> g = M.transpose() * err;
00391 Eigen::Matrix<double, 6, 6> MtM = M.transpose() * M;
00392 Eigen::Matrix<double, 6, 1> delta = - 2 * MtM.inverse() * g;
00393 #else
00394
00395
00396
00397 Eigen::Matrix<double, 6, 1> g = M.transpose() * err;
00398 Eigen::Matrix<double, 6, 6> MtM = M.transpose() * M;
00399 Eigen::Matrix<double, 6, 1> delta = - MtM.inverse() * g;
00400 #endif
00401
00402
00403 Eigen::Matrix<double, 6, 1> next_params = estimate_vec + delta;
00404 Eigen::Isometry3d next_estimate = isometryFromParams(next_params);
00405
00406
00407 computeReprojectionError(target_points, ref_projections, xyz_c_to_uvw_c, next_estimate,
00408 &err, 0);
00409 computeReprojectionError(ref_points, target_projections, xyz_c_to_uvw_c, next_estimate.inverse(),
00410 &err, num_points*2);
00411 double sse = err.cwiseProduct(err).sum();
00412
00413
00414
00415 if(sse > final_sse)
00416 break;
00417
00418
00419 estimate_vec = next_params;
00420 final_sse = sse;
00421 *result = next_estimate;
00422
00423
00424 if(fabs(delta(0)) < 0.0001 &&
00425 fabs(delta(1)) < 0.0001 &&
00426 fabs(delta(2)) < 0.0001 &&
00427 fabs(delta(3)) < (0.01 * M_PI/180) &&
00428 fabs(delta(4)) < (0.01 * M_PI/180) &&
00429 fabs(delta(5)) < (0.01 * M_PI/180))
00430 break;
00431
00432 dbg("T%-2d: [%7.3f %7.3f %7.3f] R: [%7.3f %7.3f %7.3f] E: %f\n",
00433 iter_num+1,
00434 estimate_vec(0), estimate_vec(1), estimate_vec(2),
00435 estimate_vec(3), estimate_vec(4), estimate_vec(5), final_sse);
00436 }
00437 dbg("T--: [%7.3f %7.3f %7.3f] R: [%7.3f %7.3f %7.3f] E: %f\n",
00438 estimate_vec(0), estimate_vec(1), estimate_vec(2),
00439 estimate_vec(3), estimate_vec(4), estimate_vec(5), final_sse);
00440
00441
00442
00443
00444
00445
00446
00447
00448 if(result_covariance) {
00449 computeProjectionJacobian(estimate_vec, fx, px, py, target_points, &M, 0);
00450 computeReverseProjectionJacobian(estimate_vec, fx, px, py, ref_points, &M, num_points*2);
00451 #ifdef USE_ESM
00452 M += M_0;
00453 Eigen::Matrix<double, 6, 6> MtM_inv = (M.transpose() * M).inverse();
00454 *result_covariance = 4 * MtM_inv;
00455 #else
00456 Eigen::Matrix<double, 6, 6> MtM_inv = (M.transpose() * M).inverse();
00457 *result_covariance = MtM_inv;
00458 #endif
00459 }
00460 }
00461
00462 }