diff --git a/svo/include/svo/config.h b/svo/include/svo/config.h
index 3ad547b..d4f50df 100644
--- a/svo/include/svo/config.h
+++ b/svo/include/svo/config.h
@@ -14,8 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-#ifndef NSLAM_CONFIG_H_
-#define NSLAM_CONFIG_H_
+#ifndef SVO_CONFIG_H_
+#define SVO_CONFIG_H_
#include
#include
@@ -23,7 +23,7 @@
namespace svo {
-using namespace std;
+using std::string;
/// Global configuration file of SVO.
/// Implements the Singleton design pattern to allow global access and to ensure
@@ -154,6 +154,6 @@ private:
int quality_max_drop_fts;
};
-} // namespace nslam
+} // namespace svo
-#endif // NSLAM_CONFIG_H_
+#endif // SVO_CONFIG_H_
diff --git a/svo/include/svo/depth_filter.h b/svo/include/svo/depth_filter.h
index d943764..955799a 100644
--- a/svo/include/svo/depth_filter.h
+++ b/svo/include/svo/depth_filter.h
@@ -46,7 +46,7 @@ struct Seed
float z_range; //!< Max range of the possible depth.
float sigma2; //!< Variance of normal distribution.
Matrix2d patch_cov; //!< Patch covariance in reference image.
- Seed(Feature* ftr, float angle, float depth_mean, float depth_min);
+ Seed(Feature* ftr, float depth_mean, float depth_min);
};
/// Depth filter implements the Bayesian Update proposed in:
@@ -64,7 +64,8 @@ public:
typedef boost::function callback_t;
/// Depth-filter config parameters
- struct Options {
+ struct Options
+ {
bool check_ftr_angle; //!< gradient features are only updated if the epipolar line is orthogonal to the gradient.
bool epi_search_1d; //!< restrict Gauss Newton in the epipolar search to the epipolar line.
bool verbose; //!< display output.
@@ -83,8 +84,11 @@ public:
{}
} options_;
- DepthFilter(feature_detection::DetectorPtr feature_detector, callback_t seed_converged_cb);
- ~DepthFilter();
+ DepthFilter(
+ feature_detection::DetectorPtr feature_detector,
+ callback_t seed_converged_cb);
+
+ virtual ~DepthFilter();
/// Start this thread when seed updating should be in a parallel thread.
void startThread();
@@ -129,7 +133,7 @@ public:
const double z,
const double px_error_angle);
-private:
+protected:
feature_detection::DetectorPtr feature_detector_;
callback_t seed_converged_cb_;
std::list > seeds_;
@@ -149,7 +153,7 @@ private:
void initializeSeeds(FramePtr frame);
/// Update all seeds with a new measurement frame.
- void updateSeeds(FramePtr frame);
+ virtual void updateSeeds(FramePtr frame);
/// When a new keyframe arrives, the frame queue should be cleared.
void clearFrameQueue();
diff --git a/svo/include/svo/feature.h b/svo/include/svo/feature.h
index a0a30f4..37be2b7 100644
--- a/svo/include/svo/feature.h
+++ b/svo/include/svo/feature.h
@@ -26,34 +26,47 @@ struct Feature
{
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
- Frame* frame; //!< Pointer to frame in which the feature was detected.
+ enum FeatureType {
+ CORNER,
+ EDGELET
+ };
+
+ FeatureType type; //!< Type can be corner or edgelet.
+ Frame* frame; //!< Pointer to frame in which the feature was detected.
Vector2d px; //!< Coordinates in pixels on pyramid level 0.
Vector3d f; //!< Unit-bearing vector of the feature.
- int level; //!< Image pyramid level where feature was extracted.
- Point* point; //!< Pointer to 3D point which corresponds to the feature.
+ int level; //!< Image pyramid level where feature was extracted.
+ Point* point; //!< Pointer to 3D point which corresponds to the feature.
+ Vector2d grad; //!< Dominant gradient direction for edglets, normalized.
- Feature(Frame* _frame, const Vector2d& _px, int _level)
- : frame(_frame),
- px(_px),
- f(frame->cam_->cam2world(px)),
- level(_level),
- point(NULL)
+ Feature(Frame* _frame, const Vector2d& _px, int _level) :
+ type(CORNER),
+ frame(_frame),
+ px(_px),
+ f(frame->cam_->cam2world(px)),
+ level(_level),
+ point(NULL),
+ grad(0.0,0.0)
{}
- Feature(Frame* _frame, const Vector2d& _px, const Vector3d& _f, int _level)
- : frame(_frame),
+ Feature(Frame* _frame, const Vector2d& _px, const Vector3d& _f, int _level) :
+ type(CORNER),
+ frame(_frame),
px(_px),
f(_f),
level(_level),
- point(NULL)
+ point(NULL),
+ grad(0.0,0.0)
{}
- Feature(Frame* _frame, Point* _point, const Vector2d& _px, const Vector3d& _f, int _level)
- : frame(_frame),
- px(_px),
- f(_f),
- level(_level),
- point(_point)
+ Feature(Frame* _frame, Point* _point, const Vector2d& _px, const Vector3d& _f, int _level) :
+ type(CORNER),
+ frame(_frame),
+ px(_px),
+ f(_f),
+ level(_level),
+ point(_point),
+ grad(0.0,0.0)
{}
};
diff --git a/svo/include/svo/feature_detection.h b/svo/include/svo/feature_detection.h
index a84586a..d0990a8 100644
--- a/svo/include/svo/feature_detection.h
+++ b/svo/include/svo/feature_detection.h
@@ -26,7 +26,6 @@ namespace svo {
namespace feature_detection {
/// Temporary container used for corner detection. Features are initialized from these.
-// TODO: why not use feature struct?
struct Corner
{
int x; //!< x-coordinate of corner in the image.
@@ -44,14 +43,42 @@ typedef vector Corners;
class AbstractDetector
{
public:
- virtual ~AbstractDetector() {};
- virtual void detect(
- const ImgPyr& img_pyr,
- const Features& fts,
+ AbstractDetector(
+ const int img_width,
+ const int img_height,
const int cell_size,
- const int n_levels,
+ const int n_pyr_levels);
+
+ virtual ~AbstractDetector() {};
+
+ virtual void detect(
+ Frame* frame,
+ const ImgPyr& img_pyr,
const double detection_threshold,
- Corners* corners) const = 0;
+ Features& fts) = 0;
+
+ /// Flag the grid cell as occupied
+ void setGridOccpuancy(const Vector2d& px);
+
+ /// Set grid cells of existing features as occupied
+ void setExistingFeatures(const Features& fts);
+
+protected:
+
+ static const int border_ = 8; //!< no feature should be within 8px of border.
+ const int cell_size_;
+ const int n_pyr_levels_;
+ const int grid_n_cols_;
+ const int grid_n_rows_;
+ vector grid_occupancy_;
+
+ void resetGrid();
+
+ inline int getCellIndex(int x, int y, int level)
+ {
+ const int scale = (1< DetectorPtr;
@@ -59,15 +86,19 @@ typedef boost::shared_ptr DetectorPtr;
class FastDetector : public AbstractDetector
{
public:
- FastDetector() {}
+ FastDetector(
+ const int img_width,
+ const int img_height,
+ const int cell_size,
+ const int n_pyr_levels);
+
virtual ~FastDetector() {}
+
virtual void detect(
- const ImgPyr& img_pyr,
- const Features& fts,
- const int cell_size,
- const int n_levels,
- const double detection_threshold,
- Corners* corners) const;
+ Frame* frame,
+ const ImgPyr& img_pyr,
+ const double detection_threshold,
+ Features& fts);
};
} // namespace feature_detection
diff --git a/svo/include/svo/frame.h b/svo/include/svo/frame.h
index f4413e7..8fcfd2c 100644
--- a/svo/include/svo/frame.h
+++ b/svo/include/svo/frame.h
@@ -79,7 +79,7 @@ public:
void removeKeyPoint(Feature* ftr);
/// Return number of point observations.
- size_t nObs() const;
+ inline size_t nObs() const { return fts_.size(); }
/// Check if a point in (w)orld coordinate frame is visible in the image.
bool isVisible(const Vector3d& xyz_w) const;
diff --git a/svo/include/svo/frame_handler_base.h b/svo/include/svo/frame_handler_base.h
index 66d4595..bbde3ce 100644
--- a/svo/include/svo/frame_handler_base.h
+++ b/svo/include/svo/frame_handler_base.h
@@ -63,9 +63,6 @@ public:
virtual ~FrameHandlerBase();
- /// Set initial pose of the camera.
- void setInitialPose(const SE3& T_frame_from_world) { T_f_w_init_ = T_frame_from_world; }
-
/// Get the current map.
const Map& map() const { return map_; }
@@ -92,7 +89,6 @@ protected:
bool set_reset_; //!< Flag that the user can set. Will reset the system before the next iteration.
bool set_start_; //!< Flag the user can set to start the system when the next image is received.
Map map_; //!< Map of keyframes created by the slam system.
- SE3 T_f_w_init_; //!< Initial pose of the camera.
vk::Timer timer_; //!< Stopwatch to measure time to process frame.
vk::RingBuffer acc_frame_timings_; //!< Total processing time of the last 10 frames, used to give some user feedback on the performance.
vk::RingBuffer acc_num_obs_; //!< Number of observed features of the last 10 frames, used to give some user feedback on the tracking performance.
diff --git a/svo/include/svo/point.h b/svo/include/svo/point.h
index 933b8bc..72a7221 100644
--- a/svo/include/svo/point.h
+++ b/svo/include/svo/point.h
@@ -56,7 +56,8 @@ public:
int n_succeeded_reproj_; //!< Number of succeeded reprojections. Used to assess the quality of the point.
int last_structure_optim_; //!< Timestamp of last point optimization
- Point(Vector3d pos);
+ Point(const Vector3d& pos);
+ Point(const Vector3d& pos, Feature* ftr);
~Point();
/// Add a reference to a frame.
diff --git a/svo/include/svo/sparse_img_align.h b/svo/include/svo/sparse_img_align.h
index 17cb3a1..c9da479 100644
--- a/svo/include/svo/sparse_img_align.h
+++ b/svo/include/svo/sparse_img_align.h
@@ -51,6 +51,10 @@ public:
FramePtr ref_frame,
FramePtr cur_frame);
+ /// Return fisher information matrix, i.e. the Hessian of the log-likelihood
+ /// at the converged state.
+ Matrix getFisherInformation();
+
protected:
FramePtr ref_frame_; //!< reference frame, has depth for gradient pixels.
FramePtr cur_frame_; //!< only the image is known!
diff --git a/svo/src/depth_filter.cpp b/svo/src/depth_filter.cpp
index ce34e69..2cbafe2 100644
--- a/svo/src/depth_filter.cpp
+++ b/svo/src/depth_filter.cpp
@@ -34,7 +34,7 @@ namespace svo {
int Seed::batch_counter = 0;
int Seed::seed_counter = 0;
-Seed::Seed(Feature* ftr, float angle, float depth_mean, float depth_min) :
+Seed::Seed(Feature* ftr, float depth_mean, float depth_min) :
batch_id(batch_counter),
id(seed_counter++),
ftr(ftr),
@@ -113,32 +113,20 @@ void DepthFilter::addKeyframe(FramePtr frame, double depth_mean, double depth_mi
void DepthFilter::initializeSeeds(FramePtr frame)
{
- feature_detection::Corners corners;
- feature_detector_->detect(frame->img_pyr_, frame->fts_, svo::Config::gridSize(),
- svo::Config::nPyrLevels(), Config::triangMinCornerScore(),
- &corners);
+ Features new_features;
+ feature_detector_->setExistingFeatures(frame->fts_);
+ feature_detector_->detect(frame.get(), frame->img_pyr_,
+ Config::triangMinCornerScore(), new_features);
+ // initialize a seed for every new feature
seeds_updating_halt_ = true;
lock_t lock(seeds_mut_); // by locking the updateSeeds function stops
- size_t new_seeds = 0;
++Seed::batch_counter;
+ std::for_each(new_features.begin(), new_features.end(), [&](Feature* ftr){
+ seeds_.push_back(Seed(ftr, new_keyframe_mean_depth_, new_keyframe_min_depth_));
+ });
- // now for all maximum corners, initialize a new seed
- for(feature_detection::Corners::iterator it=corners.begin(); it!=corners.end(); ++it)
- {
- // we must check again if the score is above the threshold because the Corners
- // vector is initialized with dummy corners
- if(it->score > Config::triangMinCornerScore())
- {
- Feature* new_feature(new Feature(frame.get(), Vector2d(it->x, it->y), it->level));
- seeds_.push_back(Seed(new_feature, it->angle, new_keyframe_mean_depth_, new_keyframe_min_depth_));
- ++new_seeds;
- }
- }
-
- if(options_.verbose)
- SVO_INFO_STREAM("DepthFilter: Initialized "<ftr->frame->f2c(xyz_f));
- if(!it->ftr->frame->cam_->isInFrame(px.cast())) {
- ++it;
- if(options_.verbose)
- SVO_DEBUG_STREAM("not visible");
+ if(!it->ftr->frame->cam_->isInFrame(it->ftr->frame->f2c(xyz_f).cast())) {
+ ++it; // point does not project in image
continue;
}
@@ -250,25 +234,12 @@ void DepthFilter::updateSeeds(FramePtr frame)
*it->ftr->frame, *frame, *it->ftr, 1.0/it->mu, 1.0/z_inv_min, 1.0/z_inv_max,
options_.epi_search_1d, z, h_inv))
{
- if(options_.verbose)
- SVO_INFO_STREAM("no match found");
it->b++; // increase outlier probability when no match was found
++it;
++n_failed_matches;
continue;
}
- if(options_.use_photometric_disparity_error && h_inv > 0)
- {
- px_noise = fmax(2.0*options_.sigma_i_sq*h_inv, 1.0);
- px_error_angle = atan(px_noise/(2.0*focal_length))*2.0; // law of chord (sehnensatz)
- if(options_.verbose)
- SVO_DEBUG_STREAM("Photometric disparity error "<ftr->f, z, px_error_angle);
double tau_inverse = 0.5 * (1.0/max(0.0000001, z-tau) - 1.0/(z+tau));
@@ -284,8 +255,6 @@ void DepthFilter::updateSeeds(FramePtr frame)
{
Vector3d xyz_world(it->ftr->frame->T_f_w_.inverse() * (it->ftr->f * (1.0/it->mu)));
seed_converged_cb_(*it, xyz_world);
- if(options_.verbose)
- SVO_DEBUG_STREAM("seed converged");
}
it = seeds_.erase(it);
}
@@ -299,8 +268,7 @@ void DepthFilter::updateSeeds(FramePtr frame)
}
}
-void DepthFilter::
-clearFrameQueue()
+void DepthFilter::clearFrameQueue()
{
while(!frame_queue_.empty())
frame_queue_.pop();
diff --git a/svo/src/feature_detection.cpp b/svo/src/feature_detection.cpp
index be0ca02..98183f8 100644
--- a/svo/src/feature_detection.cpp
+++ b/svo/src/feature_detection.cpp
@@ -22,54 +22,95 @@
namespace svo {
namespace feature_detection {
-void FastDetector::detect(
- const ImgPyr& img_pyr,
- const Features& existing_fts,
+AbstractDetector::AbstractDetector(
+ const int img_width,
+ const int img_height,
const int cell_size,
- const int n_levels,
- const double detection_threshold,
- Corners* corners) const
-{
- // first, find which parts of the image already have a matched feature
- const int grid_n_cols = ceil(static_cast(img_pyr[0].cols)/cell_size);
- const int grid_n_rows = ceil(static_cast(img_pyr[0].rows)/cell_size);
- vector occupancy(grid_n_cols*grid_n_rows, false);
- for(Features::const_iterator it=existing_fts.begin(); it!=existing_fts.end(); ++it)
- {
- if((*it)->point == false)
- continue;
- const int k = static_cast((*it)->px[1]/cell_size)*grid_n_cols + static_cast((*it)->px[0]/cell_size);
- occupancy.at(k) = true;
- }
+ const int n_pyr_levels) :
+ cell_size_(cell_size),
+ n_pyr_levels_(n_pyr_levels),
+ grid_n_cols_(ceil(static_cast(img_width)/cell_size_)),
+ grid_n_rows_(ceil(static_cast(img_height)/cell_size_)),
+ grid_occupancy_(grid_n_cols_*grid_n_rows_, false)
+{}
- // now, detect features and perform nonmax suppression
- corners->resize(grid_n_cols*grid_n_rows, Corner(0,0,detection_threshold,0,0.0f));
- for(int L=0; L(i->px[1]/cell_size_)*grid_n_cols_
+ + static_cast(i->px[0]/cell_size_)) = true;
+ });
+}
+
+void AbstractDetector::setGridOccpuancy(const Vector2d& px)
+{
+ grid_occupancy_.at(
+ static_cast(px[1]/cell_size_)*grid_n_cols_
+ + static_cast(px[0]/cell_size_)) = true;
+}
+
+FastDetector::FastDetector(
+ const int img_width,
+ const int img_height,
+ const int cell_size,
+ const int n_pyr_levels) :
+ AbstractDetector(img_width, img_height, cell_size, n_pyr_levels)
+{}
+
+void FastDetector::detect(
+ Frame* frame,
+ const ImgPyr& img_pyr,
+ const double detection_threshold,
+ Features& fts)
+{
+ Corners corners(grid_n_cols_*grid_n_rows_, Corner(0,0,detection_threshold,0,0.0f));
+ for(int L=0; L fast_corners;
#if __SSE2__
- fast::fast_corner_detect_10_sse2((fast::fast_byte*) img_pyr[L].data, img_pyr[L].cols, img_pyr[L].rows, img_pyr[L].cols, 20, fast_corners);
+ fast::fast_corner_detect_10_sse2(
+ (fast::fast_byte*) img_pyr[L].data, img_pyr[L].cols,
+ img_pyr[L].rows, img_pyr[L].cols, 20, fast_corners);
#elif HAVE_FAST_NEON
- fast::fast_corner_detect_9_neon((fast::fast_byte*) img_pyr[L].data, img_pyr[L].cols, img_pyr[L].rows, img_pyr[L].cols, 20, fast_corners);
+ fast::fast_corner_detect_9_neon(
+ (fast::fast_byte*) img_pyr[L].data, img_pyr[L].cols,
+ img_pyr[L].rows, img_pyr[L].cols, 20, fast_corners);
#else
- fast::fast_corner_detect_10((fast::fast_byte*) img_pyr[L].data, img_pyr[L].cols, img_pyr[L].rows, img_pyr[L].cols, 20, fast_corners);
+ fast::fast_corner_detect_10(
+ (fast::fast_byte*) img_pyr[L].data, img_pyr[L].cols,
+ img_pyr[L].rows, img_pyr[L].cols, 20, fast_corners);
#endif
vector scores, nm_corners;
fast::fast_corner_score_10((fast::fast_byte*) img_pyr[L].data, img_pyr[L].cols, fast_corners, 20, scores);
fast::fast_nonmax_3x3(fast_corners, scores, nm_corners);
- for(vector::iterator it=nm_corners.begin(); it!=nm_corners.end(); ++it)
+ for(auto it=nm_corners.begin(), ite=nm_corners.end(); it!=ite; ++it)
{
fast::fast_xy& xy = fast_corners.at(*it);
- const int k = static_cast((xy.y*scale)/cell_size)*grid_n_cols + static_cast((xy.x*scale)/cell_size);
- if(occupancy[k])
+ const int k = static_cast((xy.y*scale)/cell_size_)*grid_n_cols_
+ + static_cast((xy.x*scale)/cell_size_);
+ if(grid_occupancy_[k])
continue;
const float score = vk::shiTomasiScore(img_pyr[L], xy.x, xy.y);
- if(score > corners->at(k).score)
- corners->at(k) = Corner(xy.x*scale, xy.y*scale, score, L, 0.0f);
+ if(score > corners.at(k).score)
+ corners.at(k) = Corner(xy.x*scale, xy.y*scale, score, L, 0.0f);
}
}
+
+ // Create feature for every corner that has high enough corner score
+ std::for_each(corners.begin(), corners.end(), [&](Corner& c) {
+ if(c.score > detection_threshold)
+ fts.push_back(new Feature(frame, Vector2d(c.x, c.y), c.level));
+ });
+
+ resetGrid();
}
} // namespace feature_detection
diff --git a/svo/src/frame.cpp b/svo/src/frame.cpp
index e5f4501..6234d0b 100644
--- a/svo/src/frame.cpp
+++ b/svo/src/frame.cpp
@@ -137,13 +137,6 @@ void Frame::removeKeyPoint(Feature* ftr)
setKeyPoints();
}
-size_t Frame::nObs() const
-{
- size_t n=0;
- std::for_each(fts_.begin(), fts_.end(), [&](Feature* i){if(i->point != NULL) ++n; });
- return n;
-}
-
bool Frame::isVisible(const Vector3d& xyz_w) const
{
Vector3d xyz_f = T_f_w_*xyz_w;
diff --git a/svo/src/frame_handler_base.cpp b/svo/src/frame_handler_base.cpp
index 91bf5f0..f526cb3 100644
--- a/svo/src/frame_handler_base.cpp
+++ b/svo/src/frame_handler_base.cpp
@@ -120,14 +120,14 @@ int FrameHandlerBase::finishFrameProcessingCommon(
num_obs_last_ = num_observations;
SVO_STOP_TIMER("tot_time");
- #ifdef SVO_TRACE
- g_permon->writeToFile();
- {
- boost::unique_lock lock(map_.point_candidates_.mut_);
- size_t n_candidates = map_.point_candidates_.candidates_.size();
- SVO_LOG(n_candidates);
- }
- #endif
+#ifdef SVO_TRACE
+ g_permon->writeToFile();
+ {
+ boost::unique_lock lock(map_.point_candidates_.mut_);
+ size_t n_candidates = map_.point_candidates_.candidates_.size();
+ SVO_LOG(n_candidates);
+ }
+#endif
// reset if failure or requested
if(dropout == RESULT_FAILURE || set_reset_)
diff --git a/svo/src/frame_handler_mono.cpp b/svo/src/frame_handler_mono.cpp
index fbd8638..e588ce9 100644
--- a/svo/src/frame_handler_mono.cpp
+++ b/svo/src/frame_handler_mono.cpp
@@ -37,7 +37,8 @@ FrameHandlerMono::FrameHandlerMono(vk::AbstractCamera* cam) :
depth_filter_(NULL)
{
// create depth filter and set callback
- feature_detection::DetectorPtr feature_detector(new feature_detection::FastDetector());
+ 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();
@@ -90,7 +91,7 @@ void FrameHandlerMono::setFirstFrame(const FramePtr& first_frame)
FrameHandlerMono::UpdateResult FrameHandlerMono::processFirstFrame()
{
- new_frame_->T_f_w_ = T_f_w_init_;
+ 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();
@@ -183,6 +184,7 @@ FrameHandlerBase::UpdateResult FrameHandlerMono::processFrame()
depth_filter_->addFrame(new_frame_);
return RESULT_NO_KEYFRAME;
}
+ new_frame_->setKeyframe();
SVO_DEBUG_STREAM("New keyframe selected.");
// new keyframe selected
@@ -222,7 +224,6 @@ FrameHandlerBase::UpdateResult FrameHandlerMono::processFrame()
}
// add keyframe to map
- new_frame_->setKeyframe();
map_.addKeyframe(new_frame_);
return RESULT_IS_KEYFRAME;
diff --git a/svo/src/initialization.cpp b/svo/src/initialization.cpp
index 2aa3931..16d1c80 100644
--- a/svo/src/initialization.cpp
+++ b/svo/src/initialization.cpp
@@ -109,25 +109,19 @@ void detectFeatures(
vector& px_vec,
vector& f_vec)
{
- feature_detection::Corners corners;
- feature_detection::FastDetector detector;
- detector.detect(frame->img_pyr_, frame->fts_, Config::gridSize(),
- Config::nPyrLevels(), Config::triangMinCornerScore(),
- &corners);
+ Features new_features;
+ feature_detection::FastDetector detector(
+ frame->img().cols, frame->img().rows, Config::gridSize(), Config::nPyrLevels());
+ detector.detect(frame.get(), frame->img_pyr_, Config::triangMinCornerScore(), new_features);
// now for all maximum corners, initialize a new seed
- px_vec.clear(); px_vec.reserve(corners.size());
- f_vec.clear(); f_vec.reserve(corners.size());
- for(feature_detection::Corners::iterator it=corners.begin(); it!=corners.end(); ++it)
- {
- // we must check again if the score is above the threshold because the Corners
- // vector is initialized with dummy corners
- if(it->score > Config::triangMinCornerScore())
- {
- px_vec.push_back(cv::Point2f(it->x, it->y));
- f_vec.push_back(frame->c2f(it->x, it->y));
- }
- }
+ px_vec.clear(); px_vec.reserve(new_features.size());
+ f_vec.clear(); f_vec.reserve(new_features.size());
+ std::for_each(new_features.begin(), new_features.end(), [&](Feature* ftr){
+ px_vec.push_back(cv::Point2f(ftr->px[0], ftr->px[1]));
+ f_vec.push_back(ftr->f);
+ delete ftr;
+ });
}
void trackKlt(
diff --git a/svo/src/matcher.cpp b/svo/src/matcher.cpp
index 9587608..550d25b 100644
--- a/svo/src/matcher.cpp
+++ b/svo/src/matcher.cpp
@@ -69,7 +69,9 @@ void getWarpMatrixAffine(
A_cur_ref.col(1) = (px_dv - px_cur)/halfpatch_size;
}
-int getBestSearchLevel(const Matrix2d& A_cur_ref, const int max_level)
+int getBestSearchLevel(
+ const Matrix2d& A_cur_ref,
+ const int max_level)
{
// Compute patch level in other image
int level_cur = 0;
@@ -99,7 +101,7 @@ void warpAffine(
return;
}
- // Perform the Warp on a larger patch
+ // Perform the warp on a larger patch.
uint8_t* ref_patch_ptr = ref_patch.patch_with_border;
const Vector2f px_ref_pyr = px_ref.cast() / (1<T_f_w_ = T_cur_from_ref * ref_frame_->T_f_w_;
+
+
+
return n_meas_/patch_area_;
}
+Matrix SparseImgAlign::getFisherInformation()
+{
+ double sigma_i_sq = 5e-4*255*255; // image noise
+ Matrix I = H_/sigma_i_sq;
+ return I;
+}
+
// TODO take jacobian from Frame Class!
void frameJac(const Vector3d & xyz, Matrix & J)
{
diff --git a/svo/test/test_depth_filter.cpp b/svo/test/test_depth_filter.cpp
index a53947d..6072218 100644
--- a/svo/test/test_depth_filter.cpp
+++ b/svo/test/test_depth_filter.cpp
@@ -90,6 +90,7 @@ void DepthFilterTest::depthFilterCb(
void DepthFilterTest::testReconstruction(
std::string dataset_dir,
+
std::string experiment_name)
{
vk::FileReader sequence_file_reader(dataset_dir+"/trajectory.txt");
@@ -103,7 +104,9 @@ void DepthFilterTest::testReconstruction(
vk::Timer t;
std::list n_converged_per_iteration;
- svo::feature_detection::DetectorPtr feature_detector(new svo::feature_detection::FastDetector());
+ svo::feature_detection::DetectorPtr feature_detector(
+ new svo::feature_detection::FastDetector(
+ cam_->width(), cam_->height(), svo::Config::gridSize(), svo::Config::nPyrLevels()));
svo::DepthFilter::callback_t depth_filter_cb = boost::bind(&DepthFilterTest::depthFilterCb, this, _1, _2);
depth_filter_ = new svo::DepthFilter(feature_detector, depth_filter_cb);
diff --git a/svo/test/test_feature_detection.cpp b/svo/test/test_feature_detection.cpp
index 6fa3723..b91e97a 100644
--- a/svo/test/test_feature_detection.cpp
+++ b/svo/test/test_feature_detection.cpp
@@ -17,8 +17,10 @@
#include
#include
#include
+#include
#include
#include
+#include
#include
#include
#include
@@ -37,38 +39,29 @@ void testCornerDetector()
cv::Mat img(cv::imread(img_name, 0));
assert(img.type() == CV_8UC1 && !img.empty());
- svo::ImgPyr img_pyr;
- svo::frame_utils::createImgPyramid(
- img, max(svo::Config::nPyrLevels(), svo::Config::kltMaxLevel()+1), img_pyr);
- svo::Features fts;
+ vk::AbstractCamera* cam = new vk::ATANCamera(752, 480, 0.511496, 0.802603, 0.530199, 0.496011, 0.934092);
+ svo::FramePtr frame(new svo::Frame(cam, img, 0.0));
// Corner detection
vk::Timer t;
- svo::feature_detection::Corners corners;
- svo::feature_detection::FastDetector fast_detector;
+ svo::Features fts;
+ svo::feature_detection::FastDetector fast_detector(
+ img.cols, img.rows, svo::Config::gridSize(), svo::Config::nPyrLevels());
for(int i=0; i<100; ++i)
{
- fast_detector.detect(img_pyr, fts, svo::Config::gridSize(), svo::Config::nPyrLevels(),
- svo::Config::triangMinCornerScore(), &corners);
+ fast_detector.detect(frame.get(), frame->img_pyr_, svo::Config::triangMinCornerScore(), fts);
}
- printf("Fast corner detection took %f ms, %zu corners detected (ref i7-W520: 8.06878ms, 416)\n", t.stop()*10, corners.size());
+ printf("Fast corner detection took %f ms, %zu corners detected (ref i7-W520: 8.06878ms, 416)\n", t.stop()*10, fts.size());
+ printf("Note, in this case, feature detection also contains the cam2world projection of the feature.\n");
+ cv::Mat img_rgb = cv::Mat(img.size(), CV_8UC3);
+ cv::cvtColor(img, img_rgb, CV_GRAY2RGB);
+ std::for_each(fts.begin(), fts.end(), [&](svo::Feature* i){
+ cv::circle(img_rgb, cv::Point2f(i->px[0], i->px[1]), 4*(i->level+1), cv::Scalar(0,255,0), 1);
+ });
+ cv::imshow("ref_img", img_rgb);
+ cv::waitKey(0);
- if(false)
- {
- cv::Mat img_rgb = cv::Mat(img.size(), CV_8UC3);
- cv::cvtColor(img, img_rgb, CV_GRAY2RGB);
- for(svo::feature_detection::Corners::iterator it=corners.begin(); it!=corners.end(); ++it)
- {
- // we must check again if the score is above the threshold because the Corners
- // vector is initialized with dummy corners
- if(it->score > svo::Config::triangMinCornerScore())
- {
- cv::circle(img_rgb, cv::Point2f(it->x, it->y), 4*(it->level+1), cv::Scalar(0,255,0), 1);
- }
- }
- cv::imshow("ref_img", img_rgb);
- cv::waitKey(0);
- }
+ std::for_each(fts.begin(), fts.end(), [&](svo::Feature* i){ delete i; });
}
} // namespace
diff --git a/svo/test/test_pose_optimizer.cpp b/svo/test/test_pose_optimizer.cpp
index 8e98eac..7dd79f8 100644
--- a/svo/test/test_pose_optimizer.cpp
+++ b/svo/test/test_pose_optimizer.cpp
@@ -58,25 +58,15 @@ class PoseOptimizerTest {
vk::blender_utils::loadBlenderDepthmap(dataset_dir + "/depth/frame_000001_0.depth", *cam_, depthmap_);
// detect features
- feature_detection::Corners corners;
- feature_detection::FastDetector detector;
- detector.detect(frame_->img_pyr_, frame_->fts_, Config::gridSize(),
- Config::nPyrLevels(), Config::triangMinCornerScore(), &corners);
+ feature_detection::FastDetector detector(
+ cam_->width(), cam_->height(), Config::gridSize(), Config::nPyrLevels());
+ detector.detect(frame_.get(), frame_->img_pyr_, Config::triangMinCornerScore(), frame_->fts_);
size_t n_fts = 0;
- for(feature_detection::Corners::iterator it=corners.begin(); it!=corners.end(); ++it)
- {
- // we must check again if the score is above the threshold because the Corners
- // vector is initialized with dummy corners
- if(it->score > Config::triangMinCornerScore())
- {
- Feature* ftr(new Feature(frame_.get(), Vector2d(it->x, it->y), it->level));
- Point* point(new Point(frame_->f2w(ftr->f*depthmap_.at(it->y, it->x))));
- ftr->point = point;
- ftr->frame = frame_.get();
- frame_->addFeature(ftr);
- ++n_fts;
- }
- }
+ std::for_each(frame_->fts_.begin(), frame_->fts_.end(), [&](Feature* i){
+ Point* point(new Point(frame_->f2w(i->f*depthmap_.at(i->px[1], i->px[0])), i));
+ i->point = point;
+ ++n_fts;
+ });
printf("Added %zu features to frame.\n", n_fts);
}
diff --git a/svo/test/test_sparse_img_align.cpp b/svo/test/test_sparse_img_align.cpp
index f719f72..fe1ece5 100644
--- a/svo/test/test_sparse_img_align.cpp
+++ b/svo/test/test_sparse_img_align.cpp
@@ -97,21 +97,16 @@ void SparseImgAlignTest::testSequence(
dataset_dir+"/depth/"+iter->image_name_+"_0.depth", *cam_, depthmap);
// extract features, generate features with 3D points
- svo::feature_detection::Corners corners;
feature_detector->detect(
- frame_ref_->img_pyr_, frame_ref_->fts_, svo::Config::gridSize(),
- svo::Config::nPyrLevels(), svo::Config::triangMinCornerScore(), &corners);
- for(svo::feature_detection::Corners::iterator corner=corners.begin(); corner!=corners.end(); ++corner)
- {
- svo::Feature* ftr = new svo::Feature(frame_ref_.get(), Eigen::Vector2d(corner->x, corner->y), corner->level);
- Eigen::Vector3d pt_pos_cur = ftr->f*depthmap.at(corner->y, corner->x);
+ frame_ref_.get(), frame_ref_->img_pyr_, svo::Config::triangMinCornerScore(), frame_ref_->fts_);
+ std::for_each(frame_ref_->fts_.begin(), frame_ref_->fts_.end(), [&](svo::Feature* i) {
+ Eigen::Vector3d pt_pos_cur = i->f*depthmap.at(i->px[1], i->px[0]);
Eigen::Vector3d pt_pos_w = frame_ref_->T_f_w_.inverse()*pt_pos_cur;
- svo::Point* pt = new svo::Point(pt_pos_w);
- ftr->point = pt;
- frame_ref_->addFeature(ftr);
- }
+ svo::Point* pt = new svo::Point(pt_pos_w, i);
+ i->point = pt;
+ });
- printf("Added %zu 3d pts to the reference frame.\n", corners.size());
+ printf("Added %zu 3d pts to the reference frame.\n", frame_ref_->nObs());
T_prev_w = frame_ref_->T_f_w_;
T_prevgt_w = T_gt_w;
continue;
@@ -158,10 +153,11 @@ int main(int argc, char** argv)
{
std::string experiment_name("flying_room_1_rig_1_fast_minlev0");
std::string dataset_dir(svo::test_utils::getDatasetDir() + "/flying_room_1_rig_1");
- svo::feature_detection::FastDetector detector;
svo::Config::triangMinCornerScore() = 20;
svo::Config::kltMinLevel() = 0;
SparseImgAlignTest test;
+ svo::feature_detection::FastDetector detector(
+ test.cam_->width(), test.cam_->height(), svo::Config::gridSize(), svo::Config::nPyrLevels());
test.testSequence(dataset_dir, experiment_name, &detector);
return 0;
}
diff --git a/svo_analysis/README.md b/svo_analysis/README.md
index 0a4deeb..07ae6eb 100644
--- a/svo_analysis/README.md
+++ b/svo_analysis/README.md
@@ -25,3 +25,27 @@ Afterwards run
rosrun svo_analysis benchmark.py flying_room_1_fast
The script will create a new folder in `svo_analysis/results/`, run svo on the dataset and save the tracefile in this folder and at the end it will run some scripts to generate plots from the tracefile.
+
+
+#### Dataset file
+
+A dataset with motion-capture-system ground-truth should contain the following files
+
+* _images.txt_
+
+ # timestamp img/imagename
+ 1329330952.725747585296631 img/00000.png
+ 1329330952.765715122222900 img/00001.png
+ 1329330952.805604219436646 img/00002.png
+ ...
+
+* _groundtruth.txt_: groundtruth poses as acquired from a motion capture system
+
+ # timestamp tx ty tz qx qy qz qw
+
+* _groundtruth_matched.txt_: the groundtruth pose associated to the images in images.txt. This file can be generated by the command:
+
+ cd my_dataset
+ rosrun svo_analysis associate.py images.txt groundtruth.txt
+
+
diff --git a/svo_analysis/experiments/synthetic.yaml b/svo_analysis/experiments/synthetic.yaml
index 5eb0402..e58ff7a 100644
--- a/svo_analysis/experiments/synthetic.yaml
+++ b/svo_analysis/experiments/synthetic.yaml
@@ -1,7 +1,10 @@
experiment_label: 'svo fast'
param_settings: vo_fast
datasets:
+ - 1404102247_fr1_straight
+ - 1404102220_fr1_straight
+ - 1404102129_fr1_straight
- 1404091756_fr1_straight
- 1404091807_fr1_straight_rot
- 1404091819_fr1_sine_2d
- - 1404091831_fr1_sine_3d
\ No newline at end of file
+ - 1404091831_fr1_sine_3d
diff --git a/svo_analysis/scripts/benchmark.py b/svo_analysis/scripts/benchmark.py
index a544ba2..c66f115 100755
--- a/svo_analysis/scripts/benchmark.py
+++ b/svo_analysis/scripts/benchmark.py
@@ -29,18 +29,28 @@ def run_experiment(dataset, params):
algo_params = yaml.load(open(algo_params_file,'r'))
# combine all parameters
- if dataset_params['rig_size'] == 1:
+ if 'rig_size' not in dataset_params or dataset_params['rig_size'] == 1:
params = dict(params.items() + algo_params.items() + dataset_params['cam0'].items())
else:
params = dict(params.items() + algo_params.items() + dataset_params.items())
if 'dataset_is_blender' in dataset_params:
- params['dataset_is_blender'] = True
+ params['dataset_is_blender'] = dataset_params['dataset_is_blender']
+ params['dataset_first_frame'] = dataset_params['dataset_first_frame']
# dump experiment params to file and copy the other parameter files:
params_dump_file = os.path.join(params['trace_dir'],'params.yaml')
with open(params_dump_file,'w') as outfile:
outfile.write(yaml.dump(params, default_flow_style=False))
- shutil.copyfile(dataset_params_file, os.path.join(params['trace_dir'], 'dataset_params.yaml'))
+ shutil.copyfile(dataset_params_file,
+ os.path.join(params['trace_dir'], 'dataset_params.yaml'))
+
+ # copy the groundtruth trajectory to the trace dir for later evaluation
+ if params['dataset_is_blender']:
+ shutil.copyfile(os.path.join(params['dataset_directory'], 'trajectory.txt'),
+ os.path.join(params['trace_dir'], 'groundtruth_matched.txt'))
+ else:
+ shutil.copyfile(os.path.join(params['dataset_directory'], 'groundtruth_matched.txt'),
+ os.path.join(params['trace_dir'], 'groundtruth_matched.txt'))
# execute ros node
node = ros_node.RosNode(args.version, args.executable)
diff --git a/svo_analysis/src/svo_analysis/analyse_trajectory.py b/svo_analysis/src/svo_analysis/analyse_trajectory.py
old mode 100644
new mode 100755
index f2a800e..a024b79
--- a/svo_analysis/src/svo_analysis/analyse_trajectory.py
+++ b/svo_analysis/src/svo_analysis/analyse_trajectory.py
@@ -1,179 +1,180 @@
-# -*- coding: utf-8 -*-
+#!/usr/bin/python
import os
import yaml
-from .tum_benchmark_tools import associate
+import argparse
+import numpy as np
import matplotlib.pyplot as plt
+import svo_analysis.tum_benchmark_tools.associate as associate
import vikit_py.transformations as transformations
import vikit_py.align_trajectory as align_trajectory
-import numpy as np
-
-# plot options
from matplotlib import rc
rc('font',**{'family':'serif','serif':['Cardo']})
rc('text', usetex=True)
def plot_translation_error(timestamps, translation_error, results_dir):
- fig = plt.figure(figsize=(8, 2.5))
- ax = fig.add_subplot(111, xlabel='time [s]', ylabel='position drift [mm]', xlim=[0,timestamps[-1]-timestamps[0]+4])
- ax.plot(timestamps-timestamps[0], translation_error[:,0]*1000, 'r-', label='x')
- ax.plot(timestamps-timestamps[0], translation_error[:,1]*1000, 'g-', label='y')
- ax.plot(timestamps-timestamps[0], translation_error[:,2]*1000, 'b-', label='z')
- ax.legend()
- fig.tight_layout()
- fig.savefig(results_dir+'/translation_error.pdf')
+ fig = plt.figure(figsize=(8, 2.5))
+ ax = fig.add_subplot(111, xlabel='time [s]', ylabel='position drift [mm]', xlim=[0,timestamps[-1]-timestamps[0]+4])
+ ax.plot(timestamps-timestamps[0], translation_error[:,0]*1000, 'r-', label='x')
+ ax.plot(timestamps-timestamps[0], translation_error[:,1]*1000, 'g-', label='y')
+ ax.plot(timestamps-timestamps[0], translation_error[:,2]*1000, 'b-', label='z')
+ ax.legend()
+ fig.tight_layout()
+ fig.savefig(results_dir+'/translation_error.pdf')
def plot_rotation_error(timestamps, rotation_error, results_dir):
- fig = plt.figure(figsize=(8, 2.5))
- ax = fig.add_subplot(111, xlabel='time [s]', ylabel='orientation drift [rad]', xlim=[0,timestamps[-1]-timestamps[0]+4])
- ax.plot(timestamps-timestamps[0], rotation_error[:,0], 'r-', label='yaw')
- ax.plot(timestamps-timestamps[0], rotation_error[:,1], 'g-', label='pitch')
- ax.plot(timestamps-timestamps[0], rotation_error[:,2], 'b-', label='roll')
- ax.legend()
- fig.tight_layout()
- fig.savefig(results_dir+'/orientation_error.pdf')
+ fig = plt.figure(figsize=(8, 2.5))
+ ax = fig.add_subplot(111, xlabel='time [s]', ylabel='orientation drift [rad]', xlim=[0,timestamps[-1]-timestamps[0]+4])
+ ax.plot(timestamps-timestamps[0], rotation_error[:,0], 'r-', label='yaw')
+ ax.plot(timestamps-timestamps[0], rotation_error[:,1], 'g-', label='pitch')
+ ax.plot(timestamps-timestamps[0], rotation_error[:,2], 'b-', label='roll')
+ ax.legend()
+ fig.tight_layout()
+ fig.savefig(results_dir+'/orientation_error.pdf')
-def analyse_trajectory(results_dir):
- # plot translation error
- data = np.loadtxt(os.path.join(results_dir, 'translation_error.txt'))
- timestamps = data[:,0]
- translation_error = data[:,1:4]
- plot_translation_error(timestamps, translation_error, results_dir)
+def analyse_trajectory(results_dir, n_align_frames = 20, use_hand_eye_calib = True):
- # plot orientation error
- data = np.loadtxt(os.path.join(results_dir, 'orientation_error.txt'))
- timestamps = data[:,0]
- orientation_error = data[:,1:4]
- plot_rotation_error(timestamps, orientation_error, results_dir)
+ if not os.path.isfile(os.path.join(results_dir, 'translation_error.txt')):
+
+ T_cm, T_mc = load_hand_eye_calib(results_dir)
+ print('loading datasets.')
+ t_es, p_es, q_es, t_gt, p_gt, q_gt = load_dataset(results_dir)
+
+ # align Sim3 to get scale
+ print('align Sim3 using '+str(n_align_frames)+' first frames.')
+ scale,rot,trans = align_trajectory.align_sim3(p_gt[0:n_align_frames,:], p_es[0:n_align_frames,:])
+ print 'scale = '+str(scale)
+ # get trafo between (v)ision and (o)ptitrack frame
+ p_es_aligned = np.zeros(np.shape(p_es))
+ if use_hand_eye_calib:
+ print q_gt[0,:]
+ print p_gt[0,:]
+ T_om = get_rigid_body_trafo(q_gt[0,:], p_gt[0,:])
+ T_vc = get_rigid_body_trafo(q_es[0,:], scale*p_es[0,:])
+ T_cv = transformations.inverse_matrix(T_vc)
+ T_ov = np.dot(T_om, np.dot(T_mc, T_cv))
+ print 'T_ov = ' + str(T_ov)
+
+ # apply transformation to estimated trajectory
+ q_es_aligned = np.zeros(np.shape(q_es))
+ rpy_es_aligned = np.zeros(np.shape(p_es))
+ rpy_gt = np.zeros(np.shape(p_es))
+ for i in range(np.shape(p_es)[0]):
+ T_vc = get_rigid_body_trafo(q_es[i,:],p_es[i,:])
+ T_vc[0:3,3] *= scale
+ T_om = np.dot(T_ov, np.dot(T_vc, T_cm))
+ p_es_aligned[i,:] = T_om[0:3,3]
+ q_es_aligned[i,:] = transformations.quaternion_from_matrix(T_om)
+ rpy_es_aligned[i,:] = transformations.euler_from_quaternion(q_es_aligned[i,:], 'rzyx')
+ rpy_gt[i,:] = transformations.euler_from_quaternion(q_gt[i,:], 'rzyx')
+
+ # plot position error (drift)
+ translation_error = (p_gt-p_es_aligned)
+ plot_translation_error(t_es, translation_error, results_dir)
+
+ # plot orientation error (drift)
+ orientation_error = (rpy_gt - rpy_es_aligned)
+ plot_rotation_error(t_es, orientation_error, results_dir)
+
+ # plot scale drift
+ motion_gt = np.diff(p_gt, 0)
+ motion_es = np.diff(p_es_aligned, 0)
+ dist_gt = np.sqrt(np.sum(np.multiply(motion_gt,motion_gt),1))
+ dist_es = np.sqrt(np.sum(np.multiply(motion_es,motion_es),1))
+ fig = plt.figure(figsize=(8,2.5))
+ ax = fig.add_subplot(111, xlabel='time [s]', ylabel='scale change [\%]', xlim=[0,t_es[-1]+4])
+ scale_drift = np.divide(dist_es,dist_gt)*100-100
+ ax.plot(t_es, scale_drift, 'b-')
+ fig.tight_layout()
+
+ else:
+ for i in range(np.shape(p_es)[0]):
+ p_es_aligned[i,:] = scale*rot.dot(p_es[i,:]) + trans
+
+ # plot trajectory
+ fig = plt.figure()
+ ax = fig.add_subplot(111, title='trajectory', aspect='equal', xlabel='x [m]', ylabel='y [m]')
+ ax.plot(p_es_aligned[:,0], p_es_aligned[:,1], 'b-', label='estimate')
+ ax.plot(p_gt[:,0], p_gt[:,1], 'r-', label='groundtruth')
+ #ax.plot(p_es_aligned[0:n_align_frames,0], p_es_aligned[0:n_align_frames,1], 'g-', linewidth=2, label='aligned')
+ ax.legend()
+ fig.tight_layout()
+ fig.savefig(results_dir+'/trajectory.pdf')
+
+
+
+ else:
+ data = np.loadtxt(os.path.join(results_dir, 'translation_error.txt'))
+ timestamps = data[:,0]
+ translation_error = data[:,1:4]
+ plot_translation_error(timestamps, translation_error, results_dir)
+
+ # plot orientation error
+ data = np.loadtxt(os.path.join(results_dir, 'orientation_error.txt'))
+ timestamps = data[:,0]
+ orientation_error = data[:,1:4]
+ plot_rotation_error(timestamps, orientation_error, results_dir)
+
+
def get_rigid_body_trafo(quat,trans):
- T = transformations.quaternion_matrix(quat)
- T[0:3,3] = trans
- return T
+ T = transformations.quaternion_matrix(quat)
+ T[0:3,3] = trans
+ return T
+
+def load_hand_eye_calib(results_dir):
+ P = yaml.load(open(os.path.join(results_dir, 'dataset_params.yaml'),'r'))['hand_eye_calib']
+
+ T_cm_quat = np.array([P['Tcm_qx'], P['Tcm_qy'], P['Tcm_qz'], P['Tcm_qw']])
+ T_cm_tran = np.array([P['Tcm_tx'], P['Tcm_ty'], P['Tcm_tz']])
+ T_cm = get_rigid_body_trafo(T_cm_quat, T_cm_tran)
+ T_mc = transformations.inverse_matrix(T_cm)
+ return T_cm, T_mc
+
+def load_dataset(results_dir):
+ """
+ Assumes that groundtruth measurements are already matched with the
+ images.
+ """
+ data_gt = open(os.path.join(results_dir, 'groundtruth_matched.txt')).read()
+ lines = data_gt.replace(","," ").replace("\t"," ").split("\n")
+ data_gt = np.array([[np.float(v.strip()) for i,v in enumerate(line.split(" ")) if i != 1 and v.strip()!=""] for line in lines if len(line)>0 and line[0]!="#"])
+ data_gt = [(float(l[0]),l[1:]) for l in data_gt]
+ data_gt = dict(data_gt)
+
+ data_es = open(os.path.join(results_dir, 'traj_estimate.txt')).read()
+ lines = data_es.replace(","," ").replace("\t"," ").split("\n")
+ data_es = np.array([[np.float(v.strip()) for v in line.split(" ") if v.strip()!=""] for line in lines if len(line)>0 and line[0]!="#"])
+ data_es = [(float(l[0]),l[1:]) for l in data_es]
+ data_es = dict(data_es)
+
+ matches = associate.associate(data_gt, data_es, 0.0, 0.0001)
+ p_gt = np.array([[np.float(value) for value in data_gt[a][0:3]] for a,b in matches])
+ q_gt = np.array([[np.float(value) for value in data_gt[a][3:7]] for a,b in matches])
+ p_es = np.array([[np.float(value) for value in data_es[b][0:3]] for a,b in matches])
+ q_es = np.array([[np.float(value) for value in data_es[b][3:7]] for a,b in matches])
+ t_gt = np.array([np.float(a) for a,b in matches])
+ t_es = np.array([np.float(b) for a,b in matches])
+
+ # set start time to zero
+ start_time = min(t_es[0], t_gt[0])
+ t_es -= start_time
+ t_gt -= start_time
+ return t_es, p_es, q_es, t_gt, p_gt, q_gt
+
if __name__ == '__main__':
- # config
- dataset='20131003_1937_nslam_i7_asl2_fast'
- n_align_frames = 100
- save = True
- results_dir = '../results/'+dataset
-
- # load dataset parameters
- params_stream = open(results_dir+'/params.yaml')
- params = yaml.load(params_stream)
-
- # set trajectory groundtruth and estimate file
- traj_groundtruth = params['dataset_directory']+'/groundtruth_filtered.txt'
- traj_estimate = results_dir+'/traj_estimate.txt'
- traj_estimate_rotated_filename = results_dir+'/traj_estimate_rotated.txt'
- translation_error_filename = results_dir+'/translation_error.txt'
- orientation_error_filename = results_dir+'/orientation_error.txt'
- scale_drift_filename = results_dir+'/scale_drift.txt'
-
- # load hand-eye calib: trafo between (m)arker and (c)amera.
- dataset_param_file = params['dataset_directory']+'/dataset_params.yaml'
- dataset_param_file_stream = open(dataset_param_file,'r')
- P = yaml.load(dataset_param_file_stream)
- T_cm_quat = np.array([P['calib_Tcm_qx'], P['calib_Tcm_qy'], P['calib_Tcm_qz'], P['calib_Tcm_qw']])
- T_cm_tran = np.array([P['calib_Tcm_tx'], P['calib_Tcm_ty'], P['calib_Tcm_tz']])
- T_cm = get_rigid_body_trafo(T_cm_quat, T_cm_tran)
- T_mc = transformations.inverse_matrix(T_cm)
-
- # load data
- data_gt = associate.read_file_list(traj_groundtruth)
- data_es = associate.read_file_list(traj_estimate)
-
- # find matches
- matches = associate.associate(data_gt, data_es, -P['cam_delay'], 0.02)
- p_gt = np.array([[float(value) for value in data_gt[a][0:3]] for a,b in matches])
- q_gt = np.array([[float(value) for value in data_gt[a][3:7]] for a,b in matches])
- p_es = np.array([[float(value) for value in data_es[b][0:3]] for a,b in matches])
- q_es = np.array([[float(value) for value in data_es[b][3:7]] for a,b in matches])
- t_gt = np.array([float(a) for a,b in matches])
- t_es = np.array([float(b) for a,b in matches])
- start_time = min(t_es[0], t_gt[0])
- t_es -= start_time
- t_gt -= start_time
-
- # align Sim3 to get scale
- if(n_align_frames > np.shape(matches)[0]):
- raise NameError('n_matches_for_scale is too large')
- scale,rot,trans = align_trajectory.align_sim3(p_gt, p_es, n_align_frames)
- print 'scale = '+str(scale)
-
- # get trafo between (v)ision and (o)ptitrack frame
- T_om = get_rigid_body_trafo(q_gt[0,:], p_gt[0,:])
- T_vc = get_rigid_body_trafo(q_es[0,:], scale*p_es[0,:])
- T_cv = transformations.inverse_matrix(T_vc)
- T_ov = np.dot(T_om, np.dot(T_mc, T_cv))
- print 'T_ov = ' + str(T_ov)
-
- p_es_aligned = np.zeros(np.shape(p_es))
- q_es_aligned = np.zeros(np.shape(q_es))
- rpy_es_aligned = np.zeros(np.shape(p_es))
- rpy_gt = np.zeros(np.shape(p_es))
-
- # rotate trajectory
- for i in range(np.shape(matches)[0]):
- T_vc = get_rigid_body_trafo(q_es[i,:],p_es[i,:])
- T_vc[0:3,3] *= scale
- T_om = np.dot(T_ov, np.dot(T_vc, T_cm))
- p_es_aligned[i,:] = T_om[0:3,3]
- q_es_aligned[i,:] = transformations.quaternion_from_matrix(T_om)
- rpy_es_aligned[i,:] = transformations.euler_from_quaternion(q_es_aligned[i,:], 'rzyx')
- rpy_gt[i,:] = transformations.euler_from_quaternion(q_gt[i,:], 'rzyx')
-
- if save:
- f = open(traj_estimate_rotated_filename,'w')
- for i in range(np.shape(p_es_aligned)[0]):
- f.write('%.7f %.5f %.5f %.5f %.5f %.5f %.5f %.5f\n' % (matches[i][1]-P['cam_delay'], p_es_aligned[i,0], p_es_aligned[i,1], p_es_aligned[i,2], q_es_aligned[i,0], q_es_aligned[i,1], q_es_aligned[i,2], q_es_aligned[i,3]))
- f.close()
-
- # plot trajectory
- fig = plt.figure()
- ax = fig.add_subplot(111, title='trajectory', aspect='equal', xlabel='x [m]', ylabel='Y [m]')
- ax.plot(p_es_aligned[:,0], p_es_aligned[:,1], 'b-', label='estimate')
- ax.plot(p_gt[:,0], p_gt[:,1], 'r-', label='groundtruth')
- ax.plot(p_es_aligned[0:n_align_frames,0], p_es_aligned[0:n_align_frames,1], 'g-', linewidth=2, label='aligned')
- fig.tight_layout()
- fig.savefig(results_dir+'/trajectory.pdf')
-
- # plot position error (drift)
- translation_error = (p_gt-p_es_aligned)
- plot_translation_error(t_es, translation_error, results_dir)
-
- if save:
- f = open(translation_error_filename,'w')
- for i in range(translation_error.shape[0]):
- f.write('%.7f %.5f %.5f %.5f\n'%(t_es[i], translation_error[i,0], translation_error[i,1], translation_error[i,2]))
- f.close()
-
- # plot orientation error (drift)
- orientation_error = (rpy_gt - rpy_es_aligned)
- plot_rotation_error(t_es, orientation_error, results_dir)
-
- if save:
- f = open(orientation_error_filename,'w')
- for i in range(orientation_error.shape[0]):
- f.write('%.7f %.5f %.5f %.5f\n'%(t_es[i], orientation_error[i,0], orientation_error[i,1], orientation_error[i,2]))
- f.close()
-
- # plot scale drift
- motion_gt = np.diff(p_gt, 0)
- motion_es = np.diff(p_es_aligned, 0)
- dist_gt = np.sqrt(np.sum(np.multiply(motion_gt,motion_gt),1))
- dist_es = np.sqrt(np.sum(np.multiply(motion_es,motion_es),1))
- fig = plt.figure(figsize=(8,2.5))
- ax = fig.add_subplot(111, xlabel='time [s]', ylabel='scale change [\%]', xlim=[0,t_es[-1]+4])
- scale_drift = np.divide(dist_es,dist_gt)*100-100
- ax.plot(t_es, scale_drift, 'b-')
- fig.tight_layout()
-
- if save:
- fig.savefig(results_dir+'/scale_drift.pdf')
- f = open(scale_drift_filename,'w')
- for i in range(orientation_error.shape[0]):
- f.write('%.7f %.5f\n'%(t_es[i], scale_drift[i]))
- f.close()
+ # parse command line
+ parser = argparse.ArgumentParser(description='''
+ Analyse trajectory
+ ''')
+ parser.add_argument('results_dir', help='folder with the results')
+ parser.add_argument('--use_hand_eye_calib', help='', action='store_true')
+ parser.add_argument('--n_align_frames', help='', default=20)
+ args = parser.parse_args()
+ print('analyse trajectory for dataset: '+str(args.results_dir))
+ analyse_trajectory(args.results_dir,
+ n_align_frames = int(args.n_align_frames),
+ use_hand_eye_calib = args.use_hand_eye_calib)
\ No newline at end of file
diff --git a/svo_analysis/src/svo_analysis/tum_benchmark_tools/associate.py b/svo_analysis/src/svo_analysis/tum_benchmark_tools/associate.py
index 554314a..01bea59 100755
--- a/svo_analysis/src/svo_analysis/tum_benchmark_tools/associate.py
+++ b/svo_analysis/src/svo_analysis/tum_benchmark_tools/associate.py
@@ -108,7 +108,6 @@ if __name__ == '__main__':
''')
parser.add_argument('first_file', help='first text file (format: timestamp data)')
parser.add_argument('second_file', help='second text file (format: timestamp data)')
- parser.add_argument('--first_only', help='only output associated lines from first file', action='store_true')
parser.add_argument('--offset', help='time offset added to the timestamps of the second file (default: 0.0)',default=0.0)
parser.add_argument('--max_difference', help='maximally allowed time difference for matching entries (default: 0.02)',default=0.02)
args = parser.parse_args()
@@ -117,12 +116,15 @@ if __name__ == '__main__':
second_list = read_file_list(args.second_file)
matches = associate(first_list, second_list,float(args.offset),float(args.max_difference))
-
- if args.first_only:
- for a,b in matches:
- print("%f %s"%(a," ".join(first_list[a])))
- else:
- for a,b in matches:
- print("%f %s %f %s"%(a," ".join(first_list[a]),b-float(args.offset)," ".join(second_list[b])))
-
+
+ filename = 'groundtruth_matched.txt'
+ associates_file = open(filename, 'w')
+ for a,b in matches:
+ img = first_list[a]
+ pos = second_list[b]
+ associates_file.write('%.6f %s %s %s %s %s %s %s %s\n' %
+ (a, img[0],
+ pos[0], pos[1], pos[2],
+ pos[3], pos[4], pos[5], pos[6]))
+ print('Wrote matches to file: ' + filename)
diff --git a/svo_ros/include/svo_ros/visualizer.h b/svo_ros/include/svo_ros/visualizer.h
index 1a7df1f..aa6146a 100644
--- a/svo_ros/include/svo_ros/visualizer.h
+++ b/svo_ros/include/svo_ros/visualizer.h
@@ -42,8 +42,7 @@ class FrameHandlerMono;
typedef boost::shared_ptr FramePtr;
-/// This class bundles all functions to publish visualisation messages,
-/// trace data and output debugging information.
+/// This class bundles all functions to publish visualisation messages.
class Visualizer
{
public:
@@ -59,14 +58,30 @@ public:
ros::Publisher pub_dense_;
image_transport::Publisher pub_images_;
tf::TransformBroadcaster br_;
+ bool publish_world_in_cam_frame_;
+ SE3 T_world_from_vision_;
Visualizer();
+
~Visualizer() {};
- void visualizeMarkers(const FramePtr& frame, const set& core_kfs, const Map& map);
- void publishMinimal(const cv::Mat& img, const FramePtr& frame, const FrameHandlerMono& slam, const double timestamp);
+
+ void publishMinimal(
+ const cv::Mat& img,
+ const FramePtr& frame,
+ const FrameHandlerMono& slam,
+ const double timestamp);
+
+ void visualizeMarkers(
+ const FramePtr& frame,
+ const set& core_kfs,
+ const Map& map);
+
void publishMapRegion(set frames);
+
void removeDeletedPts(const Map& map);
+
void displayKeyframeWithMps(const FramePtr& frame, int ts);
+
void exportToDense(const FramePtr& frame);
};
diff --git a/svo_ros/src/benchmark_node.cpp b/svo_ros/src/benchmark_node.cpp
index 4083bf2..0c643eb 100644
--- a/svo_ros/src/benchmark_node.cpp
+++ b/svo_ros/src/benchmark_node.cpp
@@ -124,20 +124,20 @@ void BenchmarkNode::runBenchmark(const std::string& dataset_dir)
dataset_reader.readAllEntries(dataset);
// process dataset
+ int first_frame = vk::getParam("svo/dataset_first_frame", 0);
for(auto it = dataset.begin(); it != dataset.end() && ros::ok(); ++it, ++frame_count_)
{
- // Read image
+ if(frame_count_ < first_frame)
+ continue;
std::string img_filename(dataset_dir + "/" + it->image_name_);
cv::Mat img(cv::imread(img_filename, 0));
if(img.empty()) {
SVO_ERROR_STREAM("Reading image "<addImage(img, it->timestamp_);
-
visualizer_.publishMinimal(img, vo_->lastFrame(), *vo_, it->timestamp_);
-
+ visualizer_.visualizeMarkers(vo_->lastFrame(), vo_->coreKeyframes(), vo_->map());
if(vo_->stage() == svo::FrameHandlerMono::STAGE_DEFAULT_FRAME)
tracePose(vo_->lastFrame()->T_f_w_.inverse(), it->timestamp_);
}
@@ -188,32 +188,26 @@ void BenchmarkNode::runBlenderBenchmark(const std::string& dataset_dir)
dataset_dir+"/depth/"+it->image_name_+"_0.depth", *cam_, depthmap);
// extract features, generate features with 3D points
- svo::feature_detection::FastDetector detector;
- svo::feature_detection::Corners corners;
- detector.detect(frame_ref->img_pyr_, frame_ref->fts_, svo::Config::gridSize(),
- svo::Config::nPyrLevels(), svo::Config::triangMinCornerScore(), &corners);
- for(auto corner=corners.begin(); corner!=corners.end(); ++corner)
- {
- if(corner->score < Config::triangMinCornerScore())
- continue;
- svo::Feature* ftr = new svo::Feature(frame_ref.get(), Eigen::Vector2d(corner->x, corner->y), corner->level);
- Eigen::Vector3d pt_pos_cur = ftr->f*depthmap.at(corner->y, corner->x);
+ svo::feature_detection::FastDetector detector(
+ cam_->width(), cam_->height(), svo::Config::gridSize(), svo::Config::nPyrLevels());
+ detector.detect(frame_ref.get(), frame_ref->img_pyr_, svo::Config::triangMinCornerScore(), frame_ref->fts_);
+ std::for_each(frame_ref->fts_.begin(), frame_ref->fts_.end(), [&](Feature* ftr) {
+ Eigen::Vector3d pt_pos_cur = ftr->f*depthmap.at(ftr->px[1], ftr->px[0]);
Eigen::Vector3d pt_pos_world = frame_ref->T_f_w_.inverse()*pt_pos_cur;
- svo::Point* point = new svo::Point(pt_pos_world);
+ svo::Point* point = new svo::Point(pt_pos_world, ftr);
ftr->point = point;
- ftr->point->addFrameRef(ftr);
- frame_ref->addFeature(ftr);
- }
- SVO_INFO_STREAM("Added "<nObs()<<" 3d pts to the reference frame.");
vo_->setFirstFrame(frame_ref);
SVO_INFO_STREAM("Set reference frame.");
- continue;
}
-
- SVO_DEBUG_STREAM("Processing image "<image_name_<<".");
- vo_->addImage(img, it->timestamp_);
- visualizer_.publishMinimal(img, vo_->lastFrame(), *vo_, it->timestamp_);
- visualizer_.visualizeMarkers(vo_->lastFrame(), vo_->coreKeyframes(), vo_->map());
+ else
+ {
+ SVO_DEBUG_STREAM("Processing image "<image_name_<<".");
+ vo_->addImage(img, it->timestamp_);
+ visualizer_.publishMinimal(img, vo_->lastFrame(), *vo_, it->timestamp_);
+ visualizer_.visualizeMarkers(vo_->lastFrame(), vo_->coreKeyframes(), vo_->map());
+ }
if(vo_->stage() != svo::FrameHandlerMono::STAGE_DEFAULT_FRAME)
{
diff --git a/svo_ros/src/visualizer.cpp b/svo_ros/src/visualizer.cpp
index eda01c6..32ac3e1 100644
--- a/svo_ros/src/visualizer.cpp
+++ b/svo_ros/src/visualizer.cpp
@@ -43,7 +43,9 @@ Visualizer() :
trace_id_(0),
img_pub_level_(vk::getParam("svo/image_publishing_level", 0)),
img_pub_nth_(vk::getParam("svo/publish_every_nth_img", 1)),
- dense_pub_nth_(vk::getParam("svo/publish_every_nth_dense_input", 1))
+ dense_pub_nth_(vk::getParam("svo/publish_every_nth_dense_input", 1)),
+ T_world_from_vision_(Matrix3d::Identity(), Vector3d::Zero()),
+ publish_world_in_cam_frame_(vk::getParam("svo/publish_world_in_cam_frame", true))
{
// Init ROS Marker Publishers
pub_frames_ = pnh_.advertise("keyframes", 10);
@@ -57,25 +59,11 @@ Visualizer() :
pub_images_ = it.advertise("image", 10);
}
-void Visualizer::
-visualizeMarkers(const FramePtr& frame, const set& core_kfs, const Map& map)
-{
- if(frame != NULL)
- {
- // publish camera pose tf
- vk::output_helper::publishTfTransform(frame->T_f_w_, ros::Time(frame->timestamp_), "cam_pos", "world", br_);
- if(pub_frames_.getNumSubscribers() > 0 || pub_points_.getNumSubscribers() > 0)
- {
- vk::output_helper::publishHexacopterMarker(pub_frames_, "cam_pos", "cams", ros::Time(frame->timestamp_), 1, 0, 0.3, Vector3d(0.,0.,1.));
- vk::output_helper::publishPointMarker(pub_points_, frame->pos(), "trajectory", ros::Time::now(), trace_id_, 0, 0.006, Vector3d(0.,0.,0.5));
- publishMapRegion(core_kfs);
- removeDeletedPts(map);
- }
- }
-}
-
-void Visualizer::
-publishMinimal(const cv::Mat& img, const FramePtr& frame, const FrameHandlerMono& slam, const double timestamp)
+void Visualizer::publishMinimal(
+ const cv::Mat& img,
+ const FramePtr& frame,
+ const FrameHandlerMono& slam,
+ const double timestamp)
{
++trace_id_;
std_msgs::Header header_msg;
@@ -83,7 +71,7 @@ publishMinimal(const cv::Mat& img, const FramePtr& frame, const FrameHandlerMono
header_msg.seq = trace_id_;
header_msg.stamp = ros::Time(timestamp);
- // publish svo info msgs
+ // publish info msg.
if(pub_info_.getNumSubscribers() > 0)
{
svo_msgs::Info msg_info;
@@ -101,17 +89,31 @@ publishMinimal(const cv::Mat& img, const FramePtr& frame, const FrameHandlerMono
pub_info_.publish(msg_info);
}
- // publish pyramid-image every nth-frame
- if(frame != NULL && img_pub_nth_ > 0
- && trace_id_%img_pub_nth_ == 0 && pub_images_.getNumSubscribers() > 0)
+ if(frame == 0)
+ {
+ if(pub_images_.getNumSubscribers() > 0 && slam.stage() == FrameHandlerBase::STAGE_PAUSED)
+ {
+ // Display image when slam is not running.
+ cv_bridge::CvImage img_msg;
+ img_msg.header.stamp = ros::Time::now();
+ img_msg.header.frame_id = "/image";
+ img_msg.image = img;
+ img_msg.encoding = sensor_msgs::image_encodings::MONO8;
+ pub_images_.publish(img_msg.toImageMsg());
+ }
+ return;
+ }
+
+ // Publish pyramid-image every nth-frame.
+ if(img_pub_nth_ > 0 && trace_id_%img_pub_nth_ == 0 && pub_images_.getNumSubscribers() > 0)
{
const int scale = (1<img_pyr_[img_pub_level_].size(), CV_8UC3);
cv::cvtColor(frame->img_pyr_[img_pub_level_], img_rgb, CV_GRAY2RGB);
- // during initialization, draw lines
if(slam.stage() == FrameHandlerBase::STAGE_SECOND_FRAME)
{
+ // During initialization, draw lines.
const vector& px_ref(slam.initFeatureTrackRefPx());
const vector& px_cur(slam.initFeatureTrackCurPx());
for(vector::const_iterator it_ref=px_ref.begin(), it_cur=px_cur.begin();
@@ -121,7 +123,6 @@ publishMinimal(const cv::Mat& img, const FramePtr& frame, const FrameHandlerMono
cv::Point2f(it_ref->x/scale, it_ref->y/scale), cv::Scalar(0,255,0), 2);
}
- // draw features on frame
if(img_pub_level_ == 0)
for(Features::iterator it=frame->fts_.begin(); it!=frame->fts_.end(); ++it)
cv::rectangle(img_rgb,
@@ -141,30 +142,35 @@ publishMinimal(const cv::Mat& img, const FramePtr& frame, const FrameHandlerMono
cv::Point2f((*it)->px[0]/scale, (*it)->px[1]/scale),
cv::Scalar(0,255,0), CV_FILLED);
- // publish image
cv_bridge::CvImage img_msg;
img_msg.header = header_msg;
img_msg.image = img_rgb;
img_msg.encoding = sensor_msgs::image_encodings::BGR8;
pub_images_.publish(img_msg.toImageMsg());
}
- else if(frame == NULL && pub_images_.getNumSubscribers() > 0 && slam.stage() == FrameHandlerBase::STAGE_PAUSED)
- {
- // display image when slam is not running
- cv_bridge::CvImage img_msg;
- img_msg.header.stamp = ros::Time::now();
- img_msg.header.frame_id = "/image";
- img_msg.image = img;
- img_msg.encoding = sensor_msgs::image_encodings::MONO8;
- pub_images_.publish(img_msg.toImageMsg());
- }
- // publish pose (world in camera frame)
- if(pub_pose_.getNumSubscribers() > 0 && frame != NULL)
+ if(pub_pose_.getNumSubscribers() > 0)
{
+ Quaterniond q;
+ Vector3d p;
+ Matrix Cov;
+ if(publish_world_in_cam_frame_)
+ {
+ // publish world in cam frame
+ SE3 T_cam_from_world(frame->T_f_w_* T_world_from_vision_);
+ q = Quaterniond(T_cam_from_world.rotation_matrix());
+ p = T_cam_from_world.translation();
+ Cov = frame->Cov_;
+ }
+ else
+ {
+ // publish cam in world frame
+ SE3 T_world_from_cam(T_world_from_vision_*frame->T_f_w_.inverse());
+ q = Quaterniond(T_world_from_cam.rotation_matrix()*T_world_from_vision_.rotation_matrix().transpose());
+ p = T_world_from_cam.translation();
+ Cov = T_world_from_cam.Adj()*frame->Cov_*T_world_from_cam.inverse().Adj();
+ }
geometry_msgs::PoseWithCovarianceStampedPtr msg_pose(new geometry_msgs::PoseWithCovarianceStamped);
- Quaterniond q(frame->T_f_w_.rotation_matrix());
- Vector3d p(frame->T_f_w_.translation());
msg_pose->header = header_msg;
msg_pose->pose.pose.position.x = p[0];
msg_pose->pose.pose.position.y = p[1];
@@ -174,13 +180,36 @@ publishMinimal(const cv::Mat& img, const FramePtr& frame, const FrameHandlerMono
msg_pose->pose.pose.orientation.z = q.z();
msg_pose->pose.pose.orientation.w = q.w();
for(size_t i=0; i<36; ++i)
- msg_pose->pose.covariance[i] = frame->Cov_(i%6, i/6);
+ msg_pose->pose.covariance[i] = Cov(i%6, i/6);
pub_pose_.publish(msg_pose);
}
}
-void Visualizer::
-publishMapRegion(set frames)
+void Visualizer::visualizeMarkers(
+ const FramePtr& frame,
+ const set& core_kfs,
+ const Map& map)
+{
+ if(frame != NULL)
+ {
+ vk::output_helper::publishTfTransform(
+ frame->T_f_w_*T_world_from_vision_.inverse(),
+ ros::Time(frame->timestamp_), "cam_pos", "world", br_);
+ if(pub_frames_.getNumSubscribers() > 0 || pub_points_.getNumSubscribers() > 0)
+ {
+ vk::output_helper::publishHexacopterMarker(
+ pub_frames_, "cam_pos", "cams", ros::Time(frame->timestamp_),
+ 1, 0, 0.3, Vector3d(0.,0.,1.));
+ vk::output_helper::publishPointMarker(
+ pub_points_, T_world_from_vision_*frame->pos(), "trajectory",
+ ros::Time::now(), trace_id_, 0, 0.006, Vector3d(0.,0.,0.5));
+ publishMapRegion(core_kfs);
+ removeDeletedPts(map);
+ }
+ }
+}
+
+void Visualizer::publishMapRegion(set frames)
{
if(pub_points_.getNumSubscribers() > 0)
{
@@ -190,8 +219,7 @@ publishMapRegion(set frames)
}
}
-void Visualizer::
-removeDeletedPts(const Map& map)
+void Visualizer::removeDeletedPts(const Map& map)
{
if(pub_points_.getNumSubscribers() > 0)
{
@@ -200,11 +228,13 @@ removeDeletedPts(const Map& map)
}
}
-void Visualizer::
-displayKeyframeWithMps(const FramePtr& frame, int ts)
+void Visualizer::displayKeyframeWithMps(const FramePtr& frame, int ts)
{
// publish keyframe
- vk::output_helper::publishFrameMarker(pub_frames_, frame->T_f_w_.inverse().rotation_matrix(), frame->pos(), "kfs", ros::Time::now(), frame->id_*10, 0, 0.015);
+ SE3 T_world_cam(T_world_from_vision_*frame->T_f_w_.inverse());
+ vk::output_helper::publishFrameMarker(
+ pub_frames_, T_world_cam.rotation_matrix(),
+ T_world_cam.translation(), "kfs", ros::Time::now(), frame->id_*10, 0, 0.015);
// publish point cloud and links
for(Features::iterator it=frame->fts_.begin(); it!=frame->fts_.end(); ++it)
@@ -215,13 +245,14 @@ displayKeyframeWithMps(const FramePtr& frame, int ts)
if((*it)->point->last_published_ts_ == ts)
continue;
- vk::output_helper::publishPointMarker(pub_points_, (*it)->point->pos_, "pts", ros::Time::now(), (*it)->point->id_, 0, 0.005, Vector3d(1.0, 0., 1.0));
+ vk::output_helper::publishPointMarker(
+ pub_points_, T_world_from_vision_*(*it)->point->pos_, "pts",
+ ros::Time::now(), (*it)->point->id_, 0, 0.005, Vector3d(1.0, 0., 1.0));
(*it)->point->last_published_ts_ = ts;
}
}
-void Visualizer::
-exportToDense(const FramePtr& frame)
+void Visualizer::exportToDense(const FramePtr& frame)
{
// publish air_ground_msgs
if(frame != NULL && dense_pub_nth_ > 0
@@ -246,8 +277,8 @@ exportToDense(const FramePtr& frame)
if((*it)->point==NULL)
continue;
Vector3d pos = frame->T_f_w_*(*it)->point->pos_;
- min_z = min(pos[2], min_z);
- max_z = max(pos[2], max_z);
+ min_z = fmin(pos[2], min_z);
+ max_z = fmax(pos[2], max_z);
}
msg.min_depth = (float) min_z;
msg.max_depth = (float) max_z;
diff --git a/svo_ros/src/vo_node.cpp b/svo_ros/src/vo_node.cpp
index 9dc31dd..3dfdc92 100644
--- a/svo_ros/src/vo_node.cpp
+++ b/svo_ros/src/vo_node.cpp
@@ -74,18 +74,17 @@ VoNode() :
if(!vk::camera_loader::loadFromRosNs("svo", cam_))
throw std::runtime_error("Camera model not correctly specified.");
- // Init camera
- vo_ = new svo::FrameHandlerMono(cam_);
-
- // Set initial position and orientation
- Sophus::SE3 T_cam_from_world(
+ // Get initial position and orientation
+ visualizer_.T_world_from_vision_ = Sophus::SE3(
vk::rpy2dcm(Vector3d(vk::getParam("svo/init_rx", 0.0),
vk::getParam("svo/init_ry", 0.0),
vk::getParam("svo/init_rz", 0.0))),
Eigen::Vector3d(vk::getParam("svo/init_tx", 0.0),
vk::getParam("svo/init_ty", 0.0),
vk::getParam("svo/init_tz", 0.0)));
- vo_->setInitialPose(T_cam_from_world);
+
+ // Init VO and start
+ vo_ = new svo::FrameHandlerMono(cam_);
vo_->start();
}