diff --git a/include/vision/processor/processor_visual_odometry.h b/include/vision/processor/processor_visual_odometry.h index 76ce1a19f072d534c661f3f914f6f32acda9c4e2..84d3d6c396d668f6600e3e62fab32fd0c930bd5b 100644 --- a/include/vision/processor/processor_visual_odometry.h +++ b/include/vision/processor/processor_visual_odometry.h @@ -85,6 +85,7 @@ class ProcessorVisualOdometry : public ProcessorTracker // A few casted smart pointers CaptureImagePtr capture_image_last_; CaptureImagePtr capture_image_incoming_; + CaptureImagePtr capture_image_origin_; SensorCameraPtr sen_cam_; private: @@ -107,7 +108,6 @@ class ProcessorVisualOdometry : public ProcessorTracker int search_height_; int pyramid_level_; - // camera cv::Mat Kcv_; @@ -167,9 +167,13 @@ class ProcessorVisualOdometry : public ProcessorTracker */ void resetDerived() override; - /** \brief Implementation of pyramidal KLT + /** \brief Implementation of pyramidal KLT with openCV + */ + TracksMap kltTrack(cv::Mat img_prev, cv::Mat img_curr, KeyPointsMap &mwkps_prev, KeyPointsMap &mwkps_curr); + + /** \brief Implementation of 5 point algorithm with openCV, remove outliers from the tracks map */ - TracksMap klt_track(cv::Mat img_prev, cv::Mat img_curr, KeyPointsMap &mkps_prev, KeyPointsMap &mkps_curr); + bool computeEssential(KeyPointsMap mwkps_prev, KeyPointsMap mwkps_curr, TracksMap &tracks_prev_curr, cv::Mat &E); void setParams(const ParamsProcessorVisualOdometryPtr _params); diff --git a/src/processor/processor_visual_odometry.cpp b/src/processor/processor_visual_odometry.cpp index 977be27a93cb4a491d17e7a7449f0f2ae0b63ca0..0fa917f8e4299d6c85e2c584f583688ed0f21e55 100644 --- a/src/processor/processor_visual_odometry.cpp +++ b/src/processor/processor_visual_odometry.cpp @@ -58,6 +58,16 @@ void ProcessorVisualOdometry::configure(SensorBasePtr _sensor) Kcv_ = cv::Mat(3, 3, CV_32F, K.data()); } +TracksMap merge_tracks(TracksMap tracks_prev_curr, TracksMap tracks_curr_next){ + TracksMap tracks_prev_next; + for (auto &match : tracks_prev_curr){ + if (tracks_curr_next.count(match.second) != 0){ + tracks_prev_next[match.first] = tracks_curr_next[match.second]; + } + } + return tracks_prev_next; +} + void ProcessorVisualOdometry::preProcess() { // Get Capture @@ -88,8 +98,10 @@ void ProcessorVisualOdometry::preProcess() // TRACKING // Proceeed to tracking the previous features capture_image_last_ = std::static_pointer_cast<CaptureImage>(last_ptr_); + capture_image_origin_ = std::static_pointer_cast<CaptureImage>(origin_ptr_); cv::Mat img_last = capture_image_last_->getImage(); + KeyPointsMap mwkps_origin = capture_image_origin_->getKeyPoints(); KeyPointsMap mwkps_last = capture_image_last_->getKeyPoints(); KeyPointsMap mwkps_incoming; // init incoming @@ -103,10 +115,27 @@ void ProcessorVisualOdometry::preProcess() // - ... //////////////////////////////// + // Tracks between last and incoming + TracksMap tracks_last_incoming = kltTrack(img_last, img_incoming, mwkps_last, mwkps_incoming); + capture_image_incoming_->setTracksPrev(tracks_last_incoming); + + // Merge tracks to get tracks_origin_incoming + TracksMap tracks_origin_last = capture_image_last_->getTracksOrigin(); + TracksMap tracks_origin_incoming = merge_tracks(tracks_origin_last, tracks_last_incoming); + + // Outliers rejection with essential matrix + cv::Mat E; + computeEssential(mwkps_origin, mwkps_incoming, tracks_origin_incoming, E); + + //////////////////////////////// // if too few tracks left in incoming // detect new KeyPoints in last and track them to incoming //////////////////////////////// + size_t n_tracks = tracks_origin_incoming.size(); + if (n_tracks < 500){ + return; + } //////////////////////////////// @@ -190,7 +219,7 @@ unsigned int ProcessorVisualOdometry::processNew(const int& _max_features) std::cout << "In incoming, track: " << track_li.first << " -> " << track_li.second << std::endl; if (!tracks_map_li_matched_.count(track_li.first)){ std::cout << "A NEW track is born!" << std::endl; - // create a new last feature, a new track and add the incoming feature to this track + // 2) create a new last feature, a new track and add the incoming feature to this track WKeyPoint kp_last = capture_image_last_->getKeyPoints().at(track_li.first); FeaturePointImagePtr feat_pi_last = FeatureBase::emplace<FeaturePointImage>(capture_image_last_, kp_last, pixel_cov_); track_matrix_.newTrack(feat_pi_last); @@ -223,10 +252,8 @@ void ProcessorVisualOdometry::establishFactors() for (auto lmk: getProblem()->getMap()->getLandmarkList()){ if (lmk->id() == feat_pi->trackId()){ associated_lmk = lmk; - break; } } - // 1) create a factor between new KF and assocatiated track landmark // HYP: assuming the trackid are the same as the landmark ID -> BAD if other types of landmarks involved if (associated_lmk){ @@ -236,18 +263,17 @@ void ProcessorVisualOdometry::establishFactors() // 2) create landmark if track is not associated with one and has enough length else if(track_matrix_.trackSize(feat->trackId()) >= min_track_length){ - std::cout << "NEW valid track \\o/ -> create a new Landmark" << std::endl; + std::cout << "NEW valid track \\o/" << std::endl; LandmarkBasePtr lmk = emplaceLandmark(feat_pi); - // Associate the track to the landmark - // NOT the right way -> should be replaced by book-keeping (in the processor? Feature?) - // maybe call feature->setLandmarkId() on all the features of the track? - lmk->setId(feat_pi->trackId()); - LandmarkHpPtr lmk_hp = std::dynamic_pointer_cast<LandmarkHp>(lmk); - for (auto feat_kf: track_matrix_.trackAtKeyframes(feat->trackId())){ + lmk->setId(feat_pi->trackId()); + Track track_kf = track_matrix_.trackAtKeyframes(feat->trackId()); + for (auto feat_kf: track_kf){ + LandmarkHpPtr lmk_hp = std::dynamic_pointer_cast<LandmarkHp>(lmk); FactorBase::emplace<FactorPixelHp>(feat_kf.second, feat_kf.second, lmk_hp, shared_from_this(), true); } } } + } LandmarkBasePtr ProcessorVisualOdometry::emplaceLandmark(FeatureBasePtr _feat) @@ -286,6 +312,7 @@ LandmarkBasePtr ProcessorVisualOdometry::emplaceLandmark(FeatureBasePtr _feat) getSensor(), feat_pi->getKeyPoint().getDescriptor()); + // _feat->setLandmarkId(lmk_hp_ptr->id()); // not necessary I think? return lmk_hp_ptr; } @@ -320,7 +347,7 @@ void ProcessorVisualOdometry::setParams(const ParamsProcessorVisualOdometryPtr _ params_visual_odometry_ = _params; } -TracksMap ProcessorVisualOdometry::klt_track(cv::Mat img_prev, cv::Mat img_curr, KeyPointsMap &mwkps_prev, KeyPointsMap &mwkps_curr) +TracksMap ProcessorVisualOdometry::kltTrack(cv::Mat img_prev, cv::Mat img_curr, KeyPointsMap &mwkps_prev, KeyPointsMap &mwkps_curr) { TracksMap tracks_prev_curr; @@ -386,6 +413,35 @@ TracksMap ProcessorVisualOdometry::klt_track(cv::Mat img_prev, cv::Mat img_curr, return tracks_prev_curr; } +bool ProcessorVisualOdometry::computeEssential(KeyPointsMap mwkps_prev, KeyPointsMap mwkps_curr, TracksMap &tracks_prev_curr, cv::Mat &E) +{ + // We need to build lists of pt2f for openCV function + std::vector<cv::Point2f> p2f_prev, p2f_curr; + std::vector<size_t> all_indices; + for (auto & track : tracks_prev_curr){ + all_indices.push_back(track.first); + p2f_prev.push_back(mwkps_prev[track.first].getCvKeyPoint().pt); + p2f_curr.push_back(mwkps_curr[track.second].getCvKeyPoint().pt); + } + + // We need at least five tracks + if (p2f_prev.size() < 5) return false; + + float focal_length = Kcv_.at<float>(0, 0); + cv::Mat cvMask; + cv::Point2f principal_pt(Kcv_.at<float>(0,2), Kcv_.at<float>(1,2)); + cv::findEssentialMat(p2f_prev, p2f_curr, focal_length, principal_pt, cv::RANSAC, + 0.99, 1.0, cvMask); + + // Let's remove outliers from tracksMap + for (size_t k=0; k<all_indices.size(); k++){ + if (cvMask.at<bool>(k) == 0){ + tracks_prev_curr.erase(all_indices.at(k)); + } + } + return true; +} + } //namespace wolf