diff --git a/include/vision/processor/processor_visual_odometry.h b/include/vision/processor/processor_visual_odometry.h index a7cde6c38636e7c6b24a4e5cba8b6b827f950f6e..9c6fd947338f3a2f9513fa800cf0c22a4d23254f 100644 --- a/include/vision/processor/processor_visual_odometry.h +++ b/include/vision/processor/processor_visual_odometry.h @@ -258,12 +258,15 @@ class ProcessorVisualOdometry : public ProcessorTracker /** * \brief Emplace a landmark corresponding to a track and initialize it with triangulation. * \param _feature_ptr a pointer to the feature used to create the new landmark + * \param _track_kf track with only features associated to keyframes that maye be used for initialisation * \return a pointer to the created landmark. If null, the triangulation failed due to low parallax * * Implementation: try to triangulate a new landmark based on previous frames estimates. * Apply a numerical test to asses if parallax is enough. */ - LandmarkBasePtr emplaceLandmarkTriangulation(FeatureBasePtr _feature_ptr); + LandmarkBasePtr emplaceLandmarkTriangulation(FeatureBasePtr _feature_ptr, Track _track_kf); + + Eigen::Isometry3d getTcw(TimeStamp _ts) const; /** \brief Advance the incoming Capture to become the last. * diff --git a/src/processor/processor_visual_odometry.cpp b/src/processor/processor_visual_odometry.cpp index cdefc35d32c1cb88726f28700afaff35db4f56ec..a8e398289a1bb892cac30fade2b081ea2106088c 100644 --- a/src/processor/processor_visual_odometry.cpp +++ b/src/processor/processor_visual_odometry.cpp @@ -25,6 +25,8 @@ #include "vision/processor/processor_visual_odometry.h" #include <opencv2/imgproc.hpp> +#include <opencv2/calib3d.hpp> +#include <opencv2/core/eigen.hpp> #include <chrono> #include <ctime> @@ -345,11 +347,19 @@ void ProcessorVisualOdometry::establishFactors() else if(track_matrix_.trackSize(feat->trackId()) >= params_visual_odometry_->min_track_length_for_landmark) { WOLF_INFO(" NEW valid track \\o/") - LandmarkBasePtr lmk = emplaceLandmarkNaive(feat_pi); - lmk->setId(feat_pi->trackId()); + Track track_kf = track_matrix_.trackAtKeyframes(feat->trackId()); + + LandmarkBasePtr lmk = emplaceLandmarkTriangulation(feat_pi, track_kf); + if (lmk == nullptr){ + continue; + } // Add factors from all KFs of this track to the new lmk - Track track_kf = track_matrix_.trackAtKeyframes(feat->trackId()); + WOLF_INFO("\n\nHEY") + WOLF_INFO(feat_pi->getFrame()->getStateVector().transpose()) + WOLF_INFO(lmk->getStateVector().transpose()) + WOLF_INFO(getSensor()->getStateVector().transpose()) + for (auto feat_kf: track_kf) { LandmarkHpPtr lmk_hp = std::dynamic_pointer_cast<LandmarkHp>(lmk); @@ -364,6 +374,76 @@ void ProcessorVisualOdometry::establishFactors() return; } + +LandmarkBasePtr ProcessorVisualOdometry::emplaceLandmarkTriangulation(FeatureBasePtr _feat, Track _track_kf) +{ + // at least 2 KF needed to triangulate + if (_track_kf.size() < 2) + { + return nullptr; + } + + // heuristic: use oldest feature/KF to triangulate with respect to current time + FeaturePointImagePtr feat_pi1 = std::static_pointer_cast<FeaturePointImage>(_track_kf.begin()->second); + // Feature at current time + FeaturePointImagePtr feat_pi2 = std::static_pointer_cast<FeaturePointImage>(_feat); + + cv::Vec2d pix1, pix2; + // WOLF_INFO("TOTO") + cv::eigen2cv(feat_pi1->getMeasurement(), pix1); + cv::eigen2cv(feat_pi2->getMeasurement(), pix2); + // WOLF_INFO(pix1, pix2) + + // create 3x4 projection matrices [K|0] * Tcw + Matrix4d Tcw1 = getTcw(feat_pi1->getCapture()->getTimeStamp()).matrix(); + Matrix4d Tcw2 = getTcw(feat_pi2->getCapture()->getTimeStamp()).matrix(); + Eigen::Matrix<double, 3, 4> P1 = sen_cam_->getProjectionMatrix() * Tcw1; + Eigen::Matrix<double, 3, 4> P2 = sen_cam_->getProjectionMatrix() * Tcw2; + cv::Mat P1_cv, P2_cv; + cv::eigen2cv(P1, P1_cv); + cv::eigen2cv(P2, P2_cv); + // WOLF_INFO(P1) + // WOLF_INFO(P1_cv) + + // perform triangulation of one landmark + cv::Vec4d ptcv_w; // output: homogeneous landmark point expressed in world frame + cv::triangulatePoints(P1_cv, P2_cv, pix1, pix2, ptcv_w); + // WOLF_INFO("YAY: ", ptcv_w) + + + ///////////////////////////////////////////////////////// + // check that triangulation was done with enough parallax + ///////////////////////////////////////////////////////// + bool triangulation_is_a_success = true; // Not implemented yet + if(!triangulation_is_a_success) + { + return nullptr; + } + + // normalize to make equivalent to a unit quaternion + Eigen::Vector4d pt_c; + cv::cv2eigen(ptcv_w, pt_c); + + // HACK: to avoid "nans" in residal, set Z = 1 + pt_c(2) = 1 * pt_c(3); + + // Normilization necessary since homogeneous point stateblock is supposed to be unitary + pt_c.normalize(); + + WOLF_INFO("New lmk: ", pt_c.transpose()) + auto lmk_hp_ptr = LandmarkBase::emplace<LandmarkHp>(getProblem()->getMap(), + pt_c, + feat_pi2->getKeyPoint().getDescriptor()); + + // Set all IDs equal to track ID + size_t track_id = _feat->trackId(); + lmk_hp_ptr->setId(track_id); + _feat->setLandmarkId(track_id); + + return lmk_hp_ptr; +} + + LandmarkBasePtr ProcessorVisualOdometry::emplaceLandmarkNaive(FeatureBasePtr _feat) { // Taken from processor_bundle_adjustment @@ -410,6 +490,24 @@ LandmarkBasePtr ProcessorVisualOdometry::emplaceLandmarkNaive(FeatureBasePtr _fe } + +Eigen::Isometry3d ProcessorVisualOdometry::getTcw(TimeStamp _ts) const +{ + // get robot position and orientation at capture's timestamp + const auto& state = getProblem()->getState(_ts, "PO"); + const auto& pos = state.at('P'); + const auto& ori = state.at('O'); + + // compute world-to-camera transform + Eigen::Isometry3d T_w_r = Translation3d(pos) * Quaterniond(ori.data()); + Eigen::Isometry3d T_r_c = Translation3d(getSensor()->getP()->getState()) + * Quaterniond(getSensor()->getP()->getState().data()); + + // invert transform to camera-to-world and return + return (T_w_r * T_r_c).inverse(); +} + + void ProcessorVisualOdometry::postProcess() { // Delete tracks with no keyframes