00001
00002
00009 #ifndef VSLAM_LANDMARK_MANAGER_H_INCLUDED
00010 #define VSLAM_LANDMARK_MANAGER_H_INCLUDED
00011 #include "utils.h"
00012 #include "pose_graph.h"
00013 #include "graph_utils.h"
00014 #include "error_func.h"
00015
00016 #include <algorithm>
00017 #include <tr1/functional>
00018
00019
00020 namespace vslam{
00021
00043 template<typename G> struct Point{
00044 typedef typename graph_traits<G>::vertex_descriptor VertexD;
00045 typedef pair<VertexD, int> Feature;
00046 typedef std::vector<Feature> FeatureCont;
00047
00048 FeatureCont seen_from;
00049 Vector3d global;
00050 bool initialized;
00051 bool usable;
00052 };
00053
00054
00055
00056
00057
00063 namespace lmdetails{
00064
00074
00075 template<typename G> class PointsLoc{
00076 protected:
00077 vector<Point<G> > pts;
00078 typedef typename graph_traits<G>::vertex_descriptor VertexD;
00079
00080 public:
00081 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
00082 void destroy(int idx, G&g){
00083 getPoint(idx).initialized=false;
00084 getPoint(idx).global=Vector3d::Zero();
00085 typedef typename Point<G>::Feature T;
00086 BOOST_FOREACH(T pf, getPoint(idx).seen_from){
00087 vector<int> & fixed_points = get(vertex_fix_idx, g, pf.first);
00088 fixed_points[pf.second]=BAD_INDEX;
00089 }
00090 getPoint(idx).seen_from.clear();
00091 }
00092
00093 int create(const G& g, const vector<FeatureID<G> >& observations,
00094 const Vector3d& estimate);
00095
00096 void addObservation(const G&g, int idx, FeatureID<G>& f, const Vector3d& estimate);
00097
00098 Point<G> & getPoint(int idx){
00099 return pts[idx];
00100 }
00101
00102 const Point<G> & getPoint(int idx) const{
00103 return pts[idx];
00104 }
00105
00106 int size() const { return pts.size(); }
00107 };
00108
00109
00110
00116 template<class G> int PointsLoc<G>::create(const G& g, const vector<FeatureID<G> >& observations,
00117 const Vector3d& estimate){
00118 Point<G> pt;
00119 vector<LandmarkObservation> lm_obs;
00120 BOOST_FOREACH(FeatureID<G> f, observations){
00121 pt.seen_from.push_back(make_pair(f.vd, f.vidx));
00122 lm_obs.push_back(LandmarkObservation(get(vertex_transform, g, f.vd).inverse(),
00123 getFeaturePixel(g, f)));
00124 }
00125 pt.initialized=false;
00126 pt.usable=true;
00127 BOOST_FOREACH(LandmarkObservation tmp, lm_obs){
00128 if(!isEstimateInFrontOfCamera(tmp, estimate)) pt.usable=false;
00129 }
00130 FeatureID<G> ff = FeatureID<G>(pt.seen_from.begin()->first, pt.seen_from.begin()->second);
00131 FeatureID<G> fl = FeatureID<G>(pt.seen_from.rbegin()->first, pt.seen_from.rbegin()->second);
00132 if(disparityAngle(g, ff, fl) < (M_PI/180.0)*3.0){
00133 pt.usable=false;
00134 }
00135 pt.global=estimate;
00136 pts.push_back(pt);
00137 return pts.size() - 1;
00138 }
00139
00146 template<class G> void PointsLoc<G>::addObservation(
00147 const G&g, int idx, FeatureID<G>& f, const Vector3d& estimate){
00148 typename Point<G>::FeatureCont & seen_from_idx = pts[idx].seen_from;
00149 seen_from_idx.push_back(make_pair(f.vd, f.vidx));
00150
00151 pts[idx].global=estimate;
00152 if(seen_from_idx.size()>=4){
00153 pts[idx].usable=true;
00154 } else{
00155 Point<G> pt = pts[idx];
00156 FeatureID<G> ff = FeatureID<G>(pt.seen_from.begin()->first, pt.seen_from.begin()->second);
00157 FeatureID<G> fl = FeatureID<G>(pt.seen_from.rbegin()->first, pt.seen_from.rbegin()->second);
00158 if(disparityAngle(g, ff, fl) > (M_PI/180.0)*3.0){
00159 pt.usable=true;
00160 }
00161 }
00162 }
00163
00164
00165
00166
00167
00168
00169
00173 template<class G> class LandmarkQualityOrdering{
00174 protected:
00175 const G & g;
00176 const PointsLoc<G> & points;
00177
00178 public:
00179 LandmarkQualityOrdering(const G& _g, const PointsLoc<G>& _points) : g(_g), points(_points) {}
00180
00181 bool operator()(int lm_idx_a, int lm_idx_b){
00182 return lm_idx_a > lm_idx_b;
00183 }
00184 };
00185
00186
00188 template<class G> double ftrTrackEndToEndDisparity(const G&g, const Point<G>& pt){
00189 FeatureID<G> ff = FeatureID<G>(pt.seen_from.begin()->first, pt.seen_from.begin()->second);
00190 FeatureID<G> fl = FeatureID<G>(pt.seen_from.rbegin()->first, pt.seen_from.rbegin()->second);
00191 return disparityAngle(g, ff, fl);
00192 }
00193
00195 template<class G> class DisparityLmOrdering : public LandmarkQualityOrdering<G>{
00196 public:
00197 DisparityLmOrdering(const G& _g, const PointsLoc<G>& _points) :
00198 LandmarkQualityOrdering<G>(_g, _points) {}
00199
00201 bool operator()(int lm_idx_a, int lm_idx_b){
00202 if(this->points.getPoint(lm_idx_a).usable){
00203 if(this->points.getPoint(lm_idx_b).usable){
00204 double disparity_a = ftrTrackEndToEndDisparity(
00205 this->g, this->points.getPoint(lm_idx_a));
00206 double disparity_b = ftrTrackEndToEndDisparity(
00207 this->g, this->points.getPoint(lm_idx_b));
00208 return disparity_a > disparity_b;
00209 } else{
00210 return true;
00211 }
00212 } else{
00213 if(this->points.getPoint(lm_idx_b).usable){
00214 return false;
00215 } else{
00216 return lm_idx_a > lm_idx_b;
00217 }
00218 }
00219 }
00220 };
00221
00222
00223 template<class G> void pruneLandmarks(
00224 const G&g, PointsLoc<G>& points, typename graph_traits<G>::vertex_descriptor v, int keep_num){
00225 vector<int> visible_lm_idxs;
00226 BOOST_FOREACH(int fix_idx, get(vertex_fix_idx, g, v)){
00227 if(fix_idx!=BAD_INDEX){
00228 visible_lm_idxs.push_back(fix_idx);
00229 }
00230 }
00231
00232 std::sort(visible_lm_idxs.begin(), visible_lm_idxs.end(), DisparityLmOrdering<G>(g, points));
00233 vector<int>::const_iterator idx_it = visible_lm_idxs.begin();
00234 for(int j=0; (j< keep_num) && idx_it!=visible_lm_idxs.end(); j++){
00235 if(points.getPoint(*idx_it).usable){
00236 points.getPoint(*idx_it).initialized = true;
00237 } else{
00238 keep_num++;
00239 }
00240 ++idx_it;
00241 }
00242 }
00243
00245
00246
00251
00252 #define VSLAM_REPROJ_ERROR_TOLERANCE 5.0 //XXX
00253
00254
00255
00256 typedef ALIGNED<LandmarkObservation>::vector LandmarkObservationVec;
00257
00258 typedef Model<Vector3d, LandmarkObservation> PointFromLandmarkObservationsModel;
00259
00276 inline bool landmarkObservationSetConsistency(
00277 const LandmarkObservationVec& observations1,
00278 const LandmarkObservationVec& observations2,
00279 const LandmarkObservationVec& all_observations,
00280 Vector3d& global_est){
00281 typedef LandmarkToPoseReprojectionError T0;
00282 typedef MDErrorFuncAdaptor<T0> T1;
00283 typedef RealErrorMaxErrorAccumulator<T1> T2;
00284 shared_ptr<ModelBuilder<PointFromLandmarkObservationsModel> > mbuilder;
00285 T0 error_func;
00286 T1 adapted_error_func = T1(error_func);
00287 T2 error_acum = T2(adapted_error_func, VSLAM_REPROJ_ERROR_TOLERANCE);
00288 mbuilder.reset(new Point3dModelFromLandmarkObservationPairBuilder<T2>(error_acum));
00289 shared_ptr<SingleSolutionModel<T2> > initial_test_model;
00290 ALIGNED<LandmarkObservation>::vector tmp_vec;
00291 initial_test_model.reset(new SingleSolutionModel<T2>(0, tmp_vec,global_est,error_acum));
00292 initial_test_model->compute(all_observations);
00293 if(initial_test_model->getInlierCount()==(int)all_observations.size()){
00294 return true;
00295 }
00296 shared_ptr<PointFromLandmarkObservationsModel> model, best_model;
00297 LandmarkObservationVec selected_data;
00298 BOOST_FOREACH(LandmarkObservation obs1, observations1){
00299 selected_data.push_back(obs1);
00300 BOOST_FOREACH(LandmarkObservation obs2, observations2){
00301 if(!(obs1 == obs2)){
00302 selected_data.push_back(obs2);
00303 model=mbuilder->operator()(selected_data);
00304 model->compute(all_observations);
00305 if(model->getInlierCount()==(int)all_observations.size()){
00306 if(!best_model || model->getError() < best_model->getError()) best_model=model;
00307 }
00308 selected_data.pop_back();
00309 }
00310 }
00311 selected_data.pop_back();
00312 }
00313 if(best_model){
00314 global_est = best_model->getModel();
00315 return true;
00316 } else{
00317 return false;
00318 }
00319 }
00320
00321
00322
00328 template<class G> bool consistencyCheck(
00329 const G& g, FeatureID<G> f1, FeatureID<G> f2, FeatureID<G> f3, Vector3d& estimate){
00330 LandmarkObservationVec obs;
00331 obs.push_back(LandmarkObservation(get(vertex_transform, g, f1.vd).inverse(),
00332 getFeaturePixel(g, f1)));
00333 obs.push_back(LandmarkObservation(get(vertex_transform, g, f2.vd).inverse(),
00334 getFeaturePixel(g, f2)));
00335 obs.push_back(LandmarkObservation(get(vertex_transform, g, f3.vd).inverse(),
00336 getFeaturePixel(g, f3)));
00337 return landmarkObservationSetConsistency(obs, obs, obs, estimate);
00338 }
00339
00340
00352 template<class G> bool consistencyCheck(
00353 const G& g,
00354 const Points<G>& points,
00355 FeatureID<G> lmfid,
00356 FeatureID<G> nonlmfid,
00357 Vector3d& estimate){
00358 typename property_map<G, vertex_fix_idx_t>::const_type vfixmap = get(vertex_fix_idx, g);
00359
00360 if((vfixmap[lmfid.vd][lmfid.vidx]==BAD_INDEX) &&
00361 (vfixmap[nonlmfid.vd][nonlmfid.vidx]!=BAD_INDEX)){
00362 assert(false);
00363 return false;
00364 } else{
00365 typedef pair<typename graph_traits<G>::vertex_descriptor, int> T1;
00366 Point<G> point=points.getPoint(vfixmap[lmfid.vd][lmfid.vidx]);
00367 LandmarkObservationVec obs;
00368 BOOST_FOREACH(T1 pt, point.seen_from){
00369 obs.push_back(LandmarkObservation(
00370 get(vertex_transform, g, pt.first).inverse(),
00371 getFeaturePixel(g, FeatureID<G>(pt.first, pt.second))));
00372 }
00373 obs.push_back(LandmarkObservation(
00374 get(vertex_transform, g, nonlmfid.vd).inverse(),
00375 getFeaturePixel(g, nonlmfid)));
00376 if(landmarkObservationSetConsistency(obs, obs, obs, estimate)){
00377 assert(vfixmap[nonlmfid.vd][nonlmfid.vidx]==BAD_INDEX);
00378 return true;
00379 } else{
00380 return false;
00381 }
00382 }
00383 }
00385
00387
00388 }
00389
00390
00401 template<class G> void Points<G>::activateLandmarks(
00402 const G&g, typename graph_traits<G>::vertex_descriptor v, int keep_num){
00403 lmdetails::pruneLandmarks(g, points, v, keep_num);
00404 }
00405
00410 template<class G> const Point<G> & Points<G>::getPoint(int idx) const{
00411 return points.getPoint(idx);
00412 }
00413
00414
00416 template<class G> void Points<G>::lmEstimateUpd(int lm_id, Vector3d& new_estimate){
00417 points.getPoint(lm_id).global=new_estimate;
00418 }
00419
00420
00426 template<class G> void Points<G>::lmEstimateUpdChecked(int lm_id, Vector3d& new_estimate){
00427 this->lmEstimateUpd(lm_id, new_estimate);
00429 }
00430
00431
00437 template<class G> void Points<G>::deleteLmObs(G&g, const FeatureID<G>& obs){
00438 Point<G> pt = points.getPoint(get(vertex_fix_idx, g, obs.vd)[obs.vidx]);
00439
00440 assert(pt.seen_from.size());
00441 vector<int> & fixed_points = get(vertex_fix_idx, g, obs.vd);
00442
00443 int to_delete_idx=BAD_INDEX; int to_delete_kp_idx=BAD_INDEX;
00444 for(size_t i=0; i<pt.seen_from.size(); i++){
00445 if(pt.seen_from[i].first==obs.vd){
00446 to_delete_idx=i;
00447 to_delete_kp_idx=pt.seen_from[i].second;
00448 }
00449 }
00450 assert(to_delete_idx!=BAD_INDEX && to_delete_kp_idx!=BAD_INDEX);
00451
00452 if((int)(pt.seen_from.size()-1)!=to_delete_idx){
00453 pt.seen_from[to_delete_idx]=pt.seen_from[pt.seen_from.size()-1];
00454 }
00455 fixed_points[to_delete_kp_idx]=BAD_INDEX;
00456 pt.seen_from.pop_back();
00457
00458 if(pt.seen_from.size()==1){
00459 vector<int> & fixed_points2 = get(vertex_fix_idx, g, pt.seen_from[0].first);
00460 fixed_points2[pt.seen_from[0].second]=BAD_INDEX;
00461 pt.seen_from.clear();
00462 pt.initialized=false;
00463 pt.usable=false;
00464 }
00465 }
00466
00467
00469 template<class G> int Points<G>::size() const{
00470 return points.size();
00471 }
00472
00473
00474
00483 template<typename G> bool Points<G>::fixNewLandmark(
00484 FeatureID<G> fa,
00485 FeatureID<G> fm,
00486 FeatureID<G> fb,
00487 G& g){
00488 using namespace boost;
00489 using namespace lmdetails;
00490 typename property_map<G, vertex_fix_idx_t>::type vm = get(vertex_fix_idx, g);
00491 bool c1,c2,c3;
00492 c1=vm[fa.vd][fa.vidx] == BAD_INDEX;
00493 c2=vm[fm.vd][fm.vidx] == BAD_INDEX;
00494 c3=vm[fb.vd][fb.vidx] == BAD_INDEX;
00495 if(!(c1 && c2 && c3)) return false;
00496 Vector3d estimate;
00497 if(consistencyCheck(g, fa, fm, fb, estimate)){
00498 vector<FeatureID<G> > fs;
00499 fs.push_back(fa); fs.push_back(fm); fs.push_back(fb);
00500 int tmp = points.create(g, fs, estimate);
00501 vm[fm.vd][fm.vidx] = tmp;
00502 vm[fa.vd][fa.vidx] = tmp;
00503 vm[fb.vd][fb.vidx] = tmp;
00504 return true;
00505 } else{
00506 return false;
00507 }
00508 }
00509
00518 template<typename G> bool Points<G>::fixNewObservation(
00519 FeatureID<G> fa, FeatureID<G> fb, G& g){
00520 using namespace boost;
00521 using namespace lmdetails;
00522 typename property_map<G, vertex_fix_idx_t>::type vm = get(vertex_fix_idx, g);
00523
00524 if(vm[fa.vd][fa.vidx] == BAD_INDEX){
00525 if(vm[fb.vd][fb.vidx] == BAD_INDEX){
00526 assert(false);
00527 } else{
00528 return this->fixNewObservation(fb, fa, g);
00529 }
00530 } else{
00531 Vector3d estimate;
00532 if(points.getPoint(vm[fa.vd][fa.vidx]).initialized){
00533 estimate= points.getPoint(
00534 vm[fa.vd][fa.vidx]).global;
00535 } else{
00536 estimate = Vector3d(1.0, 1.0, 1.0);
00537 }
00538 if(consistencyCheck<G>(g, *this, fa, fb, estimate)){
00539 points.addObservation(g, vm[fa.vd][fa.vidx], fb, estimate);
00540 vm[fb.vd][fb.vidx] = vm[fa.vd][fa.vidx];
00541 return true;
00542 } else{
00543 return false;
00544 }
00545 }
00546 assert(false);
00547 return false;
00548 }
00549
00550
00566 template<class G> void findNewLandmarks(
00567 typename graph_traits<G>::vertex_descriptor convd,
00568 typename graph_traits<G>::vertex_descriptor newvd,
00569 Points<G> & points,
00570 G & g){
00571 using namespace boost;
00572 typename graph_traits<G>::adjacency_iterator vi, vi_end;
00573 int new_track_count=0;
00574
00575 for(tie(vi, vi_end) = adjacent_vertices(convd , g); vi!=vi_end; ++vi){
00576 if(*vi != newvd){
00577 vector<int> common_ftr_convd = common_feature_tracks<vector>(g, *vi, convd, newvd);
00578 vector<int>::const_iterator common_ftr_convd_it = common_ftr_convd.begin();
00579 while(common_ftr_convd_it!=common_ftr_convd.end()){
00580 FeatureID<G> convdf = FeatureID<G>(convd, *common_ftr_convd_it);
00581 FeatureID<G> newvdf = FeatureID<G>(newvd, getFeatureIdx(g, convdf, newvd));
00582 FeatureID<G> vif = FeatureID<G>(*vi, getFeatureIdx(g, convdf, *vi));
00583
00584
00585 if(!isLandmakFeature(g, vif) && !isLandmakFeature(g, convdf) &&
00586 !isLandmakFeature(g, newvdf)){
00587 if(points.fixNewLandmark(vif, convdf, newvdf, g)){
00588 new_track_count++;
00589 }
00590 }
00591 ++common_ftr_convd_it;
00592 }
00593 }
00594 }
00595 std::string num_new_lms_key="num_new_lms";
00596 map<std::string, double> & pmap = get(vertex_debug_info, g, convd).double_propmap;
00597 if(pmap.find(num_new_lms_key) == pmap.end()){
00598 pmap[num_new_lms_key]= new_track_count;
00599 } else{
00600 get(vertex_debug_info, g, newvd).double_propmap[num_new_lms_key]=new_track_count;
00601 }
00602 ROS_INFO_STREAM_NAMED("EdgeBulder", "# of new tracks: " << new_track_count);
00603 get(vertex_debug_info, g, convd).double_propmap["tracks_new"]=new_track_count;
00604 }
00605
00616 template<class G> void extendLandmarks(
00617 typename graph_traits<G>::vertex_descriptor convd,
00618 typename graph_traits<G>::vertex_descriptor newvd,
00619 Points<G> & points,
00620 G & g){
00621 int old_track_count=0;
00622 int deleted=0;
00623 const map<int, int> & sdmap = get(edge_kp_idx_map, g, edge(convd, newvd, g).first);
00624 for(map<int, int>::const_iterator it = sdmap.begin(); it != sdmap.end(); ++it){
00625 FeatureID<G> convdf = FeatureID<G>(convd, it->first);
00626 FeatureID<G> newvdf = FeatureID<G>(newvd, it->second);
00627
00628
00629 if(isLandmakFeature(g, convdf) && !isLandmakFeature(g, newvdf)){
00630 if(points.fixNewObservation(convdf, newvdf, g)){
00631 old_track_count++;
00632 } else{
00633 deleted++;
00634 }
00635 }
00636 }
00637 ROS_INFO_STREAM_NAMED("EdgeBulder", "# of tracks that ceased: " << deleted);
00638 ROS_INFO_STREAM_NAMED("EdgeBulder", "# of tracks extended: " << old_track_count);
00639 if(deleted && old_track_count){
00640 get(vertex_debug_info, g, convd).double_propmap["tracks_ceased"]=deleted;
00641 get(vertex_debug_info, g, convd).double_propmap["tracks_extended"]=old_track_count;
00642 }
00643 }
00646
00647
00648 }
00649
00650 #endif