// 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 #include #include #include #include #include #include #include #include #include #include #include namespace svo { class BenchmarkNode { svo::FrameHandlerMono* vo_; svo::Visualizer visualizer_; int frame_count_; std::ofstream trace_est_pose_; std::ofstream trace_trans_error_; std::ofstream trace_rot_error_; std::ofstream trace_depth_error_; vk::AbstractCamera* cam_; public: BenchmarkNode(ros::NodeHandle& nh); ~BenchmarkNode(); void tracePose(const SE3& T_w_f, const double timestamp); void tracePoseError(const SE3& T_f_gt, const double timestamp); void traceDepthError(const FramePtr& frame, const cv::Mat& depthmap); void runBenchmark(const std::string& dataset_dir); void runBlenderBenchmark(const std::string& dataset_dir); }; BenchmarkNode::BenchmarkNode(ros::NodeHandle& nh) : vo_(NULL), frame_count_(0) { // Create Camera if(!vk::camera_loader::loadFromRosNs("svo", cam_)) throw std::runtime_error("Camera model not correctly specified."); // create pose tracefile std::string trace_est_name(Config::traceDir() + "/traj_estimate.txt"); trace_est_pose_.open(trace_est_name.c_str()); if(trace_est_pose_.fail()) throw std::runtime_error("Could not create tracefile. Does folder exist?"); // Initialize VO vo_ = new svo::FrameHandlerMono(cam_); vo_->start(); } BenchmarkNode::~BenchmarkNode() { delete vo_; delete cam_; } void BenchmarkNode::tracePose(const SE3& T_w_f, const double timestamp) { Quaterniond q(T_w_f.unit_quaternion()); Vector3d p(T_w_f.translation()); trace_est_pose_.precision(15); trace_est_pose_.setf(std::ios::fixed, std::ios::floatfield ); trace_est_pose_ << timestamp << " "; trace_est_pose_.precision(6); trace_est_pose_ << p.x() << " " << p.y() << " " << p.z() << " " << q.x() << " " << q.y() << " " << q.z() << " " << q.w() << std::endl; } void BenchmarkNode::tracePoseError(const SE3& T_f_gt, const double timestamp) { Vector3d et(T_f_gt.translation()); // translation error trace_trans_error_.precision(15); trace_trans_error_.setf(std::ios::fixed, std::ios::floatfield ); trace_trans_error_ << timestamp << " "; trace_trans_error_.precision(6); trace_trans_error_ << et.x() << " " << et.y() << " " << et.z() << " " << std::endl; Vector3d er(vk::dcm2rpy(T_f_gt.rotation_matrix())); // rotation error in roll-pitch-yaw trace_rot_error_.precision(15); trace_rot_error_.setf(std::ios::fixed, std::ios::floatfield ); trace_rot_error_ << timestamp << " "; trace_rot_error_.precision(6); trace_rot_error_ << er.x() << " " << er.y() << " " << er.z() << " " << std::endl; } void BenchmarkNode::traceDepthError(const FramePtr& frame, const cv::Mat& depthmap) { trace_depth_error_.precision(6); std::for_each(frame->fts_.begin(), frame->fts_.end(), [&](Feature* ftr){ if(ftr->point != NULL) { double depth_estimated = (ftr->point->pos_-frame->pos()).norm(); double depth_true = depthmap.at((int) ftr->px[1], (int) ftr->px[0]); trace_depth_error_ << frame->id_ << " " << depth_estimated-depth_true << std::endl; } }); } void BenchmarkNode::runBenchmark(const std::string& dataset_dir) { // create image reader and load dataset std::string filename_benchmark(dataset_dir + "/images.txt"); vk::FileReader dataset_reader(filename_benchmark); dataset_reader.skipComments(); if(!dataset_reader.next()) { SVO_ERROR_STREAM("Failed opening dataset: "< dataset; 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_) { 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_); } } void BenchmarkNode::runBlenderBenchmark(const std::string& dataset_dir) { // create image reader and load dataset std::string filename_benchmark(dataset_dir + "/trajectory.txt"); vk::FileReader dataset_reader(filename_benchmark); dataset_reader.skipComments(); if(!dataset_reader.next()) { SVO_ERROR_STREAM("Failed opening dataset: "< dataset; dataset_reader.readAllEntries(dataset); // create tracefiles trace_trans_error_.open(Config::traceDir() + "/translation_error.txt"); trace_rot_error_.open(Config::traceDir() + "/orientation_error.txt"); trace_depth_error_.open(Config::traceDir() + "/depth_error.txt"); if(trace_trans_error_.fail() || trace_rot_error_.fail() || trace_depth_error_.fail()) throw std::runtime_error("Could not create tracefile. Does folder exist?"); // process dataset for(auto it = dataset.begin(); it != dataset.end() && ros::ok(); ++it, ++frame_count_) { // Read image, ground-truth depth-map and ground-truth pose std::string img_filename(dataset_dir + "/img/" + it->image_name_ + "_0.png"); cv::Mat img(cv::imread(img_filename, 0)); if(img.empty()) { SVO_ERROR_STREAM("Reading image "<image_name_+"_0.depth", *cam_, depthmap); Sophus::SE3 T_w_gt(it->q_, it->t_); // Set reference frame with depth if(frame_count_ == 0) { // set reference frame at ground-truth pose FramePtr frame_ref(new Frame(cam_, img, it->timestamp_)); frame_ref->T_f_w_ = T_w_gt.inverse(); // extract features, generate features with 3D points 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, ftr); ftr->point = point; }); SVO_INFO_STREAM("Added "<nObs()<<" 3d pts to the reference frame."); vo_->setFirstFrame(frame_ref); SVO_INFO_STREAM("Set reference frame."); } 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) { SVO_ERROR_STREAM("SVO failed before entire dataset could be processed."); break; } // Compute pose error and trace to file Sophus::SE3 T_f_gt(vo_->lastFrame()->T_f_w_*T_w_gt); tracePoseError(T_f_gt, it->timestamp_); tracePose(vo_->lastFrame()->T_f_w_.inverse(), it->timestamp_); traceDepthError(vo_->lastFrame(), depthmap); } } } // namespace svo int main(int argc, char** argv) { ros::init(argc, argv, "svo"); ros::NodeHandle nh; svo::BenchmarkNode benchmark(nh); std::string benchmark_dir(vk::getParam("svo/dataset_directory")); if(vk::getParam("svo/dataset_is_blender", false)) benchmark.runBlenderBenchmark(benchmark_dir); else benchmark.runBenchmark(benchmark_dir); printf("BenchmarkNode finished.\n"); return 0; }