vo.h
Go to the documentation of this file.
00001 //part of the source code of master thesis, source code produced by Jiri Divis
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                 //if(adjacent_vertices(*vb, graph).first != adjacent_vertices(*vb, graph).second){
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             //SE3 T_vd1vd2 = get(edge_transform, graph, edge(vd1, vd2, graph).first);
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){// do we have at least one pose in the pose-graph?
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);//XXX
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);//XXX
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         //* @see ctreshold*/
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 template<class VOT> class VodomScaleFixer : public VOT{
00573     public:
00574     virtual shared_ptr<typename VOT::VertexD> processPose0(
00575         typename VOT::template NodeBuilderTT<VOT::PoseGraphT>::InputType input,
00576         int pose_id, 
00577         uint64_t pose_timestamp, 
00578         bool debug_images=false, 
00579         bool trajectory_file=false){
00580             VOT::processPose0(input, pose_id, pose_timestamp, debug_images, trajectory_file);
00581             doLogging(this->graph);
00582         }
00583 };
00584 */
00585 
00586 
00587 
00588 
00589 
00590 
00593 }//namespace vslam
00594 
00595 #endif
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines


pose_estimation
Author(s): Jiri Divis/jiridivis@gmail.com
autogenerated on Wed Mar 27 2013 21:00:12