// This file is part of SVO - Semi-direct Visual Odometry. // // Copyright (C) 2014 Christian Forster // (Robotics and Perception Group, University of Zurich, Switzerland). // // SVO is free software: you can redistribute it and/or modify it under the // terms of the GNU General Public License as published by the Free Software // Foundation, either version 3 of the License, or any later version. // // SVO is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include #include #include #include #include #include #include #include #include #include #ifdef USE_BUNDLE_ADJUSTMENT #include #endif namespace svo { FrameHandlerMono::FrameHandlerMono(vk::AbstractCamera* cam) : FrameHandlerBase(), cam_(cam), reprojector_(cam_, map_), depth_filter_(NULL) { initialize(); } void FrameHandlerMono::initialize() { feature_detection::DetectorPtr feature_detector( new feature_detection::FastDetector( cam_->width(), cam_->height(), Config::gridSize(), Config::nPyrLevels())); DepthFilter::callback_t depth_filter_cb = boost::bind( &MapPointCandidates::newCandidatePoint, &map_.point_candidates_, _1, _2); depth_filter_ = new DepthFilter(feature_detector, depth_filter_cb); depth_filter_->startThread(); } FrameHandlerMono::~FrameHandlerMono() { delete depth_filter_; } void FrameHandlerMono::addImage(const cv::Mat& img, const double timestamp) { if(!startFrameProcessingCommon(timestamp)) return; // some cleanup from last iteration, can't do before because of visualization core_kfs_.clear(); overlap_kfs_.clear(); // create new frame SVO_START_TIMER("pyramid_creation"); new_frame_.reset(new Frame(cam_, img.clone(), timestamp)); SVO_STOP_TIMER("pyramid_creation"); // process frame UpdateResult res = RESULT_FAILURE; if(stage_ == STAGE_DEFAULT_FRAME) res = processFrame(); else if(stage_ == STAGE_SECOND_FRAME) res = processSecondFrame(); else if(stage_ == STAGE_FIRST_FRAME) res = processFirstFrame(); // set last frame last_frame_ = new_frame_; new_frame_.reset(); // finish processing finishFrameProcessingCommon(last_frame_->id_, res, last_frame_->nObs()); } void FrameHandlerMono::setFirstFrame(const FramePtr& first_frame) { resetAll(); last_frame_ = first_frame; last_frame_->setKeyframe(); map_.addKeyframe(last_frame_); stage_ = STAGE_DEFAULT_FRAME; } FrameHandlerMono::UpdateResult FrameHandlerMono::processFirstFrame() { new_frame_->T_f_w_ = SE3(Matrix3d::Identity(), Vector3d::Zero()); if(klt_homography_init_.addFirstFrame(new_frame_) == initialization::FAILURE) return RESULT_NO_KEYFRAME; new_frame_->setKeyframe(); map_.addKeyframe(new_frame_); stage_ = STAGE_SECOND_FRAME; SVO_INFO_STREAM("Init: Selected first frame."); return RESULT_IS_KEYFRAME; } FrameHandlerBase::UpdateResult FrameHandlerMono::processSecondFrame() { initialization::InitResult res = klt_homography_init_.addSecondFrame(new_frame_); if(res == initialization::FAILURE) return RESULT_FAILURE; else if(res == initialization::NO_KEYFRAME) return RESULT_NO_KEYFRAME; // two-frame bundle adjustment #ifdef USE_BUNDLE_ADJUSTMENT ba::twoViewBA(new_frame_.get(), map_.lastKeyframe().get(), Config::lobaThresh(), map_); #endif double depth_mean, depth_min; frame_utils::getSceneDepth(*new_frame_, depth_mean, depth_min); depth_filter_->addKeyframe(new_frame_, depth_mean, 0.5*depth_min); // add frame to map new_frame_->setKeyframe(); map_.addKeyframe(new_frame_); stage_ = STAGE_DEFAULT_FRAME; klt_homography_init_.reset(); SVO_INFO_STREAM("Init: Selected second frame, triangulated initial map."); return RESULT_IS_KEYFRAME; } FrameHandlerBase::UpdateResult FrameHandlerMono::processFrame() { // Set initial pose TODO use prior new_frame_->T_f_w_ = last_frame_->T_f_w_; // sparse image align SVO_START_TIMER("sparse_img_align"); SparseImgAlign img_align(Config::kltMaxLevel(), Config::kltMinLevel(), 30, SparseImgAlign::GaussNewton, false, false); size_t img_align_n_tracked = img_align.run(last_frame_, new_frame_); SVO_STOP_TIMER("sparse_img_align"); SVO_LOG(img_align_n_tracked); SVO_DEBUG_STREAM("Img Align:\t Tracked = " << img_align_n_tracked); // map reprojection & feature alignment SVO_START_TIMER("reproject"); reprojector_.reprojectMap(new_frame_, overlap_kfs_); SVO_STOP_TIMER("reproject"); const size_t repr_n_new_references = reprojector_.n_matches_; const size_t repr_n_mps = reprojector_.n_trials_; SVO_LOG2(repr_n_mps, repr_n_new_references); SVO_DEBUG_STREAM("Reprojection:\t nPoints = "<addFrame(new_frame_); return RESULT_NO_KEYFRAME; } new_frame_->setKeyframe(); SVO_DEBUG_STREAM("New keyframe selected."); // new keyframe selected for(Features::iterator it=new_frame_->fts_.begin(); it!=new_frame_->fts_.end(); ++it) if((*it)->point != NULL) (*it)->point->addFrameRef(*it); map_.point_candidates_.addCandidatePointToFrame(new_frame_); // optional bundle adjustment core_kfs_.insert(new_frame_); #ifdef USE_BUNDLE_ADJUSTMENT if(Config::lobaNumIter() > 0) { SVO_START_TIMER("local_ba"); setCoreKfs(Config::coreNKfs()); size_t loba_n_erredges_init, loba_n_erredges_fin; double loba_err_init, ba_final_error; ba::localBA(new_frame_.get(), &core_kfs_, &map_, loba_n_erredges_init, ba_incorrect_edges_2, ba_init_error, loba_err_fin); SVO_STOP_TIMER("local_ba"); SVO_LOG4(loba_n_erredges_init, loba_n_erredges_fin, loba_err_init, loba_err_fin); SVO_DEBUG_STREAM("Local BA:\t RemovedEdges {"<addKeyframe(new_frame_, depth_mean, 0.5*depth_min); // if limited number of keyframes, remove the one furthest apart if(Config::maxNKfs() > 2 && map_.size() >= Config::maxNKfs()) { FramePtr furthest_frame = map_.getFurthestKeyframe(new_frame_->pos()); depth_filter_->removeKeyframe(furthest_frame); // TODO this interrupts the mapper thread, maybe we can solve this better map_.safeDeleteFrame(furthest_frame); } // add keyframe to map map_.addKeyframe(new_frame_); return RESULT_IS_KEYFRAME; } void FrameHandlerMono::resetAll() { resetCommon(); last_frame_.reset(); new_frame_.reset(); core_kfs_.clear(); overlap_kfs_.clear(); depth_filter_->reset(); } bool FrameHandlerMono::needNewKf(double scene_depth_mean) { for(auto it=overlap_kfs_.begin(), ite=overlap_kfs_.end(); it!=ite; ++it) { Vector3d relpos = new_frame_->w2f(it->first->pos()); if(fabs(relpos.x())/scene_depth_mean < Config::kfSelectMinDist() && fabs(relpos.y())/scene_depth_mean < Config::kfSelectMinDist()*0.8 && fabs(relpos.z())/scene_depth_mean < Config::kfSelectMinDist()*1.3) return false; } return true; } void FrameHandlerMono::setCoreKfs(size_t n_closest) { size_t n = min(n_closest, overlap_kfs_.size()-1); std::partial_sort(overlap_kfs_.begin(), overlap_kfs_.begin()+n, overlap_kfs_.end(), boost::bind(&pair::second, _1) > boost::bind(&pair::second, _2)); std::for_each(overlap_kfs_.begin(), overlap_kfs_.end(), [&](pair& i){ core_kfs_.insert(i.first); }); } } // namespace svo