00001
00008 #ifndef VSLAM_VO_H_INCLUDED
00009 #define VSLAM_VO_H_INCLUDED
00010 #include "utils.h"
00011 #include "pose_graph.h"
00012 #include "vis.h"
00013 #include "data_dump.h"
00014 #include "landmark_manager.h"
00015
00016 #include <vector>
00017 #include <deque>
00018 #include <string>
00019 #include <iostream>
00020 #include <fstream>
00021 #include <opencv2/core/core.hpp>
00022 #include <ros/console.h>
00023 #include <boost/filesystem.hpp>
00024 #include <boost/lexical_cast.hpp>
00025 #include "graph_opt.h"
00026
00027 #include "yaml-cpp/yaml.h"
00028
00029 namespace vslam{
00030
00037 template<class PoseGraphT> class VodomLogger{
00038 public:
00039 virtual void doLogging(const PoseGraphT& graph) = 0;
00040 };
00041
00042
00043 template<class PoseGraphT>
00044 class DummyVodomLogger : public VodomLogger<PoseGraphT>{
00045 public:
00046 virtual void doLogging(const PoseGraphT& graph){ }
00047 };
00048
00049 template<class PoseGraphT>
00050 class VertexDebugInfoLogger : public VodomLogger<PoseGraphT>{
00051 public:
00052 virtual void doLogging(const PoseGraphT& graph){
00053 YAML::Emitter yaml_out;
00054 typedef typename boost::graph_traits<PoseGraphT>::vertex_iterator VIT;
00055 VIT vb, ve;
00056 tie(vb, ve) = vertices(graph);
00057 yaml_out << YAML::BeginSeq;
00058 for(;vb!=ve; vb++){
00059
00060 yaml_out << YAML::BeginSeq;
00061 yaml_out << get(vertex_timestamp, graph, *vb);
00062 yaml_out << get(vertex_debug_info, graph, *vb).double_propmap;
00063 yaml_out << YAML::EndSeq;
00064
00065 }
00066 yaml_out << YAML::EndSeq;
00067
00068 boost::filesystem::path p(debug_output_path.native());
00069 p /= "vertex_debug_info.yaml";
00070 std::ofstream f(p.native().c_str());
00071 f << yaml_out.c_str();
00072 f.close();
00073 }
00074 };
00075
00076
00088 template<template<class> class NodeBuilderTT, class PoseGraphT> class VisualOdometry{
00089 protected:
00090 shared_ptr<NodeBuilderTT<PoseGraphT> > nbuilder;
00091 shared_ptr<EdgeBuilder<PoseGraphT> > ebuilder;
00092 PoseGraphT graph;
00093 Points<PoseGraphT> points;
00094
00095 typedef typename graph_traits<PoseGraphT>::vertex_descriptor VertexD;
00096 VertexD vd;
00097 VertexD pvd;
00103 std::deque<VertexD> past_vertices;
00105 size_t target_past_verticies_size;
00106
00107 YAML::Emitter yaml_out;
00108 shared_ptr<VodomLogger<PoseGraphT> > logger;
00109
00110 public:
00118 VisualOdometry(
00119 shared_ptr<NodeBuilderTT<PoseGraphT> > _nbuilder,
00120 shared_ptr<EdgeBuilder<PoseGraphT> > _ebuilder,
00121 size_t _target_past_verticies_size = 2,
00122 shared_ptr<VodomLogger<PoseGraphT> > _logger= shared_ptr<VodomLogger<PoseGraphT> >()) :
00123 nbuilder(_nbuilder),
00124 ebuilder(_ebuilder),
00125 target_past_verticies_size(_target_past_verticies_size) {
00126 assert(_target_past_verticies_size > 0 && _target_past_verticies_size < 10);
00127 logger = _logger;
00128 if(!logger) logger.reset(new DummyVodomLogger<PoseGraphT>());
00129 }
00130
00138 shared_ptr<SE3> processPose(
00139 typename NodeBuilderTT<PoseGraphT>::InputType input,
00140 int pose_id,
00141 uint64_t pose_timestamp,
00142 std::deque<SE3>& past_poses,
00143 bool debug_images=false,
00144 bool trajectory_file=false){
00145 ros::WallTime all_start_time = ros::WallTime::now();
00146 shared_ptr<VertexD> vdp = this->processPose0(
00147 input, pose_id, pose_timestamp, debug_images, trajectory_file);
00148 if(vdp){
00149 past_vertices.push_front(*vdp);
00150 while(past_vertices.size() > target_past_verticies_size){
00151 past_vertices.pop_back();
00152 }
00153 past_poses.clear();
00154 BOOST_FOREACH(VertexD v, past_vertices){
00155 past_poses.push_back(SE3(get(vertex_transform, this->graph, v)));
00156 }
00157 Vector3d t1, t2;
00158 SE3 T_w_vd=get(vertex_debug_info, graph, past_vertices.front()).gt_T_wc;
00159 SE3 T_w_pvd=get(vertex_debug_info, graph, past_vertices.back()).gt_T_wc;
00160 SE3 T_pvd_vd = T_w_vd * T_w_pvd.inverse();
00161 get(vertex_debug_info, graph, vd).double_propmap["pose_tr_diff_gt"] =
00162 T_pvd_vd.translation().norm();
00163
00164 SE3 T_w_vd2=get(vertex_transform, graph, past_vertices.front());
00165 SE3 T_w_pvd2=get(vertex_transform, graph, past_vertices.back());
00166 SE3 T_pvd_vd2 = T_w_vd2 * T_w_pvd2.inverse();
00167 get(vertex_debug_info, graph, vd).double_propmap["pose_tr_diff"] =
00168 T_pvd_vd2.translation().norm();
00169
00170 ros::WallDuration all_duration = ros::WallTime::now() - all_start_time;
00171 get(vertex_debug_info, this->graph,
00172 *vdp).double_propmap["timings_all_d"]= all_duration.toSec();
00173 logger->doLogging(graph);
00174 return shared_ptr<SE3>(new SE3(get(vertex_transform, this->graph, *vdp)));
00175 } else{
00176 return shared_ptr<SE3>();
00177 }
00178 }
00179
00184 shared_ptr<SE3> processPose(
00185 typename NodeBuilderTT<PoseGraphT>::InputType input,
00186 int pose_id,
00187 uint64_t pose_timestamp,
00188 bool debug_images=false,
00189 bool trajectory_file=false){
00190 std::deque<SE3> past_poses;
00191 return processPose(
00192 input, pose_id, pose_timestamp, past_poses, debug_images, trajectory_file);
00193 }
00194
00195 virtual shared_ptr<VertexD> processPose0(
00196 typename NodeBuilderTT<PoseGraphT>::InputType input,
00197 int pose_id,
00198 uint64_t pose_timestamp,
00199 bool debug_images=false,
00200 bool trajectory_file=false) = 0;
00201
00202
00205 PoseGraphT& getPoseGraph(){
00206 return graph;
00207 }
00208
00210
00211 virtual pair<pair<VertexD, VertexD>, bool> getLastEdge(){
00212 return make_pair(make_pair(pvd, vd), true);
00213 }
00214
00215 protected:
00216 static const int MINSEQ = 1000000;
00217
00218 template<class T> std::string viewIDString(T view, T from, T to){
00219 std::ostringstream s;
00220 s << MINSEQ + view << "_view_" <<
00221 MINSEQ + from << "to" <<
00222 MINSEQ + to << ".jpg";
00223 return s.str();
00224 }
00225
00227 void pose2dVisualization(
00228 std::string prefix,
00229 typename graph_traits<PoseGraphT>::vertex_descriptor vd1,
00230 typename graph_traits<PoseGraphT>::vertex_descriptor vd2,
00231 typename NodeBuilderTT<PoseGraphT>::InputType input_vd1,
00232 typename NodeBuilderTT<PoseGraphT>::InputType input_vd2){
00233
00234 SE3 T_vd1vd2 = getEdgeTransform(graph, vd1, vd2);
00235 ImageMatchVec matches = computeMatches(vd1, vd2, graph);
00236
00237 cv::Mat image_vd1 = nbuilder->getPoseImage(input_vd1);
00238 cv::Mat image_vd2 = nbuilder->getPoseImage(input_vd2);
00239 cv::Mat image_vd1_wkpts = image_vd1.clone();
00240 cv::Mat image_vd2_wkpts = image_vd2.clone();
00241 drawMatchedNodeKeypoints(image_vd2, vd2, vd1, graph, image_vd2_wkpts);
00242 drawMatchedNodeKeypoints(image_vd1, vd1, vd2, graph, image_vd1_wkpts);
00243
00244
00245 using boost::filesystem::path;
00246 using boost::lexical_cast;
00247 {
00248 cv::Mat kpts_image;
00249 drawNodeKeypoints(vd2, graph, image_vd2, kpts_image);
00250 path p(debug_output_path.native());
00251 p /= prefix;
00252 p /= "kpts";
00253 create_directories(p);
00254 p /= lexical_cast<std::string>(MINSEQ + get(vertex_seq, graph, vd2)) + ".jpg";
00255 cv::imwrite(p.native(), kpts_image);
00256 }
00257
00258 cv::Mat image_vd1_epilines = image_vd1_wkpts.clone();
00259 cv::Mat image_vd2_epilines = image_vd2_wkpts.clone();
00260 drawRaysOnEpipolarPlane(sE3ToE(T_vd1vd2), matches, image_vd1_epilines,
00261 image_vd2_epilines);
00262 drawLandmarkReprojections(image_vd2_epilines, vd2, graph, points);
00263 drawLandmarkReprojections(image_vd1_epilines, vd1, graph, points);
00264 {
00265 path p(debug_output_path.native());
00266 p /= prefix;
00267 p /= "epilines";
00268 create_directories(p);
00269 p /= viewIDString( get(vertex_seq, graph, vd2), get(vertex_seq, graph, vd1),
00270 get(vertex_seq, graph, vd2));
00271 cv::imwrite(p.native(), image_vd2_epilines);
00272
00273 p.remove_filename();
00274 p /= viewIDString( get(vertex_seq, graph, vd1), get(vertex_seq, graph, vd2),
00275 get(vertex_seq, graph, vd1));
00276 cv::imwrite(p.native(), image_vd1_epilines);
00277 }
00278 {
00279 path p(debug_output_path.native());
00280 p /= prefix;
00281 p /= "matches_only";
00282 create_directories(p);
00283 cv::Mat image_vd1_matches = image_vd1.clone();
00284 cv::Mat image_vd2_matches = image_vd2.clone();
00285 drawMatches3(image_vd1_matches, vd1, image_vd2_matches, vd2, graph, points);
00286 p /= viewIDString( get(vertex_seq, graph, vd1), get(vertex_seq, graph, vd2),
00287 get(vertex_seq, graph, vd1));
00288 cv::imwrite(p.native(), image_vd1_matches);
00289 p.remove_filename();
00290 p /= viewIDString( get(vertex_seq, graph, vd2), get(vertex_seq, graph, vd1),
00291 get(vertex_seq, graph, vd2));
00292 cv::imwrite(p.native(), image_vd2_matches);
00293 }
00294 drawMatches3(image_vd1_wkpts, vd1, image_vd2_wkpts, vd2, graph, points);
00295 {
00296 path p(debug_output_path.native());
00297 p /= prefix;
00298 p /= "matches_plus_kpts";
00299 create_directories(p);
00300 p /= viewIDString( get(vertex_seq, graph, vd1), get(vertex_seq, graph, vd2),
00301 get(vertex_seq, graph, vd1));
00302 cv::imwrite(p.native(), image_vd1);
00303
00304 p.remove_filename();
00305 p /= viewIDString( get(vertex_seq, graph, vd2), get(vertex_seq, graph, vd1),
00306 get(vertex_seq, graph, vd2));
00307 cv::imwrite(p.native(), image_vd2);
00308 }
00309
00310
00311 }
00312 };
00313
00314
00315
00316 #define VERTEX_WINDOW_LEN 4
00317
00333 template<template<class> class NodeBuilderTT, class PoseGraphT>
00334 class KeyframeVisualOdometry : public VisualOdometry<NodeBuilderTT, PoseGraphT> {
00335 protected:
00336 typedef typename graph_traits<PoseGraphT>::vertex_descriptor VertexD;
00337
00338 bool initial_pose_registred;
00339 bool has_pvd;
00340 VertexD current_keyframe;
00341 vector<VertexD> window_vertices;
00342 int window_idx;
00343 int total_vertex_count;
00344
00345 typename NodeBuilderTT<PoseGraphT>::InputType vd_image, pvd_image, keyframe_image;
00346
00347 G2ORunner2<PoseGraphT> opt;
00348 public:
00349 KeyframeVisualOdometry(
00350 shared_ptr<NodeBuilderTT<PoseGraphT> > _nbuilder,
00351 shared_ptr<EdgeBuilder<PoseGraphT> > _ebuilder,
00352 size_t _target_past_verticies_size = 1,
00353 shared_ptr<VodomLogger<PoseGraphT> > _logger= shared_ptr<VodomLogger<PoseGraphT> >()) :
00354 VisualOdometry<NodeBuilderTT, PoseGraphT>(
00355 _nbuilder, _ebuilder, _target_past_verticies_size, _logger),
00356 initial_pose_registred(false), has_pvd(false), window_vertices(VERTEX_WINDOW_LEN),
00357 window_idx(-1), total_vertex_count(0) { }
00358
00360 virtual bool buildNewKeyframe() = 0;
00361
00363 virtual void newKeyframeBuilt() = 0;
00364
00365 virtual shared_ptr<VertexD> processPose0(
00366 typename NodeBuilderTT<PoseGraphT>::InputType input,
00367 int pose_id,
00368 uint64_t pose_timestamp,
00369 bool debug_images=false,
00370 bool trajectory_file=false){
00371 VertexD new_pose;
00372 this->nbuilder->build(input, pose_id, pose_timestamp, new_pose, this->graph);
00373 get(vertex_keyframe_flag, this->graph, new_pose) = false;
00374
00375 if(this->initial_pose_registred){
00376 if(!this->ebuilder->build(current_keyframe, new_pose, this->points, this->graph)){
00378 this->nbuilder->demolish(this->graph, this->points, new_pose);
00379 } else{
00380 total_vertex_count++;
00381 window_idx=(window_idx+1) % VERTEX_WINDOW_LEN;
00382 if(total_vertex_count>VERTEX_WINDOW_LEN){
00383 if(!get(vertex_keyframe_flag, this->graph, window_vertices[window_idx])){
00384 this->nbuilder->demolish(
00385 this->graph, this->points, window_vertices[window_idx]);
00386 }
00387 }
00388 window_vertices[window_idx]=new_pose;
00389 this->points.activateLandmarks(this->graph, new_pose, 50);
00390 this->pvd=this->vd;
00391 this->vd=new_pose;
00392 pvd_image=vd_image;
00393 vd_image = input;
00394
00395 vector<vector<int> > all_result, kf_result;
00396 landmarkObservationsBySize(
00397 this->graph, this->points, this->vd, all_result, kf_result);
00398 get(vertex_debug_info, this->graph, this->vd).double_propmap["lmlen_eq1"]=
00399 kf_result[1].size();
00400 get(vertex_debug_info, this->graph, this->vd).double_propmap["lmlen_ge3"]=
00401 all_result[3].size();
00402 for(int i=1; i< (int)all_result.size(); i++){
00403 ROS_INFO_STREAM_NAMED("PoseGraph", "# of tracks of length " <<
00404 i << "+, kf-only:" << kf_result[i].size());
00405 ROS_INFO_STREAM_NAMED("PoseGraph", "# of tracks of length " << i
00406 << "+, all:" << all_result[i].size());
00407 }
00408 if(total_vertex_count >= 5){
00409 G2OConstActiveDistanceBuilder<PoseGraphT> builder(1000000, 8.0, 3);
00410 opt.doOptimalization(this->graph, this->points, builder, this->vd);
00411 }
00412 get(vertex_debug_info, this->graph, this->vd).double_propmap["max_lm_im_distance"]=
00413 maxFeatureDistance(this->graph, this->points, this->vd);
00414 this->pose2dVisualization(
00415 "all", current_keyframe, this->vd, pvd_image, vd_image);
00416 if(this->buildNewKeyframe()){
00417 this->pose2dVisualization(
00418 "kf", current_keyframe, this->vd, keyframe_image, vd_image);
00419 this->points.activateLandmarks(this->graph, current_keyframe, 100);
00420 current_keyframe=this->vd;
00421 keyframe_image=vd_image;
00422 get(vertex_keyframe_flag, this->graph, current_keyframe) = true;
00423 this->newKeyframeBuilt();
00424 }
00425 }
00426 } else{
00427 total_vertex_count++;
00428 window_idx=(window_idx+1) % VERTEX_WINDOW_LEN;
00429 this->vd=new_pose;
00430 vd_image = input;
00431 window_idx=0;
00432 window_vertices[0]=new_pose;
00433
00434 this->initial_pose_registred=true;
00435 current_keyframe=this->vd;
00436 keyframe_image=vd_image;
00437 get(vertex_keyframe_flag, this->graph, current_keyframe) = true;
00438 }
00439 if(trajectory_file) drawGraph(this->graph);
00440 return shared_ptr<VertexD>(new VertexD(this->vd));
00441 }
00442 };
00443
00444
00446 template<template<class> class NodeBuilderTT, class PoseGraphT>
00447 class SimpleVisualOdometry : public KeyframeVisualOdometry<NodeBuilderTT, PoseGraphT> {
00448
00449 public:
00450 SimpleVisualOdometry(
00451 shared_ptr<NodeBuilderTT<PoseGraphT> > _nbuilder,
00452 shared_ptr<EdgeBuilder<PoseGraphT> > _ebuilder,
00453 size_t _target_past_verticies_size,
00454 shared_ptr<VodomLogger<PoseGraphT> > _logger= shared_ptr<VodomLogger<PoseGraphT> >()) :
00455 KeyframeVisualOdometry<NodeBuilderTT, PoseGraphT>(
00456 _nbuilder, _ebuilder, _target_past_verticies_size, _logger) {}
00457
00458 virtual bool buildNewKeyframe() {return true;}
00459 virtual void newKeyframeBuilt() {};
00460 };
00461
00462
00463
00465 template<template<class> class NodeBuilderTT, class PoseGraphT>
00466 class CovisibilityKeyframeVisualOdometry : public KeyframeVisualOdometry<NodeBuilderTT, PoseGraphT> {
00467 protected:
00468 typedef typename graph_traits<PoseGraphT>::vertex_descriptor VertexD;
00469
00470 double minimum;
00471 double ctreshold;
00472
00473 public:
00474
00480 CovisibilityKeyframeVisualOdometry(
00481 shared_ptr<NodeBuilderTT<PoseGraphT> > _nbuilder,
00482 shared_ptr<EdgeBuilder<PoseGraphT> > _ebuilder,
00483 double _ctreshold = 0.8,
00484 size_t _target_past_verticies_size = 1,
00485 shared_ptr<VodomLogger<PoseGraphT> > _logger= shared_ptr<VodomLogger<PoseGraphT> >()) :
00486 KeyframeVisualOdometry<NodeBuilderTT, PoseGraphT>(_nbuilder, _ebuilder,
00487 _target_past_verticies_size, _logger),
00488 ctreshold(_ctreshold){
00489 newKeyframeBuilt();
00490 }
00491
00492 int getEdgeCovisibility(VertexD s, VertexD t){
00493 return get(edge_kp_idx_map, this->graph, boost::edge(s, t, this->graph).first).size();
00494 }
00495
00496
00497 virtual bool buildNewKeyframe(){
00498 int covisibility = getEdgeCovisibility(this->current_keyframe, this->vd);
00499 ROS_INFO_STREAM_NAMED("VisualOdometry", "Covisibility: " << covisibility);
00500 ROS_INFO_STREAM_NAMED("VisualOdometry", "Cov. Treshold: " << ctreshold*minimum);
00501 bool result;
00502 if(minimum<0 || covisibility > minimum){
00503 minimum=covisibility;
00504 result=false;
00505 } else{
00506 if(covisibility < ctreshold*minimum) result = true;
00507 else result=false;
00508 }
00509 return result;
00510 }
00511
00512 virtual void newKeyframeBuilt(){
00513 minimum=-1;
00514 }
00515
00516 };
00517
00518
00524 template<template<class> class NodeBuilderTT, class PoseGraphT>
00525 class ScaleErrorKeyframeVisualOdometry : public KeyframeVisualOdometry<NodeBuilderTT, PoseGraphT> {
00526 protected:
00527 typedef typename graph_traits<PoseGraphT>::vertex_descriptor VertexD;
00528
00529 bool initial_pose_registred;
00530 VertexD current_keyframe;
00531 int k;
00532 double error;
00533 double prev_error;
00534 int count;
00535 int asc_count;
00536 public:
00537 ScaleErrorKeyframeVisualOdometry(
00538 shared_ptr<NodeBuilderTT<PoseGraphT> > _nbuilder,
00539 shared_ptr<EdgeBuilder<PoseGraphT> > _ebuilder,
00540 size_t _target_past_verticies_size,
00541 shared_ptr<VodomLogger<PoseGraphT> > _logger= shared_ptr<VodomLogger<PoseGraphT> >()) :
00542 VisualOdometry<NodeBuilderTT, PoseGraphT>(
00543 _nbuilder, _ebuilder, _target_past_verticies_size, _logger),
00544 initial_pose_registred(false), k(2) {
00545 newKeyframeBuilt();
00546 }
00547
00548 virtual bool buildNewKeyframe(){
00549 prev_error=error;
00550 error = get(edge_rel_scale_error, this->graph,
00551 edge(current_keyframe, this->vd, this->graph).first);
00552 ROS_INFO_STREAM_NAMED("VisualOdometry", "relative error: " << error);
00553
00554 if(count){
00555 if(prev_error < error) asc_count++;
00556 else asc_count=0;
00557 }
00558 count++;
00559 if(asc_count>=k) return true;
00560 else return false;
00561 }
00562
00563 virtual void newKeyframeBuilt(){
00564 count=0;
00565 asc_count=0;
00566 }
00567
00568 };
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00593 }
00594
00595 #endif