00001
00019 #ifndef __D_T_TEMPLATED_LOOP_DETECTOR__
00020 #define __D_T_TEMPLATED_LOOP_DETECTOR__
00021
00022 #include <vector>
00023 #include <numeric>
00024 #include <fstream>
00025 #include <string>
00026
00027 #include <opencv/cv.h>
00028
00029 #include "TemplatedVocabulary.h"
00030 #include "TemplatedDatabase.h"
00031 #include "QueryResults.h"
00032 #include "BowVector.h"
00033
00034 #include "DUtils.h"
00035 #include "DUtilsCV.h"
00036 #include "DVision.h"
00037
00038 using namespace std;
00039 using namespace DUtils;
00040 using namespace DBoW2;
00041
00042 namespace DLoopDetector {
00043
00044
00046 enum GeometricalCheck
00047 {
00049 GEOM_EXHAUSTIVE,
00051 GEOM_DI,
00053 GEOM_FLANN,
00055 GEOM_NONE
00056 };
00057
00059 enum DetectionStatus
00060 {
00062 LOOP_DETECTED,
00064 CLOSE_MATCHES_ONLY,
00066 NO_DB_RESULTS,
00068 LOW_NSS_FACTOR,
00070 LOW_SCORES,
00072 NO_GROUPS,
00074 NO_TEMPORAL_CONSISTENCY,
00076 NO_GEOMETRICAL_CONSISTENCY
00077 };
00078
00080 struct DetectionResult
00081 {
00083 DetectionStatus status;
00085 EntryId query;
00087 EntryId match;
00088
00093 inline bool detection() const
00094 {
00095 return status == LOOP_DETECTED;
00096 }
00097 };
00098
00101 template<class TDescriptor, class F>
00103 class TemplatedLoopDetector
00104 {
00105 public:
00106
00108 struct Parameters
00109 {
00111 int image_rows;
00113 int image_cols;
00114
00115
00116
00118 bool use_nss;
00120 float alpha;
00122 int k;
00124 GeometricalCheck geom_check;
00126 int di_levels;
00127
00128
00129
00131 int dislocal;
00133 int max_db_results;
00135 float min_nss_factor;
00137 int min_matches_per_group;
00139 int max_intragroup_gap;
00141 int max_distance_between_groups;
00143 int max_distance_between_queries;
00144
00145
00146
00148 int min_Fpoints;
00150 int max_ransac_iterations;
00152 double ransac_probability;
00154 double max_reprojection_error;
00155
00156
00157
00159 double max_neighbor_ratio;
00160
00164 Parameters();
00165
00178 Parameters(int height, int width, float frequency = 1, bool nss = true,
00179 float _alpha = 0.3, int _k = 3,
00180 GeometricalCheck geom = GEOM_DI, int dilevels = 0);
00181
00182 private:
00187 void set(float frequency);
00188 };
00189
00190 public:
00191
00195 TemplatedLoopDetector(const Parameters ¶ms = Parameters());
00196
00203 TemplatedLoopDetector(const TemplatedVocabulary<TDescriptor, F> &voc,
00204 const Parameters ¶ms = Parameters());
00205
00212 TemplatedLoopDetector(const TemplatedDatabase<TDescriptor, F> &db,
00213 const Parameters ¶ms = Parameters());
00214
00222 template<class T>
00223 TemplatedLoopDetector(const T &db, const Parameters ¶ms = Parameters());
00224
00228 virtual ~TemplatedLoopDetector(void);
00229
00234 inline const TemplatedDatabase<TDescriptor, F>& getDatabase() const;
00235
00240 inline const TemplatedVocabulary<TDescriptor, F>& getVocabulary() const;
00241
00248 template<class T>
00249 void setDatabase(const T &db);
00250
00255 void setVocabulary(const TemplatedVocabulary<TDescriptor, F>& voc);
00256
00262 void allocate(int nentries, int nkeys = 0);
00263
00272 bool detectLoop(const std::vector<cv::KeyPoint> &keys,
00273 const std::vector<TDescriptor> &descriptors,
00274 DetectionResult &match);
00275
00280 inline void clear();
00281
00282 protected:
00283
00285 struct tIsland
00286 {
00288 EntryId first;
00290 EntryId last;
00292 double score;
00293
00295 EntryId best_entry;
00297 double best_score;
00298
00302 tIsland(){}
00303
00309 tIsland(EntryId f, EntryId l): first(f), last(l){}
00310
00317 tIsland(EntryId f, EntryId l, double s): first(f), last(l), score(s){}
00318
00324 inline bool operator < (const tIsland &b) const
00325 {
00326 return this->score < b.score;
00327 }
00328
00334 inline bool operator > (const tIsland &b) const
00335 {
00336 return this->score > b.score;
00337 }
00338
00346 static inline bool gt(const tIsland &a, const tIsland &b)
00347 {
00348 return a.score > b.score;
00349 }
00350
00358 static inline bool ltId(const tIsland &a, const tIsland &b)
00359 {
00360 return a.first < b.first;
00361 }
00362
00367 inline int length() const { return last - first + 1; }
00368
00373 std::string toString() const
00374 {
00375 stringstream ss;
00376 ss << "[" << first << "-" << last << ": " << score << " | best: <"
00377 << best_entry << ": " << best_score << "> ] ";
00378 return ss.str();
00379 }
00380 };
00381
00383 struct tTemporalWindow
00384 {
00386 tIsland last_matched_island;
00388 EntryId last_query_id;
00390 int nentries;
00391
00395 tTemporalWindow(): nentries(0) {}
00396 };
00397
00398
00399 protected:
00400
00407 void removeLowScores(QueryResults &q, double threshold) const;
00408
00414 void computeIslands(QueryResults &q, vector<tIsland> &islands) const;
00415
00424 double calculateIslandScore(const QueryResults &q, unsigned int i_first,
00425 unsigned int i_last) const;
00426
00433 void updateTemporalWindow(const tIsland &matched_island, EntryId entry_id);
00434
00439 inline int getConsistentEntries() const
00440 {
00441 return m_window.nentries;
00442 }
00443
00452 bool isGeometricallyConsistent_DI(EntryId old_entry,
00453 const std::vector<cv::KeyPoint> &keys,
00454 const std::vector<TDescriptor> &descriptors,
00455 const FeatureVector &curvec) const;
00456
00466 bool isGeometricallyConsistent_Flann(EntryId old_entry,
00467 const std::vector<cv::KeyPoint> &keys,
00468 const std::vector<TDescriptor> &descriptors,
00469 cv::FlannBasedMatcher &flann_structure) const;
00470
00476 void getFlannStructure(const std::vector<TDescriptor> &descriptors,
00477 cv::FlannBasedMatcher &flann_structure) const;
00478
00489 bool isGeometricallyConsistent_Exhaustive(
00490 const std::vector<cv::KeyPoint> &old_keys,
00491 const std::vector<TDescriptor> &old_descriptors,
00492 const std::vector<cv::KeyPoint> &cur_keys,
00493 const std::vector<TDescriptor> &cur_descriptors) const;
00494
00505 void getMatches_neighratio(const std::vector<TDescriptor> &A,
00506 const vector<unsigned int> &i_A, const vector<TDescriptor> &B,
00507 const vector<unsigned int> &i_B,
00508 vector<unsigned int> &i_match_A, vector<unsigned int> &i_match_B) const;
00509
00510 protected:
00511
00513
00514 TemplatedDatabase<TDescriptor,F> *m_database;
00515
00517 vector<vector<cv::KeyPoint> > m_image_keys;
00518
00520 vector<vector<TDescriptor> > m_image_descriptors;
00521
00523 BowVector m_last_bowvec;
00524
00526 tTemporalWindow m_window;
00527
00529 Parameters m_params;
00530
00532 DVision::FSolver m_fsolver;
00533
00534 };
00535
00536
00537
00538 template <class TDescriptor, class F>
00539 TemplatedLoopDetector<TDescriptor,F>::Parameters::Parameters():
00540 use_nss(true), alpha(0.3), k(4), geom_check(GEOM_DI), di_levels(0)
00541 {
00542 set(1);
00543 }
00544
00545
00546
00547 template <class TDescriptor, class F>
00548 TemplatedLoopDetector<TDescriptor,F>::Parameters::Parameters
00549 (int height, int width, float frequency, bool nss, float _alpha,
00550 int _k, GeometricalCheck geom, int dilevels)
00551 : image_rows(height), image_cols(width), use_nss(nss), alpha(_alpha), k(_k),
00552 geom_check(geom), di_levels(dilevels)
00553 {
00554 set(frequency);
00555 }
00556
00557
00558
00559 template <class TDescriptor, class F>
00560 void TemplatedLoopDetector<TDescriptor,F>::Parameters::set(float f)
00561 {
00562 dislocal = 20 * f;
00563 max_db_results = 50 * f;
00564 min_nss_factor = 0.005;
00565 min_matches_per_group = f;
00566 max_intragroup_gap = 3 * f;
00567 max_distance_between_groups = 3 * f;
00568 max_distance_between_queries = 2 * f;
00569
00570 min_Fpoints = 12;
00571 max_ransac_iterations = 500;
00572 ransac_probability = 0.99;
00573 max_reprojection_error = 2.0;
00574
00575 max_neighbor_ratio = 0.6;
00576 }
00577
00578
00579
00580 template<class TDescriptor, class F>
00581 TemplatedLoopDetector<TDescriptor,F>::TemplatedLoopDetector
00582 (const Parameters ¶ms)
00583 : m_database(NULL), m_params(params)
00584 {
00585 }
00586
00587
00588
00589 template<class TDescriptor, class F>
00590 TemplatedLoopDetector<TDescriptor,F>::TemplatedLoopDetector
00591 (const TemplatedVocabulary<TDescriptor, F> &voc, const Parameters ¶ms)
00592 : m_params(params)
00593 {
00594 m_database = new TemplatedDatabase<TDescriptor, F>(voc,
00595 params.geom_check == GEOM_DI, params.di_levels);
00596
00597 m_fsolver.setImageSize(params.image_cols, params.image_rows);
00598 }
00599
00600
00601
00602 template<class TDescriptor, class F>
00603 void TemplatedLoopDetector<TDescriptor,F>::setVocabulary
00604 (const TemplatedVocabulary<TDescriptor, F>& voc)
00605 {
00606 delete m_database;
00607 m_database = new TemplatedDatabase<TDescriptor, F>(voc,
00608 m_params.geom_check == GEOM_DI, m_params.di_levels);
00609 }
00610
00611
00612
00613 template<class TDescriptor, class F>
00614 TemplatedLoopDetector<TDescriptor, F>::TemplatedLoopDetector
00615 (const TemplatedDatabase<TDescriptor, F> &db, const Parameters ¶ms)
00616 : m_params(params)
00617 {
00618 m_database = new TemplatedDatabase<TDescriptor, F>(db.getVocabulary(),
00619 params.geom_check == GEOM_DI, params.di_levels);
00620
00621 m_fsolver.setImageSize(params.image_cols, params.image_rows);
00622 }
00623
00624
00625
00626 template<class TDescriptor, class F>
00627 template<class T>
00628 TemplatedLoopDetector<TDescriptor, F>::TemplatedLoopDetector
00629 (const T &db, const Parameters ¶ms)
00630 : m_params(params)
00631 {
00632 m_database = new T(db);
00633 m_database->clear();
00634
00635 m_fsolver.setImageSize(params.image_cols, params.image_rows);
00636 }
00637
00638
00639
00640 template<class TDescriptor, class F>
00641 template<class T>
00642 void TemplatedLoopDetector<TDescriptor, F>::setDatabase(const T &db)
00643 {
00644 delete m_database;
00645 m_database = new T(db);
00646 clear();
00647 }
00648
00649
00650
00651 template<class TDescriptor, class F>
00652 TemplatedLoopDetector<TDescriptor, F>::~TemplatedLoopDetector(void)
00653 {
00654 delete m_database;
00655 m_database = NULL;
00656 }
00657
00658
00659
00660 template<class TDescriptor, class F>
00661 void TemplatedLoopDetector<TDescriptor,F>::allocate
00662 (int nentries, int nkeys)
00663 {
00664 const int sz = (const int)m_image_keys.size();
00665
00666 if(sz < nentries)
00667 {
00668 m_image_keys.resize(nentries);
00669 m_image_descriptors.resize(nentries);
00670 }
00671
00672 if(nkeys > 0)
00673 {
00674 for(int i = sz; i < nentries; ++i)
00675 {
00676 m_image_keys[i].reserve(nkeys);
00677 m_image_descriptors[i].reserve(nkeys);
00678 }
00679 }
00680
00681 m_database->allocate(nentries, nkeys);
00682 }
00683
00684
00685
00686 template<class TDescriptor, class F>
00687 inline const TemplatedDatabase<TDescriptor, F>&
00688 TemplatedLoopDetector<TDescriptor, F>::getDatabase() const
00689 {
00690 return *m_database;
00691 }
00692
00693
00694
00695 template<class TDescriptor, class F>
00696 inline const TemplatedVocabulary<TDescriptor, F>&
00697 TemplatedLoopDetector<TDescriptor, F>::getVocabulary() const
00698 {
00699 return m_database->getVocabulary();
00700 }
00701
00702
00703
00704 template<class TDescriptor, class F>
00705 bool TemplatedLoopDetector<TDescriptor, F>::detectLoop(
00706 const std::vector<cv::KeyPoint> &keys,
00707 const std::vector<TDescriptor> &descriptors,
00708 DetectionResult &match)
00709 {
00710 EntryId entry_id = m_database->size();
00711 match.query = entry_id;
00712
00713 BowVector bowvec;
00714 FeatureVector featvec;
00715
00716 if(m_params.geom_check == GEOM_DI)
00717 m_database->getVocabulary()->transform(descriptors, bowvec, featvec,
00718 m_params.di_levels);
00719 else
00720 m_database->getVocabulary()->transform(descriptors, bowvec);
00721
00722 if((int)entry_id <= m_params.dislocal)
00723 {
00724
00725 m_database->add(bowvec, featvec);
00726 match.status = CLOSE_MATCHES_ONLY;
00727 }
00728 else
00729 {
00730 int max_id = (int)entry_id - m_params.dislocal;
00731
00732 QueryResults qret;
00733 m_database->query(bowvec, qret, m_params.max_db_results, max_id);
00734
00735
00736 m_database->add(bowvec, featvec);
00737
00738 if(!qret.empty())
00739 {
00740
00741 double ns_factor = 1.0;
00742
00743 if(m_params.use_nss)
00744 {
00745 ns_factor = m_database->getVocabulary()->score(bowvec, m_last_bowvec);
00746 }
00747
00748 if(!m_params.use_nss || ns_factor >= m_params.min_nss_factor)
00749 {
00750
00751
00752
00753
00754
00755
00756 removeLowScores(qret, m_params.alpha * ns_factor);
00757
00758 if(!qret.empty())
00759 {
00760
00761 match.match = qret[0].Id;
00762
00763
00764 vector<tIsland> islands;
00765 computeIslands(qret, islands);
00766
00767
00768
00769 if(!islands.empty())
00770 {
00771 const tIsland& island =
00772 *std::max_element(islands.begin(), islands.end());
00773
00774
00775 updateTemporalWindow(island, entry_id);
00776
00777
00778 match.match = island.best_entry;
00779
00780 if(getConsistentEntries() > m_params.k)
00781 {
00782
00783
00784 bool detection;
00785
00786 if(m_params.geom_check == GEOM_DI)
00787 {
00788
00789 detection = isGeometricallyConsistent_DI(island.best_entry,
00790 keys, descriptors, featvec);
00791 }
00792 else if(m_params.geom_check == GEOM_FLANN)
00793 {
00794 cv::FlannBasedMatcher flann_structure;
00795 getFlannStructure(descriptors, flann_structure);
00796
00797 detection = isGeometricallyConsistent_Flann(island.best_entry,
00798 keys, descriptors, flann_structure);
00799 }
00800 else if(m_params.geom_check == GEOM_EXHAUSTIVE)
00801 {
00802 detection = isGeometricallyConsistent_Exhaustive(
00803 m_image_keys[island.best_entry],
00804 m_image_descriptors[island.best_entry],
00805 keys, descriptors);
00806 }
00807 else
00808 {
00809 detection = true;
00810 }
00811
00812 if(detection)
00813 {
00814 match.status = LOOP_DETECTED;
00815 }
00816 else
00817 {
00818 match.status = NO_GEOMETRICAL_CONSISTENCY;
00819 }
00820
00821 }
00822 else
00823 {
00824 match.status = NO_TEMPORAL_CONSISTENCY;
00825 }
00826
00827 }
00828 else
00829 {
00830 match.status = NO_GROUPS;
00831 }
00832 }
00833 else
00834 {
00835 match.status = LOW_SCORES;
00836 }
00837 }
00838 else
00839 {
00840 match.status = LOW_NSS_FACTOR;
00841 }
00842 }
00843 else
00844 {
00845 match.status = NO_DB_RESULTS;
00846 }
00847 }
00848
00849
00850
00851 if(m_image_keys.size() == entry_id)
00852 {
00853 m_image_keys.push_back(keys);
00854 m_image_descriptors.push_back(descriptors);
00855 }
00856 else
00857 {
00858 m_image_keys[entry_id] = keys;
00859 m_image_descriptors[entry_id] = descriptors;
00860 }
00861
00862
00863 if(m_params.use_nss && (int)entry_id + 1 > m_params.dislocal)
00864 {
00865 m_last_bowvec = bowvec;
00866 }
00867
00868 return match.detection();
00869 }
00870
00871
00872
00873 template<class TDescriptor, class F>
00874 inline void TemplatedLoopDetector<TDescriptor, F>::clear()
00875 {
00876 m_database->clear();
00877 m_window.nentries = 0;
00878 }
00879
00880
00881
00882 template<class TDescriptor, class F>
00883 void TemplatedLoopDetector<TDescriptor, F>::computeIslands
00884 (QueryResults &q, vector<tIsland> &islands) const
00885 {
00886 islands.clear();
00887
00888 if(q.size() == 1)
00889 {
00890 islands.push_back(tIsland(q[0].Id, q[0].Id, calculateIslandScore(q, 0, 0)));
00891 islands.back().best_entry = q[0].Id;
00892 islands.back().best_score = q[0].Score;
00893 }
00894 else if(!q.empty())
00895 {
00896
00897 std::sort(q.begin(), q.end(), Result::ltId);
00898
00899
00900 QueryResults::const_iterator dit = q.begin();
00901 int first_island_entry = dit->Id;
00902 int last_island_entry = dit->Id;
00903
00904
00905 unsigned int i_first = 0;
00906 unsigned int i_last = 0;
00907
00908 double best_score = dit->Score;
00909 EntryId best_entry = dit->Id;
00910
00911 ++dit;
00912 for(unsigned int idx = 1; dit != q.end(); ++dit, ++idx)
00913 {
00914 if((int)dit->Id - last_island_entry < m_params.max_intragroup_gap)
00915 {
00916
00917 last_island_entry = dit->Id;
00918 i_last = idx;
00919 if(dit->Score > best_score)
00920 {
00921 best_score = dit->Score;
00922 best_entry = dit->Id;
00923 }
00924 }
00925 else
00926 {
00927
00928 int length = last_island_entry - first_island_entry + 1;
00929 if(length >= m_params.min_matches_per_group)
00930 {
00931 islands.push_back( tIsland(first_island_entry, last_island_entry,
00932 calculateIslandScore(q, i_first, i_last)) );
00933
00934 islands.back().best_score = best_score;
00935 islands.back().best_entry = best_entry;
00936 }
00937
00938
00939 first_island_entry = last_island_entry = dit->Id;
00940 i_first = i_last = idx;
00941 best_score = dit->Score;
00942 best_entry = dit->Id;
00943 }
00944 }
00945
00946 if(last_island_entry - first_island_entry + 1 >=
00947 m_params.min_matches_per_group)
00948 {
00949 islands.push_back( tIsland(first_island_entry, last_island_entry,
00950 calculateIslandScore(q, i_first, i_last)) );
00951
00952 islands.back().best_score = best_score;
00953 islands.back().best_entry = best_entry;
00954 }
00955 }
00956
00957 }
00958
00959
00960
00961 template<class TDescriptor, class F>
00962 double TemplatedLoopDetector<TDescriptor, F>::calculateIslandScore(
00963 const QueryResults &q, unsigned int i_first, unsigned int i_last) const
00964 {
00965
00966 double sum = 0;
00967 while(i_first <= i_last) sum += q[i_first++].Score;
00968 return sum;
00969 }
00970
00971
00972
00973 template<class TDescriptor, class F>
00974 void TemplatedLoopDetector<TDescriptor, F>::updateTemporalWindow
00975 (const tIsland &matched_island, EntryId entry_id)
00976 {
00977
00978
00979
00980 if(m_window.nentries == 0 || int(entry_id - m_window.last_query_id)
00981 > m_params.max_distance_between_queries)
00982 {
00983 m_window.nentries = 1;
00984 }
00985 else
00986 {
00987 EntryId a1 = m_window.last_matched_island.first;
00988 EntryId a2 = m_window.last_matched_island.last;
00989 EntryId b1 = matched_island.first;
00990 EntryId b2 = matched_island.last;
00991
00992 bool fit = (b1 <= a1 && a1 <= b2) || (a1 <= b1 && b1 <= a2);
00993
00994 if(!fit)
00995 {
00996 int d1 = (int)a1 - (int)b2;
00997 int d2 = (int)b1 - (int)a2;
00998 int gap = (d1 > d2 ? d1 : d2);
00999
01000 fit = (gap <= m_params.max_distance_between_groups);
01001 }
01002
01003 if(fit) m_window.nentries++;
01004 else m_window.nentries = 1;
01005 }
01006
01007 m_window.last_matched_island = matched_island;
01008 m_window.last_query_id = entry_id;
01009 }
01010
01011
01012
01013 template<class TDescriptor, class F>
01014 bool TemplatedLoopDetector<TDescriptor, F>::isGeometricallyConsistent_DI(
01015 EntryId old_entry, const std::vector<cv::KeyPoint> &keys,
01016 const std::vector<TDescriptor> &descriptors,
01017 const FeatureVector &bowvec) const
01018 {
01019 const FeatureVector &oldvec = m_database->retrieveFeatures(old_entry);
01020
01021
01022
01023 vector<unsigned int> i_old, i_cur;
01024
01025 FeatureVector::const_iterator old_it, cur_it;
01026 const FeatureVector::const_iterator old_end = oldvec.end();
01027 const FeatureVector::const_iterator cur_end = bowvec.end();
01028
01029 old_it = oldvec.begin();
01030 cur_it = bowvec.begin();
01031
01032 while(old_it != old_end && cur_it != cur_end)
01033 {
01034 if(old_it->first == cur_it->first)
01035 {
01036
01037
01038
01039 vector<unsigned int> i_old_now, i_cur_now;
01040
01041 getMatches_neighratio(
01042 m_image_descriptors[old_entry], old_it->second,
01043 descriptors, cur_it->second,
01044 i_old_now, i_cur_now);
01045
01046 i_old.insert(i_old.end(), i_old_now.begin(), i_old_now.end());
01047 i_cur.insert(i_cur.end(), i_cur_now.begin(), i_cur_now.end());
01048
01049
01050 ++old_it;
01051 ++cur_it;
01052 }
01053 else if(old_it->first < cur_it->first)
01054 {
01055
01056 old_it = oldvec.lower_bound(cur_it->first);
01057
01058 }
01059 else
01060 {
01061
01062 cur_it = bowvec.lower_bound(old_it->first);
01063
01064 }
01065 }
01066
01067
01068 if((int)i_old.size() >= m_params.min_Fpoints)
01069 {
01070 vector<cv::Point2f> old_points, cur_points;
01071
01072
01073 vector<unsigned int>::const_iterator oit, cit;
01074 oit = i_old.begin();
01075 cit = i_cur.begin();
01076
01077 for(; oit != i_old.end(); ++oit, ++cit)
01078 {
01079 const cv::KeyPoint &old_k = m_image_keys[old_entry][*oit];
01080 const cv::KeyPoint &cur_k = keys[*cit];
01081
01082 old_points.push_back(old_k.pt);
01083 cur_points.push_back(cur_k.pt);
01084 }
01085
01086 cv::Mat oldMat(old_points.size(), 2, CV_32F, &old_points[0]);
01087 cv::Mat curMat(cur_points.size(), 2, CV_32F, &cur_points[0]);
01088
01089 return m_fsolver.checkFundamentalMat(oldMat, curMat,
01090 m_params.max_reprojection_error, m_params.min_Fpoints,
01091 m_params.ransac_probability, m_params.max_ransac_iterations);
01092 }
01093
01094 return false;
01095 }
01096
01097
01098
01099 template<class TDescriptor, class F>
01100 bool TemplatedLoopDetector<TDescriptor, F>::
01101 isGeometricallyConsistent_Exhaustive(
01102 const std::vector<cv::KeyPoint> &old_keys,
01103 const std::vector<TDescriptor> &old_descriptors,
01104 const std::vector<cv::KeyPoint> &cur_keys,
01105 const std::vector<TDescriptor> &cur_descriptors) const
01106 {
01107 vector<unsigned int> i_old, i_cur;
01108 vector<unsigned int> i_all_old, i_all_cur;
01109
01110 i_all_old.reserve(old_keys.size());
01111 i_all_cur.reserve(cur_keys.size());
01112
01113 for(unsigned int i = 0; i < old_keys.size(); ++i)
01114 {
01115 i_all_old.push_back(i);
01116 }
01117
01118 for(unsigned int i = 0; i < cur_keys.size(); ++i)
01119 {
01120 i_all_cur.push_back(i);
01121 }
01122
01123 getMatches_neighratio(old_descriptors, i_all_old,
01124 cur_descriptors, i_all_cur, i_old, i_cur);
01125
01126 if((int)i_old.size() >= m_params.min_Fpoints)
01127 {
01128
01129 vector<unsigned int>::const_iterator oit, cit;
01130 oit = i_old.begin();
01131 cit = i_cur.begin();
01132
01133 vector<cv::Point2f> old_points, cur_points;
01134 old_points.reserve(i_old.size());
01135 cur_points.reserve(i_cur.size());
01136
01137 for(; oit != i_old.end(); ++oit, ++cit)
01138 {
01139 const cv::KeyPoint &old_k = old_keys[*oit];
01140 const cv::KeyPoint &cur_k = cur_keys[*cit];
01141
01142 old_points.push_back(old_k.pt);
01143 cur_points.push_back(cur_k.pt);
01144 }
01145
01146 cv::Mat oldMat(old_points.size(), 2, CV_32F, &old_points[0]);
01147 cv::Mat curMat(cur_points.size(), 2, CV_32F, &cur_points[0]);
01148
01149 return m_fsolver.checkFundamentalMat(oldMat, curMat,
01150 m_params.max_reprojection_error, m_params.min_Fpoints,
01151 m_params.ransac_probability, m_params.max_ransac_iterations);
01152 }
01153
01154 return false;
01155 }
01156
01157
01158
01159 template<class TDescriptor, class F>
01160 void TemplatedLoopDetector<TDescriptor, F>::getFlannStructure(
01161 const std::vector<TDescriptor> &descriptors,
01162 cv::FlannBasedMatcher &flann_structure) const
01163 {
01164 vector<cv::Mat> features(1);
01165 F::toMat32F(descriptors, features[0]);
01166
01167 flann_structure.clear();
01168 flann_structure.add(features);
01169 flann_structure.train();
01170 }
01171
01172
01173
01174 template<class TDescriptor, class F>
01175 bool TemplatedLoopDetector<TDescriptor, F>::isGeometricallyConsistent_Flann
01176 (EntryId old_entry,
01177 const std::vector<cv::KeyPoint> &keys,
01178 const std::vector<TDescriptor> &descriptors,
01179 cv::FlannBasedMatcher &flann_structure) const
01180 {
01181 vector<unsigned int> i_old, i_cur;
01182
01183 const vector<cv::KeyPoint>& old_keys = m_image_keys[old_entry];
01184 const vector<TDescriptor>& old_descs = m_image_descriptors[old_entry];
01185 const vector<cv::KeyPoint>& cur_keys = keys;
01186
01187 vector<cv::Mat> queryDescs_v(1);
01188 F::toMat32F(old_descs, queryDescs_v[0]);
01189
01190 vector<vector<cv::DMatch> > matches;
01191
01192 flann_structure.knnMatch(queryDescs_v[0], matches, 2);
01193
01194 for(int old_idx = 0; old_idx < (int)matches.size(); ++old_idx)
01195 {
01196 if(!matches[old_idx].empty())
01197 {
01198 int cur_idx = matches[old_idx][0].trainIdx;
01199 float dist = matches[old_idx][0].distance;
01200
01201 bool ok = true;
01202 if(matches[old_idx].size() >= 2)
01203 {
01204 float dist_ratio = dist / matches[old_idx][1].distance;
01205 ok = dist_ratio <= m_params.max_neighbor_ratio;
01206 }
01207
01208 if(ok)
01209 {
01210 vector<unsigned int>::iterator cit =
01211 std::find(i_cur.begin(), i_cur.end(), cur_idx);
01212
01213 if(cit == i_cur.end())
01214 {
01215 i_old.push_back(old_idx);
01216 i_cur.push_back(cur_idx);
01217 }
01218 else
01219 {
01220 int idx = i_old[ cit - i_cur.begin() ];
01221 if(dist < matches[idx][0].distance)
01222 {
01223 i_old[ cit - i_cur.begin() ] = old_idx;
01224 }
01225 }
01226 }
01227 }
01228 }
01229
01230 if((int)i_old.size() >= m_params.min_Fpoints)
01231 {
01232
01233 vector<unsigned int>::const_iterator oit, cit;
01234 oit = i_old.begin();
01235 cit = i_cur.begin();
01236
01237 vector<cv::Point2f> old_points, cur_points;
01238 old_points.reserve(i_old.size());
01239 cur_points.reserve(i_cur.size());
01240
01241 for(; oit != i_old.end(); ++oit, ++cit)
01242 {
01243 const cv::KeyPoint &old_k = old_keys[*oit];
01244 const cv::KeyPoint &cur_k = cur_keys[*cit];
01245
01246 old_points.push_back(old_k.pt);
01247 cur_points.push_back(cur_k.pt);
01248 }
01249
01250 cv::Mat oldMat(old_points.size(), 2, CV_32F, &old_points[0]);
01251 cv::Mat curMat(cur_points.size(), 2, CV_32F, &cur_points[0]);
01252
01253 return m_fsolver.checkFundamentalMat(oldMat, curMat,
01254 m_params.max_reprojection_error, m_params.min_Fpoints,
01255 m_params.ransac_probability, m_params.max_ransac_iterations);
01256 }
01257
01258 return false;
01259 }
01260
01261
01262
01263 template<class TDescriptor, class F>
01264 void TemplatedLoopDetector<TDescriptor, F>::getMatches_neighratio(
01265 const vector<TDescriptor> &A, const vector<unsigned int> &i_A,
01266 const vector<TDescriptor> &B, const vector<unsigned int> &i_B,
01267 vector<unsigned int> &i_match_A, vector<unsigned int> &i_match_B) const
01268 {
01269 i_match_A.resize(0);
01270 i_match_B.resize(0);
01271 i_match_A.reserve( min(i_A.size(), i_B.size()) );
01272 i_match_B.reserve( min(i_A.size(), i_B.size()) );
01273
01274 vector<unsigned int>::const_iterator ait, bit;
01275 unsigned int i, j;
01276 i = 0;
01277 for(ait = i_A.begin(); ait != i_A.end(); ++ait, ++i)
01278 {
01279 int best_j_now = -1;
01280 double best_dist_1 = 1e9;
01281 double best_dist_2 = 1e9;
01282
01283 j = 0;
01284 for(bit = i_B.begin(); bit != i_B.end(); ++bit, ++j)
01285 {
01286 double d = F::distance(A[*ait], B[*bit]);
01287
01288
01289 if(d < best_dist_1)
01290 {
01291 best_j_now = j;
01292 best_dist_2 = best_dist_1;
01293 best_dist_1 = d;
01294 }
01295 else if(d < best_dist_2)
01296 {
01297 best_dist_2 = d;
01298 }
01299 }
01300
01301 if(best_dist_1 / best_dist_2 <= m_params.max_neighbor_ratio)
01302 {
01303 unsigned int idx_B = i_B[best_j_now];
01304 bit = find(i_match_B.begin(), i_match_B.end(), idx_B);
01305
01306 if(bit == i_match_B.end())
01307 {
01308 i_match_B.push_back(idx_B);
01309 i_match_A.push_back(*ait);
01310 }
01311 else
01312 {
01313 unsigned int idx_A = i_match_A[ bit - i_match_B.begin() ];
01314 double d = F::distance(A[idx_A], B[idx_B]);
01315 if(best_dist_1 < d)
01316 {
01317 i_match_A[ bit - i_match_B.begin() ] = *ait;
01318 }
01319 }
01320
01321 }
01322 }
01323 }
01324
01325
01326
01327 template<class TDescriptor, class F>
01328 void TemplatedLoopDetector<TDescriptor, F>::removeLowScores(QueryResults &q,
01329 double threshold) const
01330 {
01331
01332
01333
01334
01335 Result aux(0, threshold);
01336 QueryResults::iterator qit =
01337 lower_bound(q.begin(), q.end(), aux, Result::geq);
01338
01339
01340
01341 if(qit != q.end())
01342 {
01343 int valid_entries = qit - q.begin();
01344 q.resize(valid_entries);
01345 }
01346 }
01347
01348
01349
01350 }
01351
01352 #endif