mirror of
https://github.com/introlab/rtabmap.git
synced 2026-10-06 18:17:47 +08:00
Add FlannIndex abstract interface and implement NanoFlannIndex subclass (#1744)
* Add FlannIndex abstract interface and implement NanoFlannIndex subclass * Refactored: made NanoFlann a new NN type instead of inheriting FlannIndex. Added tests. Vendoring nanoflann.h directly in the repo. RegistrationVis now use NANOFLANN_INDEX_KDTREE_SINGLE (instead of FLANN_INDEX_KDTREE_SINGLE) flann index for 2d points matching. * cleanup comments, added FlannIndex doxygen * Fixing windows tests * updating flaky test * Simplified interface, added flann kdtree single approach selectable by parameters. * RegVis: symmetry of nanoflann for two branches of guess feature matching * cv::BFMatcher baseline * Small cmake optimization FLANN_KDTREE_MEM_OPT only defined for FlannIndex * Refactored where FLANN_KDTREE_MEM_OPT is defined * fixed file name already exist * cleanup * fixup build --------- Co-authored-by: matlabbe <[email protected]>
This commit is contained in:
@@ -56,6 +56,43 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
namespace rtabmap
|
||||
{
|
||||
|
||||
// Whether the strategy searches with a FlannIndex, as opposed to the brute
|
||||
// force ones matching against the _dataTree matrix.
|
||||
static bool isFlannStrategy(VWDictionary::NNStrategy strategy)
|
||||
{
|
||||
return strategy == VWDictionary::kNNFlannNaive ||
|
||||
strategy == VWDictionary::kNNFlannKdTree ||
|
||||
strategy == VWDictionary::kNNFlannLSH ||
|
||||
strategy == VWDictionary::kNNNanoFlannKdTree ||
|
||||
strategy == VWDictionary::kNNFlannKdTreeSingle;
|
||||
}
|
||||
|
||||
// Whether the strategy indexes float descriptors in a kd-tree, in which case
|
||||
// binary descriptors have to be converted first.
|
||||
static bool isKdTreeStrategy(VWDictionary::NNStrategy strategy)
|
||||
{
|
||||
return strategy == VWDictionary::kNNFlannKdTree ||
|
||||
strategy == VWDictionary::kNNNanoFlannKdTree ||
|
||||
strategy == VWDictionary::kNNFlannKdTreeSingle;
|
||||
}
|
||||
|
||||
static FlannIndex::flann_algorithm_t flannAlgorithm(VWDictionary::NNStrategy strategy)
|
||||
{
|
||||
switch(strategy)
|
||||
{
|
||||
case VWDictionary::kNNFlannNaive:
|
||||
return FlannIndex::FLANN_INDEX_LINEAR;
|
||||
case VWDictionary::kNNFlannLSH:
|
||||
return FlannIndex::FLANN_INDEX_LSH;
|
||||
case VWDictionary::kNNNanoFlannKdTree:
|
||||
return FlannIndex::NANOFLANN_INDEX_KDTREE_SINGLE;
|
||||
case VWDictionary::kNNFlannKdTreeSingle:
|
||||
return FlannIndex::FLANN_INDEX_KDTREE_SINGLE;
|
||||
default:
|
||||
return FlannIndex::FLANN_INDEX_KDTREE; // kNNFlannKdTree
|
||||
}
|
||||
}
|
||||
|
||||
const int VWDictionary::ID_START = 1;
|
||||
const int VWDictionary::ID_INVALID = 0;
|
||||
|
||||
@@ -115,7 +152,17 @@ void VWDictionary::parseParameters(const ParametersMap & parameters)
|
||||
NNStrategy nnStrategy = (NNStrategy)std::atoi((*iter).second.c_str());
|
||||
treeUpdated = this->setNNStrategy(nnStrategy);
|
||||
}
|
||||
if(!treeUpdated && byteToFloat!=_byteToFloat && _strategy == kNNFlannKdTree)
|
||||
if(_strategy == kNNFlannKdTreeSingle && _incrementalDictionary && _incrementalFlann)
|
||||
{
|
||||
UWARN("%s=%d (%s) rebuilds its whole index every time a word is added, which "
|
||||
"is very slow with %s=true. It is meant for an index built once and "
|
||||
"searched once, like the one matching the features of two frames.",
|
||||
Parameters::kKpNNStrategy().c_str(), (int)_strategy,
|
||||
nnStrategyName(_strategy).c_str(),
|
||||
Parameters::kKpIncrementalFlann().c_str());
|
||||
}
|
||||
|
||||
if(!treeUpdated && byteToFloat!=_byteToFloat && isKdTreeStrategy(_strategy))
|
||||
{
|
||||
UINFO("KDTree: Binary to Float conversion approach has changed, re-initialize kd-tree.");
|
||||
this->rebuildIndex();
|
||||
@@ -392,7 +439,7 @@ unsigned long VWDictionary::getMemoryUsed() const
|
||||
memoryUsage += _visualWords.size()*(sizeof(int) + _visualWords.rbegin()->second->getMemoryUsed() + sizeof(std::map<int, VisualWord *>::iterator)) + sizeof(std::map<int, VisualWord *>);
|
||||
if(_dataTree.empty() &&
|
||||
_visualWords.begin()->second->getDescriptor().type() == CV_8U &&
|
||||
_strategy == kNNFlannKdTree)
|
||||
isKdTreeStrategy(_strategy))
|
||||
{
|
||||
// Binary descriptors were converted to float, and not included in _dataTree
|
||||
memoryUsage += _visualWords.size() * _visualWords.begin()->second->getDescriptor().total() * sizeof(float) * (_byteToFloat?1:8);
|
||||
@@ -507,7 +554,7 @@ void VWDictionary::update()
|
||||
|
||||
if(!firstUpdate &&
|
||||
_incrementalFlann &&
|
||||
_strategy < kNNBruteForce &&
|
||||
isFlannStrategy(_strategy) &&
|
||||
_visualWords.size())
|
||||
{
|
||||
ULOGGER_DEBUG("Incremental FLANN: Removing %d words...", (int)_removedIndexedWords.size());
|
||||
@@ -535,7 +582,7 @@ void VWDictionary::update()
|
||||
if(w->getDescriptor().type() == CV_8U)
|
||||
{
|
||||
useDistanceL1_ = true;
|
||||
if(_strategy == kNNFlannKdTree)
|
||||
if(isKdTreeStrategy(_strategy))
|
||||
{
|
||||
descriptor = convertBinTo32F(w->getDescriptor(), _byteToFloat);
|
||||
}
|
||||
@@ -555,9 +602,7 @@ void VWDictionary::update()
|
||||
UDEBUG("Building FLANN index... (strategy=%s, byteToFloat=%s, useDistanceL1=%s, rebalancingFactor=%f)",
|
||||
nnStrategyName(_strategy).c_str(), _byteToFloat?"true":"false", useDistanceL1_?"true":"false", _rebalancingFactor);
|
||||
_flannIndex->buildIndex(
|
||||
_strategy == kNNFlannNaive ? FlannIndex::FLANN_INDEX_LINEAR:
|
||||
_strategy == kNNFlannLSH ? FlannIndex::FLANN_INDEX_LSH:
|
||||
FlannIndex::FLANN_INDEX_KDTREE, // kNNFlannKdTree
|
||||
flannAlgorithm(_strategy),
|
||||
descriptor, useDistanceL1_, _rebalancingFactor);
|
||||
UDEBUG("Building FLANN index... done!");
|
||||
}
|
||||
@@ -577,7 +622,7 @@ void VWDictionary::update()
|
||||
ULOGGER_DEBUG("Incremental FLANN: Inserting %d words... done! (in %f s)", (int)_notIndexedWords.size(), timer.ticks());
|
||||
}
|
||||
}
|
||||
else if(_strategy >= kNNBruteForce &&
|
||||
else if(!isFlannStrategy(_strategy) &&
|
||||
_notIndexedWords.size() &&
|
||||
_removedIndexedWords.size() == 0 &&
|
||||
_visualWords.size())
|
||||
@@ -587,8 +632,8 @@ void VWDictionary::update()
|
||||
if(_dataTree.rows >= IMGIDX_ONE)
|
||||
{
|
||||
UWARN("%s=%d is not a FLANN strategy and the number of words in the vocabulary (%d) is over %d (IMGIDX_ONE), so opencv may "
|
||||
"assert on an IMGIDX_ONE check when adding new words. Use a FLANN strategy instead (%s<%d).",
|
||||
Parameters::kKpNNStrategy().c_str(), _strategy, _dataTree.rows, IMGIDX_ONE, Parameters::kKpNNStrategy().c_str(), kNNBruteForce);
|
||||
"assert on an IMGIDX_ONE check when adding new words. Use a FLANN strategy instead (e.g. %s=%d).",
|
||||
Parameters::kKpNNStrategy().c_str(), _strategy, _dataTree.rows, IMGIDX_ONE, Parameters::kKpNNStrategy().c_str(), kNNFlannKdTree);
|
||||
}
|
||||
|
||||
//just add not indexed words
|
||||
@@ -633,7 +678,7 @@ void VWDictionary::update()
|
||||
if(_visualWords.begin()->second->getDescriptor().type() == CV_8U)
|
||||
{
|
||||
useDistanceL1_ = true;
|
||||
if(_strategy == kNNFlannKdTree)
|
||||
if(isKdTreeStrategy(_strategy))
|
||||
{
|
||||
type = CV_32F;
|
||||
if(!_byteToFloat)
|
||||
@@ -662,7 +707,7 @@ void VWDictionary::update()
|
||||
cv::Mat descriptor;
|
||||
if(iter->second->getDescriptor().type() == CV_8U)
|
||||
{
|
||||
if(_strategy == kNNFlannKdTree)
|
||||
if(isKdTreeStrategy(_strategy))
|
||||
{
|
||||
descriptor = convertBinTo32F(iter->second->getDescriptor(), _byteToFloat);
|
||||
}
|
||||
@@ -687,12 +732,10 @@ void VWDictionary::update()
|
||||
ULOGGER_DEBUG("_mapIndexId.size() = %d, words.size()=%d, _dim=%d",(int)_mapIndexId.size(), (int)_visualWords.size(), dim);
|
||||
ULOGGER_DEBUG("copying data = %f s", timer.ticks());
|
||||
|
||||
if(_strategy < kNNBruteForce)
|
||||
if(isFlannStrategy(_strategy))
|
||||
{
|
||||
_flannIndex->buildIndex(
|
||||
_strategy == kNNFlannNaive ? FlannIndex::FLANN_INDEX_LINEAR:
|
||||
_strategy == kNNFlannLSH ? FlannIndex::FLANN_INDEX_LSH:
|
||||
FlannIndex::FLANN_INDEX_KDTREE, // kNNFlannKdTree
|
||||
flannAlgorithm(_strategy),
|
||||
_dataTree,
|
||||
useDistanceL1_,
|
||||
_incrementalDictionary&&_incrementalFlann?_rebalancingFactor:1);
|
||||
@@ -714,7 +757,7 @@ void VWDictionary::update()
|
||||
|
||||
std::vector<unsigned char> VWDictionary::serializeIndex() const
|
||||
{
|
||||
if(_strategy >= kNNBruteForce) {
|
||||
if(!isFlannStrategy(_strategy)) {
|
||||
UINFO("Not flann strategy, ignoring serialization...");
|
||||
return std::vector<unsigned char>();
|
||||
}
|
||||
@@ -739,7 +782,7 @@ bool VWDictionary::deserializeIndex(const unsigned char * data, size_t size)
|
||||
return false;
|
||||
}
|
||||
UDEBUG("Loading flann index... (data size=%ld bytes)", size);
|
||||
if(_strategy >= kNNBruteForce) {
|
||||
if(!isFlannStrategy(_strategy)) {
|
||||
//ignore
|
||||
return false;
|
||||
}
|
||||
@@ -772,7 +815,7 @@ bool VWDictionary::deserializeIndex(const unsigned char * data, size_t size)
|
||||
if(_visualWords.begin()->second->getDescriptor().type() == CV_8U)
|
||||
{
|
||||
useDistanceL1_ = true;
|
||||
if(_strategy == kNNFlannKdTree)
|
||||
if(isKdTreeStrategy(_strategy))
|
||||
{
|
||||
type = CV_32F;
|
||||
if(!_byteToFloat)
|
||||
@@ -801,7 +844,7 @@ bool VWDictionary::deserializeIndex(const unsigned char * data, size_t size)
|
||||
cv::Mat descriptor;
|
||||
if(iter->second->getDescriptor().type() == CV_8U)
|
||||
{
|
||||
if(_strategy == kNNFlannKdTree)
|
||||
if(isKdTreeStrategy(_strategy))
|
||||
{
|
||||
descriptor = convertBinTo32F(iter->second->getDescriptor(), _byteToFloat);
|
||||
}
|
||||
@@ -830,9 +873,7 @@ bool VWDictionary::deserializeIndex(const unsigned char * data, size_t size)
|
||||
if(_flannIndex->loadIndex(
|
||||
data,
|
||||
size,
|
||||
_strategy == kNNFlannNaive ? FlannIndex::FLANN_INDEX_LINEAR:
|
||||
_strategy == kNNFlannLSH ? FlannIndex::FLANN_INDEX_LSH:
|
||||
FlannIndex::FLANN_INDEX_KDTREE,
|
||||
flannAlgorithm(_strategy),
|
||||
dataTree,
|
||||
useDistanceL1_,
|
||||
_incrementalDictionary && _incrementalFlann ? _rebalancingFactor:1,
|
||||
@@ -975,7 +1016,7 @@ std::list<int> VWDictionary::addNewWords(
|
||||
if(descriptorsIn.type() == CV_8U)
|
||||
{
|
||||
useDistanceL1_ = true;
|
||||
if(_strategy == kNNFlannKdTree)
|
||||
if(isKdTreeStrategy(_strategy))
|
||||
{
|
||||
descriptors = convertBinTo32F(descriptorsIn, _byteToFloat);
|
||||
}
|
||||
@@ -1031,7 +1072,7 @@ std::list<int> VWDictionary::addNewWords(
|
||||
//Find nearest neighbors
|
||||
UDEBUG("newPts.total()=%d _strategy=%d", descriptors.rows, _strategy);
|
||||
|
||||
if(_strategy == kNNFlannNaive || _strategy == kNNFlannKdTree || _strategy == kNNFlannLSH)
|
||||
if(isFlannStrategy(_strategy))
|
||||
{
|
||||
_flannIndex->knnSearch(descriptors, results, dists, k, KNN_CHECKS);
|
||||
}
|
||||
@@ -1308,7 +1349,7 @@ std::vector<int> VWDictionary::findNN(const cv::Mat & queryIn) const
|
||||
cv::Mat query;
|
||||
if(queryIn.type() == CV_8U)
|
||||
{
|
||||
if(_strategy == kNNFlannKdTree)
|
||||
if(isKdTreeStrategy(_strategy))
|
||||
{
|
||||
query = convertBinTo32F(queryIn, _byteToFloat);
|
||||
}
|
||||
@@ -1353,7 +1394,7 @@ std::vector<int> VWDictionary::findNN(const cv::Mat & queryIn) const
|
||||
//Find nearest neighbors
|
||||
UDEBUG("query.rows=%d ", query.rows);
|
||||
|
||||
if(_strategy == kNNFlannNaive || _strategy == kNNFlannKdTree || _strategy == kNNFlannLSH)
|
||||
if(isFlannStrategy(_strategy))
|
||||
{
|
||||
_flannIndex->knnSearch(query, results, dists, k, KNN_CHECKS);
|
||||
}
|
||||
@@ -1436,7 +1477,7 @@ std::vector<int> VWDictionary::findNN(const cv::Mat & queryIn) const
|
||||
cv::Mat descriptor;
|
||||
if(vw->getDescriptor().type() == CV_8U)
|
||||
{
|
||||
if(_strategy == kNNFlannKdTree)
|
||||
if(isKdTreeStrategy(_strategy))
|
||||
{
|
||||
descriptor = convertBinTo32F(vw->getDescriptor(), _byteToFloat);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user