PageRenderTime 51ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/navigation/odometry/libfovis/libfovis/motion_estimation.cpp

https://gitlab.com/ginocorrales/Kinect-Mapper
C++ | 671 lines | 510 code | 91 blank | 70 comment | 65 complexity | a1430b0adc7ce4aba16dc414c5e68a35 MD5 | raw file
Possible License(s): GPL-3.0
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <assert.h>
  5. #include <math.h>
  6. #include <iostream>
  7. #include <iomanip>
  8. #include "motion_estimation.hpp"
  9. #include "absolute_orientation_horn.hpp"
  10. #include "depth_source.hpp"
  11. #include "refine_motion_estimate.hpp"
  12. #include "tictoc.hpp"
  13. #include "internal_utils.hpp"
  14. #include "sad.hpp"
  15. #include "visual_odometry.hpp"
  16. #include "refine_feature_match.hpp"
  17. #include "stereo_depth.hpp"
  18. #define USE_HORN_ABSOLUTE_ORIENTATION
  19. #define USE_ROBUST_STEREO_COMPATIBILITY
  20. #define USE_BIDIRECTIONAL_REFINEMENT
  21. #define dbg(...) fprintf(stderr, __VA_ARGS__)
  22. #define dump(var) (std::cerr<<" "#var<<" =[\n"<< std::setprecision (12)<<var<<"]"<<std::endl)
  23. namespace fovis {
  24. const char* MotionEstimateStatusCodeStrings[] = {
  25. "NO_DATA",
  26. "SUCCESS",
  27. "INSUFFICIENT_INLIERS",
  28. "OPTIMIZATION_FAILURE",
  29. "REPROJECTION_ERROR"
  30. };
  31. MotionEstimator::MotionEstimator(const Rectification* rectification,
  32. const VisualOdometryOptions& options)
  33. {
  34. _rectification = rectification;
  35. _ref_frame = NULL;
  36. _target_frame = NULL;
  37. _num_inliers = 0;
  38. _motion_estimate = new Eigen::Isometry3d();
  39. _motion_estimate_covariance = new Eigen::MatrixXd();
  40. _motion_estimate->setIdentity();
  41. _estimate_status = NO_DATA;
  42. _motion_estimate_covariance->setIdentity(6,6);
  43. _matches = NULL;
  44. _num_matches = 0;
  45. _matches_capacity = 0;
  46. _num_tracks = 0;
  47. _num_frames = 0;
  48. // extract options
  49. VisualOdometryOptions defaults = VisualOdometry::getDefaultOptions();
  50. _inlier_max_reprojection_error = optionsGetDoubleOrFromDefault(options, "inlier-max-reprojection-error", defaults);
  51. _clique_inlier_threshold = optionsGetDoubleOrFromDefault(options, "clique-inlier-threshold", defaults);
  52. _min_features_for_valid_motion_estimate = optionsGetIntOrFromDefault(options, "min-features-for-estimate", defaults);
  53. _max_mean_reprojection_error = optionsGetDoubleOrFromDefault(options, "max-mean-reprojection-error", defaults);
  54. _use_subpixel_refinement = optionsGetBoolOrFromDefault(options, "use-subpixel-refinement", defaults);
  55. _max_feature_motion = optionsGetDoubleOrFromDefault(options, "feature-search-window", defaults);
  56. _update_target_features_with_refined = _use_subpixel_refinement && optionsGetBoolOrFromDefault(options, "update-target-features-with-refined", defaults);
  57. }
  58. MotionEstimator::~MotionEstimator()
  59. {
  60. _ref_frame = NULL;
  61. _target_frame = NULL;
  62. delete[] _matches;
  63. _num_matches = 0;
  64. _matches_capacity = 0;
  65. _num_inliers = 0;
  66. delete _motion_estimate_covariance;
  67. delete _motion_estimate;
  68. _motion_estimate_covariance = NULL;
  69. _motion_estimate = NULL;
  70. _estimate_status = NO_DATA;
  71. }
  72. void
  73. MotionEstimator::estimateMotion(OdometryFrame* ref_frame,
  74. OdometryFrame* target_frame,
  75. DepthSource* depth_source,
  76. const Eigen::Isometry3d &init_motion_est,
  77. const Eigen::MatrixXd &init_motion_cov)
  78. {
  79. tictoc("estimateMotion");
  80. _depth_source = depth_source;
  81. _ref_frame = ref_frame;
  82. _target_frame = target_frame;
  83. *_motion_estimate = init_motion_est;
  84. *_motion_estimate_covariance = init_motion_cov;
  85. // dbg("Process frame\n");
  86. tictoc("matchFeatures_all_levels");
  87. _num_matches = 0;
  88. int max_num_matches = std::min(_ref_frame->getNumKeypoints(), _target_frame->getNumKeypoints());
  89. if (max_num_matches > _matches_capacity) {
  90. delete[] _matches;
  91. _matches_capacity = static_cast<int>(max_num_matches * 1.2);
  92. _matches = new FeatureMatch[_matches_capacity];
  93. }
  94. int num_levels = _ref_frame->getNumLevels();
  95. for (int level_ind = 0; level_ind < num_levels; level_ind++) {
  96. PyramidLevel* ref_level = _ref_frame->getLevel(level_ind);
  97. PyramidLevel* target_level = _target_frame->getLevel(level_ind);
  98. matchFeatures(ref_level, target_level);
  99. }
  100. if (_use_subpixel_refinement) {
  101. depth_source->refineXyz(_matches, _num_matches, target_frame);
  102. }
  103. tictoc("matchFeatures_all_levels");
  104. // assign a 'local' match id for use in inlier detection
  105. for (int i=0; i < _num_matches; ++i) {
  106. _matches[i].id = i;
  107. }
  108. tictoc("computeMaximallyConsistentClique");
  109. computeMaximallyConsistentClique();
  110. tictoc("computeMaximallyConsistentClique");
  111. // first pass at motion estimation
  112. tictoc("estimateRigidBodyTransform1");
  113. #ifdef USE_ROBUST_STEREO_COMPATIBILITY
  114. // Horn/SVD doesn't do too well when using robust stereo metric
  115. _estimate_status = SUCCESS;
  116. #else
  117. estimateRigidBodyTransform();
  118. #endif
  119. tictoc("estimateRigidBodyTransform1");
  120. // refine motion estimate by minimizing reprojection error
  121. tictoc("refineMotionEstimate");
  122. refineMotionEstimate();
  123. tictoc("refineMotionEstimate");
  124. // compute inlier reprojection error
  125. tictoc("computeReprojectionError");
  126. computeReprojectionError();
  127. tictoc("computeReprojectionError");
  128. // remove features with a high reprojection error from the inlier set
  129. _num_reprojection_failures = 0;
  130. for (int m_ind = 0; m_ind < _num_matches; m_ind++) {
  131. FeatureMatch& match = _matches[m_ind];
  132. if (!match.inlier)
  133. continue;
  134. if (match.reprojection_error > _inlier_max_reprojection_error) {
  135. match.inlier = false;
  136. _num_inliers--;
  137. _num_reprojection_failures++;
  138. }
  139. }
  140. // prevent propagation of track id's through outlier matches.
  141. for (int m_ind = 0; m_ind < _num_matches; m_ind++) {
  142. FeatureMatch& match = _matches[m_ind];
  143. if (!match.inlier) {
  144. match.target_keypoint->track_id = -1;
  145. }
  146. }
  147. // second motion estimate refinement
  148. tictoc("refineMotionEstimate");
  149. refineMotionEstimate();
  150. tictoc("refineMotionEstimate");
  151. // compute new reprojection error
  152. tictoc("computeReprojectionError");
  153. computeReprojectionError();
  154. tictoc("computeReprojectionError");
  155. if (_mean_reprojection_error > _max_mean_reprojection_error) {
  156. _estimate_status = REPROJECTION_ERROR;
  157. }
  158. #if 0
  159. switch (_estimate_status) {
  160. case SUCCESS:
  161. dbg("Inliers: %4d Rep. fail: %4d Matches: %4d Feats: %4d Mean err: %5.2f ",
  162. _num_inliers,
  163. _num_reprojection_failures,
  164. _num_matches,
  165. (int) _target_frame->getNumKeypoints(),
  166. _mean_reprojection_error);
  167. // print_isometry(_motion_estimate);
  168. dbg("\n");
  169. break;
  170. case REPROJECTION_ERROR:
  171. dbg("Excessive reprojection error (%f).\n", _mean_reprojection_error);
  172. break;
  173. case OPTIMIZATION_FAILURE:
  174. dbg("Unable to solve for rigid body transform\n");
  175. break;
  176. case INSUFFICIENT_INLIERS:
  177. dbg("Insufficient inliers\n");
  178. break;
  179. default:
  180. dbg("Unknown error (this should never happen)\n");
  181. break;
  182. }
  183. #endif
  184. if (_update_target_features_with_refined) {
  185. for (int m_ind = 0; m_ind < _num_matches; m_ind++) {
  186. FeatureMatch& match = _matches[m_ind];
  187. match.target_keypoint->copyFrom(match.refined_target_keypoint);
  188. }
  189. }
  190. _num_frames++;
  191. tictoc("estimateMotion");
  192. }
  193. void MotionEstimator::sanityCheck() const
  194. {
  195. #ifndef NDEBUG
  196. for (int m_ind = 0; m_ind < _num_matches; m_ind++) {
  197. const FeatureMatch& match = _matches[m_ind];
  198. const KeypointData* ref_kp = match.ref_keypoint;
  199. const KeypointData* target_kp = match.target_keypoint;
  200. assert(ref_kp->pyramid_level >= 0 &&
  201. ref_kp->pyramid_level < _ref_frame->getNumLevels());
  202. assert(target_kp->pyramid_level >= 0 &&
  203. target_kp->pyramid_level < _target_frame->getNumLevels());
  204. const PyramidLevel * ref_level = _ref_frame->getLevel(ref_kp->pyramid_level);
  205. const PyramidLevel * target_level = _target_frame->getLevel(target_kp->pyramid_level);
  206. assert(ref_kp->kp.u >= 0 && ref_kp->kp.v >= 0 &&
  207. ref_kp->kp.u < ref_level->getWidth() &&
  208. ref_kp->kp.v < ref_level->getHeight());
  209. assert(target_kp->kp.u >= 0 && target_kp->kp.v >= 0 &&
  210. target_kp->kp.u < target_level->getWidth() &&
  211. target_kp->kp.v < target_level->getHeight());
  212. }
  213. #endif
  214. }
  215. void MotionEstimator::matchFeatures(PyramidLevel* ref_level, PyramidLevel* target_level)
  216. {
  217. // get the camera projection matrix
  218. Eigen::Matrix<double, 3, 4> xyz_c_to_uvw_c =
  219. _rectification->getRectifiedCameraParameters().toProjectionMatrix();
  220. // get the ref_to_target isometry cuz of order of loops
  221. Eigen::Isometry3d ref_to_target = _motion_estimate->inverse();
  222. Eigen::Matrix<double, 3, 4> reproj_mat = xyz_c_to_uvw_c * ref_to_target.matrix();
  223. int num_ref_features = ref_level->getNumKeypoints();
  224. int num_target_features = target_level->getNumKeypoints();
  225. std::vector<std::vector<int> > candidates(num_ref_features);
  226. for (int ref_ind = 0; ref_ind < num_ref_features; ref_ind++) {
  227. // constrain the matching to a search-region based on the
  228. // current motion estimate
  229. const Eigen::Vector4d& ref_xyzw = ref_level->getKeypointXYZW(ref_ind);
  230. assert(!isnan(ref_xyzw(0)) && !isnan(ref_xyzw(1)) &&
  231. !isnan(ref_xyzw(2)) && !isnan(ref_xyzw(3)));
  232. Eigen::Vector3d reproj_uv1 = reproj_mat * ref_xyzw;
  233. reproj_uv1 /= reproj_uv1(2);
  234. Eigen::Vector2d ref_uv(ref_level->getKeypointRectBaseU(ref_ind),
  235. ref_level->getKeypointRectBaseV(ref_ind));
  236. std::vector<int>& ref_candidates(candidates[ref_ind]);
  237. for (int target_ind = 0; target_ind < num_target_features; target_ind++) {
  238. Eigen::Vector2d target_uv(target_level->getKeypointRectBaseU(target_ind),
  239. target_level->getKeypointRectBaseV(target_ind));
  240. //TODO: Should adapt based on covariance instead of constant sized window!
  241. //Eigen::Vector2d err = target_uv - ref_uv; //ignore motion est
  242. Eigen::Vector2d err = target_uv - reproj_uv1.head<2>();
  243. if (err.norm() < _max_feature_motion) {
  244. ref_candidates.push_back(target_ind);
  245. }
  246. }
  247. }
  248. int inserted_matches = 0;
  249. _matcher.matchFeatures(ref_level, target_level, candidates,
  250. &(_matches[_num_matches]), &inserted_matches);
  251. int old_num_matches = _num_matches;
  252. _num_matches = old_num_matches + inserted_matches;
  253. if (_use_subpixel_refinement) {
  254. for (int n=old_num_matches; n < _num_matches; ++n) {
  255. //std::cerr << "n = " << n << std::endl;
  256. FeatureMatch& match(_matches[n]);
  257. const KeypointData* ref_kpdata(match.ref_keypoint);
  258. const KeypointData* target_kpdata(match.target_keypoint);
  259. Eigen::Vector2d ref_uv(ref_kpdata->kp.u, ref_kpdata->kp.v);
  260. Eigen::Vector2d init_target_uv(target_kpdata->kp.u, target_kpdata->kp.v);
  261. Eigen::Vector2d final_target_uv;
  262. float delta_sse = -1;
  263. refineFeatureMatch(ref_level, target_level, ref_uv, init_target_uv,
  264. &final_target_uv, &delta_sse);
  265. double ds = (init_target_uv - final_target_uv).norm();
  266. if (ds < 1e-9) {
  267. match.refined_target_keypoint.copyFrom(*match.target_keypoint);
  268. match.status = MATCH_OK;
  269. } else if (ds > 1.5) {
  270. // TODO make threshold a parameter. Also, reject or keep?
  271. match.refined_target_keypoint.copyFrom(*match.target_keypoint);
  272. match.status = MATCH_OK;
  273. } else {
  274. match.refined_target_keypoint.kp.u = final_target_uv(0);
  275. match.refined_target_keypoint.kp.v = final_target_uv(1);
  276. match.refined_target_keypoint.base_uv =
  277. final_target_uv * (1 << target_kpdata->pyramid_level);
  278. _rectification->rectifyBilinearLookup(match.refined_target_keypoint.base_uv,
  279. &match.refined_target_keypoint.rect_base_uv);
  280. match.status = MATCH_NEEDS_DEPTH_REFINEMENT;
  281. }
  282. }
  283. }
  284. // label matches with their track_id
  285. for (int n=old_num_matches; n < _num_matches; ++n) {
  286. FeatureMatch& match(_matches[n]);
  287. KeypointData* ref_kpdata(match.ref_keypoint);
  288. KeypointData* target_kpdata(match.target_keypoint);
  289. if (ref_kpdata->track_id < 0) {
  290. //ref wasn't already part of a track
  291. ref_kpdata->track_id = _num_tracks++;
  292. }
  293. target_kpdata->track_id = ref_kpdata->track_id;
  294. match.track_id = ref_kpdata->track_id;
  295. }
  296. }
  297. // used for sorting feature matches.
  298. static bool consistencyCompare(const FeatureMatch &ca, const FeatureMatch& cb)
  299. {
  300. return ca.compatibility_degree > cb.compatibility_degree;
  301. }
  302. #ifdef USE_ROBUST_STEREO_COMPATIBILITY
  303. // Robust Stereo Compatibility Check from:
  304. // Heiko Hirschmuller, Peter R. Innocent and Jon M. Garibaldi
  305. // "Fast, Unconstrained Camera Motion Estimation from Stereo without Tracking
  306. // and Robust Statistics"
  307. // Paper recommends a De (compatibility thresh) of ~0.2 pixels
  308. static inline double sqr(double x) { return x * x; }
  309. static inline
  310. double robustStereoCompatibility_computeDL(double L,
  311. const Eigen::Vector3d & p1,
  312. const Eigen::Vector3d & p2,
  313. double t, double f, double De)
  314. {
  315. double A = sqr((p1(0) - p2(0)) * (t - p1(0)) - (p1(1) - p2(1)) * p1(1) - (p1(2) - p2(2)) * p1(2));
  316. double B = sqr((p1(0) - p2(0)) * p1(0) + (p1(1) - p2(1)) * p1(1) + (p1(2) - p2(2)) * p1(2));
  317. double C = 0.5 * sqr(t * (p1(1) - p2(1)));
  318. double D = sqr((p1(0) - p2(0)) * (t - p2(0)) - (p1(1) - p2(1)) * p2(1) - (p1(2) - p2(2)) * p2(2));
  319. double E = sqr((p1(0) - p2(0)) * p2(0) + (p1(1) - p2(1)) * p2(1) + (p1(2) - p2(2)) * p2(2));
  320. double F = 0.5 * sqr(t * (p1(1) - p2(1)));
  321. return De / (L * f * t) * sqrt(sqr(p1(2)) * (A + B + C) + sqr(p2(2)) * (D + E + F));
  322. }
  323. static inline bool
  324. robustStereoCompatibility(const Eigen::Vector3d & C1,
  325. const Eigen::Vector3d & C2,
  326. const Eigen::Vector3d & P1,
  327. const Eigen::Vector3d & P2,
  328. double baseline,
  329. double focal_length,
  330. double De)
  331. {
  332. //compute the L quantities (ie dist between pairs of points)
  333. double L1 = (C2-C1).norm();
  334. double L2 = (P2-P1).norm();
  335. //compute the DLs (delta L)
  336. double DL1 = robustStereoCompatibility_computeDL(L1, C1, C2, baseline, focal_length, De);
  337. double DL2 = robustStereoCompatibility_computeDL(L2, P1, P2, baseline, focal_length, De);
  338. return (fabs(L1-L2) <= 3*sqrt(sqr(DL1)+sqr(DL2)));
  339. }
  340. #endif
  341. void MotionEstimator::computeMaximallyConsistentClique()
  342. {
  343. if (!_num_matches)
  344. return;
  345. for (int m_ind = 0; m_ind < _num_matches; m_ind++) {
  346. FeatureMatch& match = _matches[m_ind];
  347. match.consistency_vec.resize(_num_matches);
  348. }
  349. #ifdef USE_ROBUST_STEREO_COMPATIBILITY
  350. double baseline = _depth_source->getBaseline();
  351. bool have_baseline = baseline > 0;
  352. // XXX this is not actually correct for kinect/primesense
  353. const CameraIntrinsicsParameters& rparams = _rectification->getRectifiedCameraParameters();
  354. double stereo_focal_length = rparams.fx;
  355. #endif
  356. // For each pair of matches, compute the distance between features in the
  357. // reference frame, and the distance between features in the target frame.
  358. // Rigid body transformations (isometries) preserve distance, so the distance
  359. // should not change significantly if the two feature matches are
  360. // "compatible".
  361. //
  362. // If the depth comes from a stereo camera, then apply a consistency metric that
  363. // allows for disparity error resulting from the stereo baseline.
  364. // FIXME using both homogeneous and cartesian coordinates is a bit gross here...
  365. for (int m_ind = 0; m_ind < _num_matches; m_ind++) {
  366. FeatureMatch& match = _matches[m_ind];
  367. const Eigen::Vector4d& ref_xyzw_1 = match.ref_keypoint->xyzw;
  368. const Eigen::Vector4d& target_xyzw_1 = match.refined_target_keypoint.xyzw;
  369. const Eigen::Vector3d& ref_xyz_1 = match.ref_keypoint->xyz;
  370. const Eigen::Vector3d& target_xyz_1 = match.refined_target_keypoint.xyz;
  371. assert(match.id == m_ind);
  372. // are the features points at infinity?
  373. bool ref_infinity = ref_xyzw_1.w() < 1e-9;
  374. bool target_infinity = target_xyzw_1.w() < 1e-9;
  375. for (int m_ind2 = m_ind + 1; m_ind2 < _num_matches; m_ind2++) {
  376. FeatureMatch& match2 = _matches[m_ind2];
  377. assert(match2.id == m_ind2);
  378. const Eigen::Vector4d& ref_xyzw_2 = match2.ref_keypoint->xyzw;
  379. const Eigen::Vector4d& target_xyzw_2 = match2.refined_target_keypoint.xyzw;
  380. const Eigen::Vector3d& ref_xyz_2 = match2.ref_keypoint->xyz;
  381. const Eigen::Vector3d& target_xyz_2 = match2.refined_target_keypoint.xyz;
  382. bool consistent;
  383. // special case: if either of the features are points at infinity, then
  384. // we can't compare their distances.
  385. if((ref_infinity && ref_xyzw_2.w() < 1e-9) ||
  386. (target_infinity && target_xyzw_2.w() < 1e-9)) {
  387. consistent = true;
  388. } else {
  389. #ifdef USE_ROBUST_STEREO_COMPATIBILITY
  390. if (have_baseline) {
  391. consistent = robustStereoCompatibility(ref_xyz_1, ref_xyz_2,
  392. target_xyz_1 ,target_xyz_2,
  393. baseline, stereo_focal_length,
  394. _clique_inlier_threshold);
  395. } else {
  396. double ref_dist = (ref_xyz_2 - ref_xyz_1).norm();
  397. double target_dist = (target_xyz_2 - target_xyz_1).norm();
  398. consistent = fabs(ref_dist - target_dist) < _clique_inlier_threshold;
  399. }
  400. #else
  401. double ref_dist = (ref_xyz_2 - ref_xyz_1).norm();
  402. double target_dist = (target_xyz_2 - target_xyz_1).norm();
  403. consistent = fabs(ref_dist - target_dist) < _clique_inlier_threshold;
  404. #endif
  405. }
  406. if (consistent) {
  407. match.consistency_vec[match2.id] = 1;
  408. match.compatibility_degree++;
  409. match2.consistency_vec[match.id] = 1;
  410. match2.compatibility_degree++;
  411. }
  412. }
  413. }
  414. // sort the features based on their consistency with other features
  415. std::sort(_matches, _matches+_num_matches, consistencyCompare);
  416. // pick the best feature and mark it as an inlier
  417. FeatureMatch &best_candidate = _matches[0];
  418. best_candidate.in_maximal_clique = true;
  419. best_candidate.inlier = true;
  420. _num_inliers = 1;
  421. // start a list of quick-reject features (features that are known to be
  422. // inconsistent with any of the existing inliers)
  423. int reject[_num_matches];
  424. std::fill(reject, reject+_num_matches, 0);
  425. for (int m_ind = 1; m_ind < _num_matches; m_ind++) {
  426. int other_id = _matches[m_ind].id;
  427. if (!best_candidate.consistency_vec[other_id])
  428. reject[other_id] = 1;
  429. }
  430. // now start adding inliers that are consistent with all existing
  431. // inliers
  432. for (int m_ind = 1; m_ind < _num_matches; m_ind++) {
  433. FeatureMatch& cand = _matches[m_ind];
  434. // if this candidate is consistent with fewer than the existing number
  435. // of inliers, then immediately stop iterating since no more features can
  436. // be inliers
  437. if (cand.compatibility_degree < _num_inliers)
  438. break;
  439. // skip if it's a quick reject
  440. if (reject[cand.id])
  441. continue;
  442. cand.in_maximal_clique = true;
  443. cand.inlier = true;
  444. _num_inliers++;
  445. // mark some more features for rejection
  446. for (int r_ind = m_ind + 1; r_ind < _num_matches; r_ind++) {
  447. int other_id = _matches[r_ind].id;
  448. if (!reject[other_id] && !cand.consistency_vec[other_id])
  449. reject[other_id] = 1;
  450. }
  451. }
  452. }
  453. void MotionEstimator::estimateRigidBodyTransform()
  454. {
  455. _motion_estimate->setIdentity();
  456. if (_num_inliers < _min_features_for_valid_motion_estimate) {
  457. _estimate_status = INSUFFICIENT_INLIERS;
  458. return;
  459. }
  460. // gather all the inliers into two big matrices
  461. Eigen::MatrixXd target_xyz(3, _num_inliers);
  462. Eigen::MatrixXd ref_xyz(3, _num_inliers);
  463. int i = 0;
  464. for (int m_ind = 0; m_ind < _num_matches; m_ind++) {
  465. FeatureMatch& match = _matches[m_ind];
  466. if (!match.inlier)
  467. continue;
  468. // FIXME so that this works with points at infinity
  469. target_xyz.col(i) = match.refined_target_keypoint.xyzw.head<3>() /
  470. match.refined_target_keypoint.xyzw(3) ;
  471. ref_xyz.col(i) = match.ref_keypoint->xyzw.head<3>() /
  472. match.ref_keypoint->xyzw(3);
  473. i++;
  474. }
  475. #ifdef USE_HORN_ABSOLUTE_ORIENTATION
  476. if (0 != absolute_orientation_horn(target_xyz, ref_xyz, _motion_estimate)) {
  477. _estimate_status = OPTIMIZATION_FAILURE;
  478. return;
  479. }
  480. #else
  481. Eigen::Matrix4d ume_estimate = Eigen::umeyama(target_xyz, ref_xyz);
  482. *_motion_estimate = Eigen::Isometry3d(ume_estimate);
  483. #endif
  484. _estimate_status = SUCCESS;
  485. }
  486. void MotionEstimator::refineMotionEstimate()
  487. {
  488. if (_num_inliers < _min_features_for_valid_motion_estimate) {
  489. _estimate_status = INSUFFICIENT_INLIERS;
  490. return;
  491. }
  492. #ifdef USE_BIDIRECTIONAL_REFINEMENT
  493. // gather all the inliers into matrices
  494. Eigen::MatrixXd target_xyz(4,_num_inliers);
  495. Eigen::MatrixXd target_projections(2,_num_inliers);
  496. Eigen::MatrixXd ref_xyz(4,_num_inliers);
  497. Eigen::MatrixXd ref_projections(2,_num_inliers);
  498. int i = 0;
  499. for (int m_ind = 0; m_ind < _num_matches; m_ind++) {
  500. FeatureMatch& match = _matches[m_ind];
  501. if (!match.inlier)
  502. continue;
  503. target_xyz.col(i) = match.refined_target_keypoint.xyzw;
  504. target_projections.col(i) = match.refined_target_keypoint.rect_base_uv;
  505. ref_xyz.col(i) = match.ref_keypoint->xyzw;
  506. ref_projections.col(i) = match.ref_keypoint->rect_base_uv;
  507. i++;
  508. }
  509. const CameraIntrinsicsParameters& rparams = _rectification->getRectifiedCameraParameters();
  510. // refine motion estimate by minimizing bidirectional reprojection error.
  511. // bidirectional reprojection error is the error of the target features
  512. // projected into the reference image, along with the reference features
  513. // projected into the target image
  514. refineMotionEstimateBidirectional(ref_xyz,
  515. ref_projections,
  516. target_xyz,
  517. target_projections,
  518. rparams.fx,
  519. rparams.cx,
  520. rparams.cy,
  521. *_motion_estimate,
  522. 6,
  523. _motion_estimate,
  524. _motion_estimate_covariance);
  525. // TODO regularize the motion estimate covariance.
  526. #else
  527. // gather all the inliers into matrices
  528. Eigen::MatrixXd target_xyz(4,_num_inliers);
  529. Eigen::MatrixXd ref_projections(2,_num_inliers);
  530. int i = 0;
  531. for (int m_ind = 0; m_ind < _num_matches; m_ind++) {
  532. FeatureMatch& match = _matches[m_ind];
  533. if (!match.inlier)
  534. continue;
  535. target_xyz.col(i) = match.refined_target_keypoint.xyzw;
  536. ref_projections.col(i) = match.ref_keypoint->rect_base_uv;
  537. i++;
  538. }
  539. const CameraIntrinsicsParameters& rparams = _rectification->getRectifiedCameraParameters();
  540. // refine motion estimate by minimizing reprojection error of the
  541. // target features projected into the reference image.
  542. *_motion_estimate = refineMotionEstimate(target_xyz,
  543. ref_projections,
  544. rparams->fx,
  545. rparams->cx,
  546. rparams->cy,
  547. *_motion_estimate,
  548. 6);
  549. #endif
  550. }
  551. void MotionEstimator::computeReprojectionError()
  552. {
  553. if (_estimate_status != SUCCESS)
  554. return;
  555. Eigen::Matrix<double, 3, 4> proj_matrix =
  556. _rectification->getRectifiedCameraParameters().toProjectionMatrix();
  557. Eigen::Matrix<double, 3, 4> reproj_matrix =
  558. proj_matrix * _motion_estimate->matrix();
  559. _mean_reprojection_error = 0;
  560. for (int m_ind = 0; m_ind < _num_matches; m_ind++) {
  561. FeatureMatch& match = _matches[m_ind];
  562. if (!match.inlier) {
  563. continue;
  564. }
  565. Eigen::Vector3d reproj_homogeneous =
  566. reproj_matrix * match.refined_target_keypoint.xyzw;
  567. Eigen::Vector2d reproj(reproj_homogeneous(0) / reproj_homogeneous(2),
  568. reproj_homogeneous(1) / reproj_homogeneous(2));
  569. Eigen::Vector2d err = match.ref_keypoint->rect_base_uv - reproj;
  570. match.reprojection_error = err.norm();
  571. // printf("%3d: %6.1f, %6.1f, %6.1f -> %6.1f, %6.1f -> %6.2f\n", m_ind,
  572. // transformed_xyzw(0), transformed_xyzw(1), transformed_xyzw(2),
  573. // reprojected_x, reprojected_y,
  574. // match.reprojection_error
  575. // );
  576. _mean_reprojection_error += match.reprojection_error;
  577. }
  578. _mean_reprojection_error /= _num_inliers;
  579. }
  580. }