2011-06-05 14:45:39 +00:00
/*
2014-08-11 17:00:55 +00:00
Copyright (c) 2010-2014, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
2011-06-05 14:45:39 +00:00
2012-06-24 17:19:34 +00:00
#include "rtabmap/core/VWDictionary.h"
2012-12-11 18:05:05 +00:00
#include "VisualWord.h"
2011-06-05 14:45:39 +00:00
2013-12-11 00:12:44 +00:00
#include "rtabmap/core/Signature.h"
2011-06-05 14:45:39 +00:00
#include "rtabmap/core/DBDriver.h"
#include "rtabmap/core/Parameters.h"
2013-04-30 20:16:01 +00:00
#include "rtabmap/utilite/UtiLite.h"
2011-06-05 14:45:39 +00:00
2015-06-18 23:00:46 -04:00
#include <opencv2/opencv_modules.hpp>
#if CV_MAJOR_VERSION < 3
2014-06-22 03:33:56 +00:00
#include <opencv2/gpu/gpu.hpp>
2015-06-18 23:00:46 -04:00
#else
#include <opencv2/core/cuda.hpp>
#ifdef HAVE_OPENCV_CUDAFEATURES2D
#include <opencv2/cudafeatures2d.hpp>
#endif
#endif
2014-06-22 03:33:56 +00:00
2011-06-05 14:45:39 +00:00
#include <fstream>
#include <string>
namespace rtabmap
{
const int VWDictionary :: ID_START = 1 ;
const int VWDictionary :: ID_INVALID = 0 ;
VWDictionary :: VWDictionary ( const ParametersMap & parameters ) :
_totalActiveReferences ( 0 ),
_incrementalDictionary ( Parameters :: defaultKpIncrementalDictionary ()),
_nndrRatio ( Parameters :: defaultKpNndrRatio ()),
_dictionaryPath ( Parameters :: defaultKpDictionaryPath ()),
2014-10-14 21:48:50 +00:00
_newWordsComparedTogether ( Parameters :: defaultKpNewWordsComparedTogether ()),
2011-06-05 14:45:39 +00:00
_lastWordId ( 0 ),
2014-06-22 03:33:56 +00:00
_flannIndex ( new cv :: flann :: Index ()),
2014-11-22 16:44:42 +00:00
_strategy ( kNNBruteForce )
2011-06-05 14:45:39 +00:00
{
2014-06-22 03:33:56 +00:00
this -> setNNStrategy (( NNStrategy ) Parameters :: defaultKpNNStrategy ());
2011-06-05 14:45:39 +00:00
this -> parseParameters ( parameters );
}
VWDictionary ::~ VWDictionary ()
{
this -> clear ();
2014-06-22 03:33:56 +00:00
delete _flannIndex ;
2011-06-05 14:45:39 +00:00
}
void VWDictionary :: parseParameters ( const ParametersMap & parameters )
{
ParametersMap :: const_iterator iter ;
2013-12-11 00:12:44 +00:00
Parameters :: parse ( parameters , Parameters :: kKpNndrRatio (), _nndrRatio );
2014-10-14 21:48:50 +00:00
Parameters :: parse ( parameters , Parameters :: kKpNewWordsComparedTogether (), _newWordsComparedTogether );
2013-12-11 00:12:44 +00:00
2015-02-04 21:20:17 -05:00
UASSERT_MSG ( _nndrRatio > 0.0f , uFormat ( "String=%s value=%f" , uContains ( parameters , Parameters :: kKpNndrRatio ()) ? parameters . at ( Parameters :: kKpNndrRatio ()). c_str () : "" , _nndrRatio ). c_str ());
2011-06-05 14:45:39 +00:00
std :: string dictionaryPath = _dictionaryPath ;
bool incrementalDictionary = _incrementalDictionary ;
if (( iter = parameters . find ( Parameters :: kKpDictionaryPath ())) != parameters . end ())
{
dictionaryPath = ( * iter ). second . c_str ();
}
if (( iter = parameters . find ( Parameters :: kKpIncrementalDictionary ())) != parameters . end ())
{
incrementalDictionary = uStr2Bool (( * iter ). second . c_str ());
}
// Verifying hypotheses strategy
if (( iter = parameters . find ( Parameters :: kKpNNStrategy ())) != parameters . end ())
{
2014-06-22 03:33:56 +00:00
NNStrategy nnStrategy = ( NNStrategy ) std :: atoi (( * iter ). second . c_str ());
this -> setNNStrategy ( nnStrategy );
2011-06-05 14:45:39 +00:00
}
2013-01-28 18:13:56 +00:00
2014-06-22 03:33:56 +00:00
if ( incrementalDictionary )
2011-06-05 14:45:39 +00:00
{
2014-06-22 03:33:56 +00:00
this -> setIncrementalDictionary ();
2011-06-05 14:45:39 +00:00
}
2014-06-22 03:33:56 +00:00
else
2011-06-05 14:45:39 +00:00
{
2014-06-22 03:33:56 +00:00
this -> setFixedDictionary ( dictionaryPath );
2011-06-05 14:45:39 +00:00
}
}
2014-06-22 03:33:56 +00:00
void VWDictionary :: setIncrementalDictionary ()
2011-06-05 14:45:39 +00:00
{
2014-06-22 03:33:56 +00:00
if ( ! _incrementalDictionary )
2011-06-05 14:45:39 +00:00
{
_incrementalDictionary = true ;
if ( _visualWords . size ())
{
UWARN ( "Incremental dictionary set: already loaded visual words (%d) from the fixed dictionary will be included in the incremental one." , _visualWords . size ());
}
}
2014-06-22 03:33:56 +00:00
_dictionaryPath = "" ;
}
void VWDictionary :: setFixedDictionary ( const std :: string & dictionaryPath )
{
if ( ! dictionaryPath . empty ())
{
if (( ! _incrementalDictionary && _dictionaryPath . compare ( dictionaryPath ) != 0 ) ||
_visualWords . size () == 0 )
{
std :: ifstream file ;
file . open ( dictionaryPath . c_str (), std :: ifstream :: in );
if ( file . good ())
{
UDEBUG ( "Deleting old dictionary and loading the new one from \" %s \" " , dictionaryPath . c_str ());
UTimer timer ;
// first line is the header
std :: string str ;
std :: list < std :: string > strList ;
std :: getline ( file , str );
strList = uSplitNumChar ( str );
unsigned int dimension = 0 ;
for ( std :: list < std :: string >:: iterator iter = strList . begin (); iter != strList . end (); ++ iter )
{
if ( uIsDigit ( iter -> at ( 0 )))
{
dimension = std :: atoi ( iter -> c_str ());
break ;
}
}
if ( dimension == 0 || dimension > 1000 )
{
UERROR ( "Invalid dictionary file, visual word dimension (%d) is not valid, \" %s \" " , dimension , dictionaryPath . c_str ());
}
else
{
// Process all words
while ( file . good ())
{
std :: getline ( file , str );
strList = uSplit ( str );
if ( strList . size () == dimension + 1 )
{
//first one is the visual word id
std :: list < std :: string >:: iterator iter = strList . begin ();
int id = std :: atoi ( iter -> c_str ());
cv :: Mat descriptor ( 1 , dimension , CV_32F );
++ iter ;
unsigned int i = 0 ;
//get descriptor
for (; i < dimension && iter != strList . end (); ++ i , ++ iter )
{
2015-02-04 21:20:17 -05:00
descriptor . at < float > ( i ) = uStr2Float ( * iter );
2014-06-22 03:33:56 +00:00
}
if ( i != dimension )
{
UERROR ( "" );
}
VisualWord * vw = new VisualWord ( id , descriptor , 0 );
_visualWords . insert ( _visualWords . end (), std :: pair < int , VisualWord *> ( id , vw ));
_notIndexedWords . insert ( _notIndexedWords . end (), id );
}
else
{
UWARN ( "Cannot parse line \" %s \" " , str . c_str ());
}
}
this -> update ();
_incrementalDictionary = false ;
}
UDEBUG ( "Time changing dictionary = %fs" , timer . ticks ());
}
else
{
UERROR ( "Cannot open dictionary file \" %s \" " , dictionaryPath . c_str ());
}
file . close ();
}
else if ( ! _incrementalDictionary )
{
UDEBUG ( "Dictionary \" %s \" already loaded..." , dictionaryPath . c_str ());
}
else
{
UERROR ( "Cannot change to a fixed dictionary if there are already words (%d) in the incremental one." , _visualWords . size ());
}
}
else if ( _visualWords . size () == 0 )
{
_incrementalDictionary = false ;
}
2015-05-06 00:29:54 -04:00
else if ( _incrementalDictionary )
{
UWARN ( "Cannot change to fixed dictionary, %d words already loaded as incremental" , ( int ) _visualWords . size ());
}
2011-06-05 14:45:39 +00:00
_dictionaryPath = dictionaryPath ;
}
2014-06-22 03:33:56 +00:00
void VWDictionary :: setNNStrategy ( NNStrategy strategy )
2011-06-05 14:45:39 +00:00
{
if ( strategy != kNNUndef )
{
2015-06-18 23:00:46 -04:00
#if CV_MAJOR_VERSION < 3
2014-06-22 03:33:56 +00:00
if ( strategy == kNNBruteForceGPU && ! cv :: gpu :: getCudaEnabledDeviceCount ())
2011-06-05 14:45:39 +00:00
{
2014-06-22 03:33:56 +00:00
UERROR ( "Nearest neighobr strategy \" kNNBruteForceGPU \" chosen but no CUDA devices found! Doing \" kNNBruteForce \" instead." );
strategy = kNNBruteForce ;
2011-06-05 14:45:39 +00:00
}
2015-06-18 23:00:46 -04:00
#else
if ( strategy == kNNBruteForceGPU && ! cv :: cuda :: getCudaEnabledDeviceCount ())
{
UERROR ( "Nearest neighobr strategy \" kNNBruteForceGPU \" chosen but no CUDA devices found! Doing \" kNNBruteForce \" instead." );
strategy = kNNBruteForce ;
}
#endif
#ifndef HAVE_OPENCV_CUDAFEATURES2D
if ( strategy == kNNBruteForceGPU )
{
UERROR ( "Nearest neighobr strategy \" kNNBruteForceGPU \" chosen but OpenCV cudafeatures2d module is not found! Doing \" kNNBruteForce \" instead." );
strategy = kNNBruteForce ;
}
#endif
2011-06-05 14:45:39 +00:00
2014-11-22 16:44:42 +00:00
if ( RTABMAP_NONFREE == 0 && strategy == kNNFlannKdTree )
{
UWARN ( "KdTree (%d) nearest neighbor is not available because RTAB-Map isn't built "
"with OpenCV nonfree module (KdTree only used for SURF/SIFT features). "
"NN strategy is not modified (current=%d)." , ( int ) kNNFlannKdTree , ( int ) _strategy );
}
else
{
_strategy = strategy ;
}
2011-06-05 14:45:39 +00:00
}
}
int VWDictionary :: getLastIndexedWordId () const
{
if ( _mapIndexId . size ())
{
return _mapIndexId . rbegin () -> second ;
}
else
{
return 0 ;
}
}
void VWDictionary :: update ()
{
ULOGGER_DEBUG ( "" );
2013-12-11 00:12:44 +00:00
if ( ! _incrementalDictionary && ! _notIndexedWords . size ())
2011-06-05 14:45:39 +00:00
{
// No need to update the search index if we
// use a fixed dictionary and the index is
// already built
return ;
}
2013-12-11 00:12:44 +00:00
if ( _notIndexedWords . size () || _visualWords . size () == 0 || _removedIndexedWords . size ())
2011-06-05 14:45:39 +00:00
{
2013-12-11 00:12:44 +00:00
_mapIndexId . clear ();
2014-06-22 03:33:56 +00:00
int oldSize = _dataTree . rows ;
2013-12-11 00:12:44 +00:00
_dataTree = cv :: Mat ();
2014-06-22 03:33:56 +00:00
_flannIndex -> release ();
2011-06-05 14:45:39 +00:00
2014-06-22 03:33:56 +00:00
if ( _visualWords . size ())
2011-06-05 14:45:39 +00:00
{
2013-12-11 00:12:44 +00:00
UTimer timer ;
timer . start ();
2011-06-05 14:45:39 +00:00
2014-06-22 03:33:56 +00:00
int type = _visualWords . begin () -> second -> getDescriptor (). type ();
int dim = _visualWords . begin () -> second -> getDescriptor (). cols ;
2013-12-11 00:12:44 +00:00
2014-06-22 03:33:56 +00:00
UASSERT ( type == CV_32F || type == CV_8U );
UASSERT ( dim > 0 );
// Create the data matrix
_dataTree = cv :: Mat ( _visualWords . size (), dim , type ); // SURF descriptors are CV_32F
2013-12-11 00:12:44 +00:00
std :: map < int , VisualWord *>:: const_iterator iter = _visualWords . begin ();
for ( unsigned int i = 0 ; i < _visualWords . size (); ++ i , ++ iter )
2011-06-05 14:45:39 +00:00
{
2014-06-22 03:33:56 +00:00
UASSERT ( iter -> second -> getDescriptor (). cols == dim );
UASSERT ( iter -> second -> getDescriptor (). type () == type );
iter -> second -> getDescriptor (). copyTo ( _dataTree . row ( i ));
_mapIndexId . insert ( _mapIndexId . end (), std :: pair < int , int > ( i , iter -> second -> id ()));
2011-06-05 14:45:39 +00:00
}
2013-12-11 00:12:44 +00:00
2014-06-22 03:33:56 +00:00
ULOGGER_DEBUG ( "_mapIndexId.size() = %d, words.size()=%d, _dim=%d" , _mapIndexId . size (), _visualWords . size (), dim );
2013-12-11 00:12:44 +00:00
ULOGGER_DEBUG ( "copying data = %f s" , timer . ticks ());
2014-06-22 03:33:56 +00:00
switch ( _strategy )
{
case kNNFlannNaive :
_flannIndex -> build ( _dataTree , cv :: flann :: LinearIndexParams (), type == CV_32F ? cvflann :: FLANN_DIST_L2 : cvflann :: FLANN_DIST_HAMMING );
break ;
case kNNFlannKdTree :
2014-09-09 21:54:32 +00:00
UASSERT_MSG ( type == CV_32F , "To use KdTree dictionary, float descriptors are required!" );
2014-06-22 03:33:56 +00:00
_flannIndex -> build ( _dataTree , cv :: flann :: KDTreeIndexParams (), cvflann :: FLANN_DIST_L2 );
break ;
case kNNFlannLSH :
2014-09-09 21:54:32 +00:00
UASSERT_MSG ( type == CV_8U , "To use LSH dictionary, binary descriptors are required!" );
2014-06-22 03:33:56 +00:00
_flannIndex -> build ( _dataTree , cv :: flann :: LshIndexParams ( 12 , 20 , 2 ), cvflann :: FLANN_DIST_HAMMING );
break ;
default :
break ;
}
2013-12-11 00:12:44 +00:00
ULOGGER_DEBUG ( "Time to create kd tree = %f s" , timer . ticks ());
2011-06-05 14:45:39 +00:00
}
2014-06-22 03:33:56 +00:00
UDEBUG ( "Dictionary updated! (size=%d->%d added=%d removed=%d)" ,
oldSize , _dataTree . rows , _notIndexedWords . size (), _removedIndexedWords . size ());
2011-06-05 14:45:39 +00:00
}
2013-12-11 00:12:44 +00:00
else
{
2014-06-22 03:33:56 +00:00
UDEBUG ( "Dictionary has not changed, so no need to update it! (size=%d)" , _dataTree . rows );
2013-12-11 00:12:44 +00:00
}
_notIndexedWords . clear ();
_removedIndexedWords . clear ();
2011-06-05 14:45:39 +00:00
}
void VWDictionary :: clear ()
{
2013-12-11 00:12:44 +00:00
ULOGGER_DEBUG ( "" );
2013-06-06 13:41:49 +00:00
if ( _visualWords . size () && _incrementalDictionary )
2012-06-24 17:19:34 +00:00
{
2013-12-11 00:12:44 +00:00
UWARN ( "Visual dictionary would be already empty here (%d words still in dictionary)." , ( int ) _visualWords . size ());
}
if ( _notIndexedWords . size ())
{
UWARN ( "Not indexed words should be empty here (%d words still not indexed)" , ( int ) _notIndexedWords . size ());
2012-06-24 17:19:34 +00:00
}
2011-06-05 14:45:39 +00:00
for ( std :: map < int , VisualWord *>:: iterator i = _visualWords . begin (); i != _visualWords . end (); ++ i )
{
delete ( * i ). second ;
}
_visualWords . clear ();
2013-12-11 00:12:44 +00:00
_notIndexedWords . clear ();
_removedIndexedWords . clear ();
2011-06-05 14:45:39 +00:00
_totalActiveReferences = 0 ;
_lastWordId = 0 ;
2012-06-24 17:19:34 +00:00
_dataTree = cv :: Mat ();
_mapIndexId . clear ();
_unusedWords . clear ();
2014-06-22 03:33:56 +00:00
_flannIndex -> release ();
2011-06-05 14:45:39 +00:00
}
int VWDictionary :: getNextId ()
{
return ++ _lastWordId ;
}
void VWDictionary :: addWordRef ( int wordId , int signatureId )
{
2012-12-12 16:40:18 +00:00
if ( signatureId > 0 && wordId > 0 )
2011-06-05 14:45:39 +00:00
{
2012-12-12 16:40:18 +00:00
VisualWord * vw = 0 ;
vw = uValue ( _visualWords , wordId , vw );
if ( vw )
{
vw -> addRef ( signatureId );
_totalActiveReferences += 1 ;
2013-12-11 00:12:44 +00:00
_unusedWords . erase ( vw -> id ());
2012-12-12 16:40:18 +00:00
}
else
{
UERROR ( "Not found word %d" , wordId );
}
2011-06-05 14:45:39 +00:00
}
}
void VWDictionary :: removeAllWordRef ( int wordId , int signatureId )
{
VisualWord * vw = 0 ;
vw = uValue ( _visualWords , wordId , vw );
if ( vw )
{
_totalActiveReferences -= vw -> removeAllRef ( signatureId );
if ( vw -> getReferences (). size () == 0 )
{
_unusedWords . insert ( std :: pair < int , VisualWord *> ( vw -> id (), vw ));
}
}
}
2012-03-03 01:46:30 +00:00
std :: list < int > VWDictionary :: addNewWords ( const cv :: Mat & descriptors ,
2011-06-05 14:45:39 +00:00
int signatureId )
{
2014-11-12 15:25:15 +00:00
UASSERT ( signatureId > 0 );
2014-10-06 19:41:45 +00:00
UDEBUG ( "id=%d descriptors=%d" , signatureId , descriptors . rows );
2011-06-05 14:45:39 +00:00
UTimer timer ;
std :: list < int > wordIds ;
2014-06-22 03:33:56 +00:00
if ( descriptors . rows == 0 || descriptors . cols == 0 )
2011-06-05 14:45:39 +00:00
{
2014-06-22 03:33:56 +00:00
UERROR ( "Descriptors size is null!" );
2011-06-05 14:45:39 +00:00
return wordIds ;
}
2014-06-22 03:33:56 +00:00
int dim = 0 ;
2014-11-12 15:25:15 +00:00
int type = - 1 ;
2014-06-22 03:33:56 +00:00
if ( _visualWords . size ())
2011-06-05 14:45:39 +00:00
{
2014-06-22 03:33:56 +00:00
dim = _visualWords . begin () -> second -> getDescriptor (). cols ;
type = _visualWords . begin () -> second -> getDescriptor (). type ();
2014-11-12 15:25:15 +00:00
UASSERT ( type == CV_32F || type == CV_8U );
2011-06-05 14:45:39 +00:00
}
2014-06-22 03:33:56 +00:00
if ( dim && dim != descriptors . cols )
{
UERROR ( "Descriptors (size=%d) are not the same size as already added words in dictionary(size=%d)" , descriptors . cols , dim );
return wordIds ;
}
dim = descriptors . cols ;
2014-11-12 15:25:15 +00:00
if ( type >= 0 && type != descriptors . type ())
2014-06-22 03:33:56 +00:00
{
UERROR ( "Descriptors (type=%d) are not the same type as already added words in dictionary(type=%d)" , descriptors . type (), type );
return wordIds ;
}
type = descriptors . type ();
if ( ! _incrementalDictionary && _visualWords . empty ())
2013-12-11 00:12:44 +00:00
{
UERROR ( "Dictionary mode is set to fixed but no words are in it!" );
return wordIds ;
}
2014-06-22 03:33:56 +00:00
int dupWordsCountFromDict = 0 ;
int dupWordsCountFromLast = 0 ;
2011-06-05 14:45:39 +00:00
2014-06-22 03:33:56 +00:00
unsigned int k = 2 ; // k nearest neighbors
cv :: Mat newWords ;
std :: vector < int > newWordsId ;
cv :: Mat results ;
cv :: Mat dists ;
std :: vector < std :: vector < cv :: DMatch > > matches ;
bool bruteForce = false ;
UTimer timerLocal ;
timerLocal . start ();
if ( ! _dataTree . empty () && _dataTree . rows >= ( int ) k )
2011-06-05 14:45:39 +00:00
{
2014-06-22 03:33:56 +00:00
//Find nearest neighbors
UDEBUG ( "newPts.total()=%d " , descriptors . rows );
2011-06-05 14:45:39 +00:00
2014-06-22 03:33:56 +00:00
if ( _strategy == kNNFlannNaive || _strategy == kNNFlannKdTree || _strategy == kNNFlannLSH )
2011-06-05 14:45:39 +00:00
{
2014-06-22 03:33:56 +00:00
_flannIndex -> knnSearch ( descriptors , results , dists , k );
}
else if ( _strategy == kNNBruteForce )
{
bruteForce = true ;
2014-12-04 11:47:25 -05:00
cv :: BFMatcher matcher ( type == CV_8U ? cv :: NORM_HAMMING : cv :: NORM_L2SQR );
2014-06-22 03:33:56 +00:00
matcher . knnMatch ( descriptors , _dataTree , matches , k );
}
else if ( _strategy == kNNBruteForceGPU )
{
bruteForce = true ;
2015-06-18 23:00:46 -04:00
#if CV_MAJOR_VERSION < 3
2014-06-22 03:33:56 +00:00
cv :: gpu :: GpuMat newDescriptorsGpu ( descriptors );
cv :: gpu :: GpuMat lastDescriptorsGpu ( _dataTree );
if ( type == CV_8U )
{
cv :: gpu :: BruteForceMatcher_GPU < cv :: Hamming > gpuMatcher ;
gpuMatcher . knnMatch ( newDescriptorsGpu , lastDescriptorsGpu , matches , k );
}
else
{
cv :: gpu :: BruteForceMatcher_GPU < cv :: L2 < float > > gpuMatcher ;
gpuMatcher . knnMatch ( newDescriptorsGpu , lastDescriptorsGpu , matches , k );
}
2015-06-18 23:00:46 -04:00
#else
#ifdef HAVE_OPENCV_CUDAFEATURES2D
cv :: cuda :: GpuMat newDescriptorsGpu ( descriptors );
cv :: cuda :: GpuMat lastDescriptorsGpu ( _dataTree );
2015-07-11 12:42:46 -04:00
cv :: Ptr < cv :: cuda :: DescriptorMatcher > gpuMatcher ;
2015-06-18 23:00:46 -04:00
if ( type == CV_8U )
{
2015-07-11 12:42:46 -04:00
gpuMatcher = cv :: cuda :: DescriptorMatcher :: createBFMatcher ( cv :: NORM_HAMMING );
gpuMatcher -> knnMatch ( newDescriptorsGpu , lastDescriptorsGpu , matches , k );
2015-06-18 23:00:46 -04:00
}
else
{
2015-07-11 12:42:46 -04:00
gpuMatcher = cv :: cuda :: DescriptorMatcher :: createBFMatcher ( cv :: NORM_L2 );
gpuMatcher -> knnMatch ( newDescriptorsGpu , lastDescriptorsGpu , matches , k );
2015-06-18 23:00:46 -04:00
}
#endif
#endif
2012-03-03 01:46:30 +00:00
}
else
{
2014-06-22 03:33:56 +00:00
UFATAL ( "" );
2011-06-05 14:45:39 +00:00
}
2014-06-22 03:33:56 +00:00
// In case of binary descriptors
if ( dists . type () == CV_32S )
{
cv :: Mat temp ;
dists . convertTo ( temp , CV_32F );
dists = temp ;
}
UDEBUG ( "Time to find nn = %f s" , timerLocal . ticks ());
}
// Process results
for ( int i = 0 ; i < descriptors . rows ; ++ i )
{
std :: multimap < float , int > fullResults ; // Contains results from the kd-tree search and the naive search in new words
if ( ! bruteForce && dists . cols )
{
for ( int j = 0 ; j < dists . cols ; ++ j )
{
if ( results . at < int > ( i , j ) >= 0 )
{
float d = dists . at < float > ( i , j );
fullResults . insert ( std :: pair < float , int > ( d , uValue ( _mapIndexId , results . at < int > ( i , j ))));
}
}
}
else if ( bruteForce && matches . size ())
{
for ( unsigned int j = 0 ; j < matches . at ( i ). size (); ++ j )
{
if ( matches . at ( i ). at ( j ). trainIdx >= 0 )
{
float d = matches . at ( i ). at ( j ). distance ;
fullResults . insert ( std :: pair < float , int > ( d , uValue ( _mapIndexId , matches . at ( i ). at ( j ). trainIdx )));
}
}
}
// Check if this descriptor matches with a word from the last signature (a word not already added to the tree)
2014-10-14 21:48:50 +00:00
if ( _newWordsComparedTogether && newWords . rows )
2014-06-22 03:33:56 +00:00
{
cv :: flann :: Index linearSeach ;
linearSeach . build ( newWords , cv :: flann :: LinearIndexParams (), type == CV_32F ? cvflann :: FLANN_DIST_L2 : cvflann :: FLANN_DIST_HAMMING );
cv :: Mat resultsLinear ;
cv :: Mat distsLinear ;
linearSeach . knnSearch ( descriptors . row ( i ), resultsLinear , distsLinear , newWords . rows > 1 ? 2 : 1 );
// In case of binary descriptors
if ( distsLinear . type () == CV_32S )
{
cv :: Mat temp ;
distsLinear . convertTo ( temp , CV_32F );
distsLinear = temp ;
}
if ( resultsLinear . cols )
{
for ( int j = 0 ; j < resultsLinear . cols ; ++ j )
{
if ( resultsLinear . at < int > ( 0 , j ) >= 0 )
{
float d = distsLinear . at < float > ( 0 , j );
fullResults . insert ( std :: pair < float , int > ( d , newWordsId [ resultsLinear . at < int > ( 0 , j )]));
}
}
}
}
if ( _incrementalDictionary )
{
bool badDist = false ;
if ( fullResults . size () == 0 )
{
badDist = true ;
}
if ( ! badDist )
{
if ( fullResults . size () >= 2 )
{
// Apply NNDR
if ( fullResults . begin () -> first > _nndrRatio * ( ++ fullResults . begin ()) -> first )
{
badDist = true ; // Rejected
}
}
else
{
badDist = true ; // Rejected
}
}
if ( badDist )
{
VisualWord * vw = new VisualWord ( getNextId (), descriptors . row ( i ), signatureId );
_visualWords . insert ( _visualWords . end (), std :: pair < int , VisualWord *> ( vw -> id (), vw ));
_notIndexedWords . insert ( _notIndexedWords . end (), vw -> id ());
newWords . push_back ( vw -> getDescriptor ());
newWordsId . push_back ( vw -> id ());
wordIds . push_back ( vw -> id ());
UASSERT ( vw -> id () > 0 );
}
else
{
if ( _notIndexedWords . find ( fullResults . begin () -> second ) != _notIndexedWords . end ())
{
++ dupWordsCountFromLast ;
}
else
{
++ dupWordsCountFromDict ;
}
this -> addWordRef ( fullResults . begin () -> second , signatureId );
wordIds . push_back ( fullResults . begin () -> second );
UASSERT ( fullResults . begin () -> second > 0 );
}
}
else if ( fullResults . size ())
{
// If the dictionary is not incremental, just take the nearest word
++ dupWordsCountFromDict ;
this -> addWordRef ( fullResults . begin () -> second , signatureId );
wordIds . push_back ( fullResults . begin () -> second );
UASSERT ( fullResults . begin () -> second > 0 );
}
}
ULOGGER_DEBUG ( "naive search and add ref/words time = %f s" , timerLocal . ticks ());
ULOGGER_DEBUG ( "%d new words added..." , _notIndexedWords . size ());
ULOGGER_DEBUG ( "%d duplicated words added (from current image = %d)..." ,
dupWordsCountFromDict + dupWordsCountFromLast , dupWordsCountFromLast );
UDEBUG ( "total time %fs" , timer . ticks ());
_totalActiveReferences += _notIndexedWords . size ();
return wordIds ;
}
std :: vector < int > VWDictionary :: findNN ( const std :: list < VisualWord *> & vws ) const
{
UTimer timer ;
timer . start ();
std :: vector < int > resultIds ( vws . size (), 0 );
unsigned int k = 2 ; // k nearest neighbor
if ( _visualWords . size () && vws . size ())
{
int dim = _visualWords . begin () -> second -> getDescriptor (). cols ;
int type = _visualWords . begin () -> second -> getDescriptor (). type ();
if ( dim != ( * vws . begin ()) -> getDescriptor (). cols )
{
UERROR ( "Descriptors (size=%d) are not the same size as already added words in dictionary(size=%d)" , ( * vws . begin ()) -> getDescriptor (). cols , dim );
return resultIds ;
}
if ( type != ( * vws . begin ()) -> getDescriptor (). type ())
{
UERROR ( "Descriptors (type=%d) are not the same type as already added words in dictionary(type=%d)" , ( * vws . begin ()) -> getDescriptor (). type (), type );
return resultIds ;
}
std :: vector < std :: vector < cv :: DMatch > > matches ;
bool bruteForce = false ;
cv :: Mat results ;
cv :: Mat dists ;
// fill the request matrix
int index = 0 ;
VisualWord * vw ;
cv :: Mat query ( vws . size (), dim , type );
for ( std :: list < VisualWord *>:: const_iterator iter = vws . begin (); iter != vws . end (); ++ iter , ++ index )
{
vw = * iter ;
UASSERT ( vw );
UASSERT ( vw -> getDescriptor (). cols == dim );
UASSERT ( vw -> getDescriptor (). type () == type );
vw -> getDescriptor (). copyTo ( query . row ( index ));
}
ULOGGER_DEBUG ( "Preparation time = %fs" , timer . ticks ());
2011-06-05 14:45:39 +00:00
2013-12-17 22:36:09 +00:00
if ( ! _dataTree . empty () && _dataTree . rows >= ( int ) k )
2011-06-05 14:45:39 +00:00
{
//Find nearest neighbors
2014-06-22 03:33:56 +00:00
UDEBUG ( "newPts.total()=%d " , query . total ());
2011-06-05 14:45:39 +00:00
2014-06-22 03:33:56 +00:00
if ( _strategy == kNNFlannNaive || _strategy == kNNFlannKdTree || _strategy == kNNFlannLSH )
2011-06-05 14:45:39 +00:00
{
2014-06-22 03:33:56 +00:00
_flannIndex -> knnSearch ( query , results , dists , k );
}
else if ( _strategy == kNNBruteForce )
{
bruteForce = true ;
2014-12-04 11:47:25 -05:00
cv :: BFMatcher matcher ( type == CV_8U ? cv :: NORM_HAMMING : cv :: NORM_L2SQR );
2014-06-22 03:33:56 +00:00
matcher . knnMatch ( query , _dataTree , matches , k );
}
else if ( _strategy == kNNBruteForceGPU )
{
bruteForce = true ;
2015-06-18 23:00:46 -04:00
#if CV_MAJOR_VERSION < 3
2014-06-22 03:33:56 +00:00
cv :: gpu :: GpuMat newDescriptorsGpu ( query );
cv :: gpu :: GpuMat lastDescriptorsGpu ( _dataTree );
if ( type == CV_8U )
2011-06-05 14:45:39 +00:00
{
2014-06-22 03:33:56 +00:00
cv :: gpu :: BruteForceMatcher_GPU < cv :: Hamming > gpuMatcher ;
gpuMatcher . knnMatch ( newDescriptorsGpu , lastDescriptorsGpu , matches , k );
}
else
{
cv :: gpu :: BruteForceMatcher_GPU < cv :: L2 < float > > gpuMatcher ;
gpuMatcher . knnMatch ( newDescriptorsGpu , lastDescriptorsGpu , matches , k );
}
2015-06-18 23:00:46 -04:00
#else
#ifdef HAVE_OPENCV_CUDAFEATURES2D
cv :: cuda :: GpuMat newDescriptorsGpu ( query );
cv :: cuda :: GpuMat lastDescriptorsGpu ( _dataTree );
cv :: Ptr < cv :: cuda :: DescriptorMatcher > gpuMatcher ;
if ( type == CV_8U )
{
gpuMatcher = cv :: cuda :: DescriptorMatcher :: createBFMatcher ( cv :: NORM_HAMMING );
2015-07-11 12:42:46 -04:00
gpuMatcher -> knnMatchAsync ( newDescriptorsGpu , lastDescriptorsGpu , matches , k );
2015-06-18 23:00:46 -04:00
}
else
{
gpuMatcher = cv :: cuda :: DescriptorMatcher :: createBFMatcher ( cv :: NORM_L2 );
2015-07-11 12:42:46 -04:00
gpuMatcher -> knnMatchAsync ( newDescriptorsGpu , lastDescriptorsGpu , matches , k );
2015-06-18 23:00:46 -04:00
}
#endif
#endif
2014-06-22 03:33:56 +00:00
}
else
{
UFATAL ( "" );
}
// In case of binary descriptors
if ( dists . type () == CV_32S )
{
cv :: Mat temp ;
dists . convertTo ( temp , CV_32F );
dists = temp ;
}
}
ULOGGER_DEBUG ( "Search dictionary time = %fs" , timer . ticks ());
cv :: Mat resultsNotIndexed ;
cv :: Mat distsNotIndexed ;
std :: map < int , int > mapIndexIdNotIndexed ;
if ( _notIndexedWords . size ())
{
cv :: Mat dataNotIndexed = cv :: Mat :: zeros ( _notIndexedWords . size (), dim , type );
unsigned int index = 0 ;
VisualWord * vw ;
for ( std :: set < int >:: iterator iter = _notIndexedWords . begin (); iter != _notIndexedWords . end (); ++ iter , ++ index )
{
vw = _visualWords . at ( * iter );
UASSERT ( vw != 0 && vw -> getDescriptor (). cols == dim && vw -> getDescriptor (). type () == type );
vw -> getDescriptor (). copyTo ( dataNotIndexed . row ( index ));
mapIndexIdNotIndexed . insert ( mapIndexIdNotIndexed . end (), std :: pair < int , int > ( index , vw -> id ()));
}
// Find nearest neighbor
ULOGGER_DEBUG ( "Searching in words not indexed..." );
cv :: flann :: Index linearSeach ;
linearSeach . build ( dataNotIndexed , cv :: flann :: LinearIndexParams (), type == CV_32F ? cvflann :: FLANN_DIST_L2 : cvflann :: FLANN_DIST_HAMMING );
linearSeach . knnSearch ( query , resultsNotIndexed , distsNotIndexed , _notIndexedWords . size () > 1 ? 2 : 1 );
// In case of binary descriptors
if ( distsNotIndexed . type () == CV_32S )
{
cv :: Mat temp ;
distsNotIndexed . convertTo ( temp , CV_32F );
distsNotIndexed = temp ;
}
}
ULOGGER_DEBUG ( "Search not yet indexed words time = %fs" , timer . ticks ());
for ( unsigned int i = 0 ; i < vws . size (); ++ i )
{
std :: multimap < float , int > fullResults ; // Contains results from the kd-tree search [and the naive search in new words]
if ( ! bruteForce && dists . cols )
{
for ( int j = 0 ; j < dists . cols ; ++ j )
{
if ( results . at < int > ( i , j ) > 0 )
{
float d = dists . at < float > ( i , j );
fullResults . insert ( std :: pair < float , int > ( d , uValue ( _mapIndexId , results . at < int > ( i , j ))));
}
}
}
else if ( bruteForce && matches . size ())
{
for ( unsigned int j = 0 ; j < matches . at ( i ). size (); ++ j )
{
if ( matches . at ( i ). at ( j ). trainIdx > 0 )
{
float d = matches . at ( i ). at ( j ). distance ;
fullResults . insert ( std :: pair < float , int > ( d , uValue ( _mapIndexId , matches . at ( i ). at ( j ). trainIdx )));
}
}
}
// not indexed..
for ( int j = 0 ; j < distsNotIndexed . cols ; ++ j )
{
if ( resultsNotIndexed . at < int > ( i , j ) > 0 )
{
float d = distsNotIndexed . at < float > ( i , j );
fullResults . insert ( std :: pair < float , int > ( d , uValue ( mapIndexIdNotIndexed , resultsNotIndexed . at < int > ( i , j ))));
2011-06-05 14:45:39 +00:00
}
}
if ( _incrementalDictionary )
{
bool badDist = false ;
if ( fullResults . size () == 0 )
{
badDist = true ;
}
2014-06-22 03:33:56 +00:00
if ( ! badDist )
2011-06-05 14:45:39 +00:00
{
if ( fullResults . size () >= 2 )
{
// Apply NNDR
if ( fullResults . begin () -> first > _nndrRatio * ( ++ fullResults . begin ()) -> first )
{
badDist = true ; // Rejected
}
}
else
{
badDist = true ; // Rejected
}
}
if ( ! badDist )
{
2014-06-22 03:33:56 +00:00
resultIds [ i ] = fullResults . begin () -> second ; // Accepted
2011-06-05 14:45:39 +00:00
}
}
2014-06-22 03:33:56 +00:00
else if ( fullResults . size ())
2011-06-05 14:45:39 +00:00
{
2014-06-22 03:33:56 +00:00
//Just take the nearest if the dictionary is not incremental
resultIds [ i ] = fullResults . begin () -> second ; // Accepted
2011-06-05 14:45:39 +00:00
}
}
2014-06-22 03:33:56 +00:00
ULOGGER_DEBUG ( "badDist check time = %fs" , timer . ticks ());
2011-06-05 14:45:39 +00:00
}
return resultIds ;
}
void VWDictionary :: addWord ( VisualWord * vw )
{
if ( vw )
{
2013-12-11 00:12:44 +00:00
_visualWords . insert ( std :: pair < int , VisualWord *> ( vw -> id (), vw ));
_notIndexedWords . insert ( vw -> id ());
2012-12-12 16:40:18 +00:00
if ( vw -> getReferences (). size ())
{
_totalActiveReferences += uSum ( uValues ( vw -> getReferences ()));
}
else
{
_unusedWords . insert ( std :: pair < int , VisualWord *> ( vw -> id (), vw ));
}
2011-06-05 14:45:39 +00:00
}
}
const VisualWord * VWDictionary :: getWord ( int id ) const
{
2012-03-03 01:46:30 +00:00
return uValue ( _visualWords , id , ( VisualWord * ) 0 );
2011-06-05 14:45:39 +00:00
}
2013-03-02 21:53:47 +00:00
VisualWord * VWDictionary :: getUnusedWord ( int id ) const
2011-06-05 14:45:39 +00:00
{
2012-03-03 01:46:30 +00:00
return uValue ( _unusedWords , id , ( VisualWord * ) 0 );
2011-06-05 14:45:39 +00:00
}
std :: vector < VisualWord *> VWDictionary :: getUnusedWords () const
{
if ( ! _incrementalDictionary )
{
ULOGGER_WARN ( "This method does nothing on a fixed dictionary" );
return std :: vector < VisualWord *> ();
}
return uValues ( _unusedWords );
}
2014-06-22 03:33:56 +00:00
std :: vector < int > VWDictionary :: getUnusedWordIds () const
{
if ( ! _incrementalDictionary )
{
ULOGGER_WARN ( "This method does nothing on a fixed dictionary" );
return std :: vector < int > ();
}
return uKeys ( _unusedWords );
}
2011-06-05 14:45:39 +00:00
void VWDictionary :: removeWords ( const std :: vector < VisualWord *> & words )
{
for ( unsigned int i = 0 ; i < words . size (); ++ i )
{
_visualWords . erase ( words [ i ] -> id ());
_unusedWords . erase ( words [ i ] -> id ());
2013-12-11 00:12:44 +00:00
if ( _notIndexedWords . erase ( words [ i ] -> id ()) == 0 )
{
_removedIndexedWords . insert ( words [ i ] -> id ());
}
2011-06-05 14:45:39 +00:00
}
}
2014-11-12 15:25:15 +00:00
void VWDictionary :: deleteUnusedWords ()
{
std :: vector < VisualWord *> unusedWords = uValues ( _unusedWords );
removeWords ( unusedWords );
for ( unsigned int i = 0 ; i < unusedWords . size (); ++ i )
{
delete unusedWords [ i ];
}
}
2011-06-05 14:45:39 +00:00
void VWDictionary :: exportDictionary ( const char * fileNameReferences , const char * fileNameDescriptors ) const
{
FILE * foutRef = 0 ;
FILE * foutDesc = 0 ;
#ifdef _MSC_VER
fopen_s ( & foutRef , fileNameReferences , "w" );
fopen_s ( & foutDesc , fileNameDescriptors , "w" );
#else
foutRef = fopen ( fileNameReferences , "w" );
foutDesc = fopen ( fileNameDescriptors , "w" );
#endif
if ( foutRef )
{
fprintf ( foutRef , "WordID SignaturesID... \n " );
}
if ( foutDesc )
{
if ( _visualWords . begin () == _visualWords . end ())
{
fprintf ( foutDesc , "WordID Descriptors... \n " );
}
else
{
2014-06-22 03:33:56 +00:00
fprintf ( foutDesc , "WordID Descriptors...%d \n " , ( * _visualWords . begin ()). second -> getDescriptor (). cols );
2011-06-05 14:45:39 +00:00
}
}
for ( std :: map < int , VisualWord *>:: const_iterator iter = _visualWords . begin (); iter != _visualWords . end (); ++ iter )
{
// References
if ( foutRef )
{
fprintf ( foutRef , "%d " , ( * iter ). first );
const std :: map < int , int > ref = ( * iter ). second -> getReferences ();
for ( std :: map < int , int >:: const_iterator jter = ref . begin (); jter != ref . end (); ++ jter )
{
for ( int i = 0 ; i < ( * jter ). second ; ++ i )
{
fprintf ( foutRef , "%d " , ( * jter ). first );
}
}
fprintf ( foutRef , " \n " );
}
//Descriptors
if ( foutDesc )
{
fprintf ( foutDesc , "%d " , ( * iter ). first );
2014-06-22 03:33:56 +00:00
const float * desc = ( const float * )( * iter ). second -> getDescriptor (). data ;
int dim = ( * iter ). second -> getDescriptor (). cols ;
2011-06-05 14:45:39 +00:00
for ( int i = 0 ; i < dim ; i ++ )
{
fprintf ( foutDesc , "%f " , desc [ i ]);
}
fprintf ( foutDesc , " \n " );
}
}
if ( foutRef )
fclose ( foutRef );
if ( foutDesc )
fclose ( foutDesc );
}
} // namespace rtabmap