/* * Copyright (C) 2010-2011, Mathieu Labbe and IntRoLab - Universite de Sherbrooke * * This file is part of RTAB-Map. * * RTAB-Map is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * RTAB-Map is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with RTAB-Map. If not, see . */ #include "KeypointMemory.h" #include "VWDictionary.h" #include "Signature.h" #include "rtabmap/core/DBDriver.h" #include "utilite/UtiLite.h" #include "rtabmap/core/Parameters.h" #include "rtabmap/core/SMState.h" #include "rtabmap/core/KeypointDetector.h" #include "rtabmap/core/KeypointDescriptor.h" #include "rtabmap/core/RtabmapEvent.h" #include "NearestNeighbor.h" #include "VerifyHypotheses.h" #include "utilite/UStl.h" #include #include namespace rtabmap { KeypointMemory::KeypointMemory(const ParametersMap & parameters) : Memory(parameters), _keypointDetector(0), _keypointDescriptor(0), _reactivatedWordsComparedToNewWords(Parameters::defaultKpReactivatedWordsComparedToNewWords()), _badSignRatio(Parameters::defaultKpBadSignRatio()), _tfIdfLikelihoodUsed(Parameters::defaultKpTfIdfLikelihoodUsed()), _parallelized(Parameters::defaultKpParallelized()), _sensorStateOnly(Parameters::defaultKpSensorStateOnly()), _tfIdfNormalized(Parameters::defaultKpTfIdfNormalized()) { _vwd = new VWDictionary(parameters); this->parseParameters(parameters); } KeypointMemory::~KeypointMemory() { ULOGGER_DEBUG(""); if(this->memoryChanged()) { this->clear(); } if(_keypointDetector) { delete _keypointDetector; } if(_keypointDescriptor) { delete _keypointDescriptor; } if(_vwd) { delete _vwd; } } void KeypointMemory::parseParameters(const ParametersMap & parameters) { ParametersMap::const_iterator iter; if(_vwd) { _vwd->parseParameters(parameters); } if((iter=parameters.find(Parameters::kKpReactivatedWordsComparedToNewWords())) != parameters.end()) { _reactivatedWordsComparedToNewWords = uStr2Bool((*iter).second.c_str()); } if((iter=parameters.find(Parameters::kKpTfIdfLikelihoodUsed())) != parameters.end()) { _tfIdfLikelihoodUsed = uStr2Bool((*iter).second.c_str()); } if((iter=parameters.find(Parameters::kKpParallelized())) != parameters.end()) { _parallelized = uStr2Bool((*iter).second.c_str()); } if((iter=parameters.find(Parameters::kKpSensorStateOnly())) != parameters.end()) { _sensorStateOnly = uStr2Bool((*iter).second.c_str()); } if((iter=parameters.find(Parameters::kKpTfIdfNormalized())) != parameters.end()) { _tfIdfNormalized = uStr2Bool((*iter).second.c_str()); } if((iter=parameters.find(Parameters::kKpBadSignRatio())) != parameters.end()) { _badSignRatio = std::atof((*iter).second.c_str()); } //Keypoint detector DetectorStrategy detectorStrategy = kDetectorUndef; if((iter=parameters.find(Parameters::kKpDetectorStrategy())) != parameters.end()) { detectorStrategy = (DetectorStrategy)std::atoi((*iter).second.c_str()); } DetectorStrategy currentDetectorStrategy = this->detectorStrategy(); if(!_keypointDetector || ( detectorStrategy!=kDetectorUndef && (detectorStrategy != currentDetectorStrategy) ) ) { ULOGGER_DEBUG("new detector strategy %d", int(detectorStrategy)); if(_keypointDetector) { delete _keypointDetector; _keypointDetector = 0; } switch(detectorStrategy) { case kDetectorStar: _keypointDetector = new StarDetector(parameters); break; case kDetectorSift: _keypointDetector = new SIFTDetector(parameters); break; case kDetectorSurf: default: _keypointDetector = new SURFDetector(parameters); break; } } else if(_keypointDetector) { _keypointDetector->parseParameters(parameters); } //Keypoint descriptor DescriptorStrategy descriptorStrategy = kDescriptorUndef; if((iter=parameters.find(Parameters::kKpDescriptorStrategy())) != parameters.end()) { descriptorStrategy = (DescriptorStrategy)std::atoi((*iter).second.c_str()); } if(!_keypointDescriptor || descriptorStrategy!=kDescriptorUndef) { ULOGGER_DEBUG("new descriptor strategy %d", int(descriptorStrategy)); if(_keypointDescriptor) { delete _keypointDescriptor; _keypointDescriptor = 0; } switch(descriptorStrategy) { case kDescriptorColorSurf: // see decorator pattern... _keypointDescriptor = new ColorDescriptor(parameters, new SURFDescriptor(parameters)); break; case kDescriptorLaplacianSurf: // see decorator pattern... _keypointDescriptor = new LaplacianDescriptor(parameters, new SURFDescriptor(parameters)); break; case kDescriptorSift: _keypointDescriptor = new SIFTDescriptor(parameters); break; case kDescriptorHueSurf: // see decorator pattern... _keypointDescriptor = new HueDescriptor(parameters, new SURFDescriptor(parameters)); break; case kDescriptorSurf: default: _keypointDescriptor = new SURFDescriptor(parameters); break; } } else if(_keypointDescriptor) { _keypointDescriptor->parseParameters(parameters); } Memory::parseParameters(parameters); } KeypointMemory::DetectorStrategy KeypointMemory::detectorStrategy() const { DetectorStrategy strategy = kDetectorUndef; StarDetector * star = dynamic_cast(_keypointDetector); SURFDetector * surf = dynamic_cast(_keypointDetector); if(star) { strategy = kDetectorStar; } else if(surf) { strategy = kDetectorSurf; } return strategy; } bool KeypointMemory::init(const std::string & dbDriverName, const std::string & dbUrl, bool dbOverwritten, const ParametersMap & parameters) { ULOGGER_DEBUG("KeypointMemory::init()"); // This will open a connection to the database, // this calls also clear() bool success = Memory::init(dbDriverName, dbUrl, dbOverwritten, parameters); // Now load the dictionary if we have a connection if(_vwd && _dbDriver && _dbDriver->isConnected()) { UEventsManager::post(new RtabmapEventInit(std::string("Loading dictionary..."))); _dbDriver->load(_vwd); ULOGGER_DEBUG("%d words loaded!", _vwd->getVisualWords().size()); UEventsManager::post(new RtabmapEventInit(std::string("Loading dictionary, done! (") + uNumber2str(int(_vwd->getVisualWords().size())) + " loaded)")); } // Enable loaded signatures KeypointSignature * ss; const std::map & signatures = this->getSignatures(); for(std::map::const_iterator i=signatures.begin(); i!=signatures.end(); ++i) { ss = dynamic_cast(this->_getSignature(i->first)); if(ss) { ss->setEnabled(true); } } if(_vwd) { ULOGGER_DEBUG("Total word reference added = %d", _vwd->getTotalActiveReferences()); } //if(this->isCommonSignatureUsed()) //{ // Memory::updateCommonSignature(); // With words loaded, update the virtual place //} return success; } // TODO : Use only the parent method (in Memory) // 1- Put "setEnabled" in the abstract Signature class, so this method is accessible from Memory void KeypointMemory::addSignatureToStm(Signature * signature, const std::list > & actions) { ULOGGER_DEBUG(""); Memory::addSignatureToStm(signature, actions); UTimer timer; KeypointSignature * ss = dynamic_cast(signature); if(ss) { if(_vwd) { ULOGGER_DEBUG("%d words ref for the signature %d", ss->getWords().size(), ss->id()); } if(ss->getWords().size()) { ss->setEnabled(true); } } UDEBUG("time = %fs", timer.ticks()); } void KeypointMemory::clear() { ULOGGER_DEBUG(""); // Save some SURF stats to the db (Must be before Memory::clear()) if(_dbDriver) { int size = 0; if(_vwd) { size = _vwd->getVisualWords().size(); } _dbDriver->addStatisticsAfterRunSurf(size); } Memory::clear(); ULOGGER_DEBUG(""); if(_dbDriver) { _dbDriver->kill(); cleanUnusedWords(); _dbDriver->emptyTrashes(); //if(_wordRefsToChange.size()) //{ // ULOGGER_DEBUG("Changing old word references (%d) in the database...", _wordRefsToChange.size()); // _dbDriver->beginTransaction(); // Change all words reference for // all signatures with the old word to the active one... //_dbDriver->changeWordsRef(_wordRefsToChange); //remove old values _dbDriver->deleteUnreferencedWords(); // _dbDriver->commit(); //} ULOGGER_DEBUG(""); _dbDriver->start(); } else { cleanUnusedWords(); } _commonWords.clear(); } void KeypointMemory::preUpdate() { Memory::preUpdate(); this->cleanUnusedWords(); if(_vwd && !_parallelized) { //When parallelized, it is done in CreateSignature _vwd->update(); } } // TODO Really useful? /*void KeypointMemory::postUpdate() { ULOGGER_DEBUG(""); // Detect if the last signature is a bad one. If the signature has less than 15% of // the average words/signature. KeypointSignature * ss = dynamic_cast(this->_getLastSignature()); float ratio = 0; if(ss) { ratio = float(uUniqueKeys(ss->getWords()).size()) / float(ss->getWords().size()); } int nbCommonWords = 0; ULOGGER_DEBUG("_workingMem.size() = %d, _stMem.size()=%d", _workingMem.size(), _stMem.size()); int treeSize= _workingMem.size() + _stMem.size();//Don't count the virtual place if(treeSize > 0) { nbCommonWords = _vwd->getTotalActiveReferences() / treeSize; } ULOGGER_DEBUG("ratio=%f, treeSize=%d, nbCommonWords=%d", ratio, treeSize, nbCommonWords); if(//(ratio < _badSignRatio) || (nbCommonWords && ss && ss->getWords().size() < _badSignRatio * nbCommonWords)) { ULOGGER_WARN("id %d is a bad signature", ss->id()); this->disableWordsRef(ss->id()); ss->removeAllWords(); } }*/ // NON class method! only used in merge() std::multimap getMostDescriptiveWords(const std::multimap & words, int max, const std::set & ignoredIds) { std::multimap mostDescriptiveWords; if(max > 0) { std::multimap > responseWordMap; //get all descriptors from features not found in the two signatures for(std::multimap::const_iterator itKey = words.begin(); itKey != words.end(); ++itKey) { if(ignoredIds.find(itKey->first) == ignoredIds.end()) { responseWordMap.insert(std::pair >(itKey->second.response, std::pair(itKey->first, &(itKey->second)))); } } int endIndex = 0; if(responseWordMap.size() > (unsigned int)max) { endIndex = responseWordMap.size() - max; } //add them int i=0; std::multimap >::reverse_iterator iter = responseWordMap.rbegin(); for(; iter != responseWordMap.rend(); ++iter, ++i) { if(i>=max) { break; } mostDescriptiveWords.insert(std::pair(iter->second.first, *(iter->second.second))); } } return mostDescriptiveWords; } void KeypointMemory::merge(const Signature * from, Signature * to, MergingStrategy s) { // The signatures must be KeypointSignature const KeypointSignature * sFrom = dynamic_cast(from); KeypointSignature * sTo = dynamic_cast(to); UTimer timer; timer.start(); if(sFrom && sTo) { if(s == kUseOnlyFromMerging) { this->disableWordsRef(sTo->id()); sTo->setWords(sFrom->getWords()); std::list id; id.push_back(sTo->id()); this->enableWordsRef(id); // Set old image to new merged signature sTo->setImage(sFrom->getImage()); } else if(s == kUseOnlyDestMerging) { // do nothing... already "merged" } } else { ULOGGER_ERROR("Can't merge the signatures because there are not same type."); } ULOGGER_DEBUG("Merging time = %fs", timer.ticks()); } std::map KeypointMemory::computeLikelihood(const Signature * signature, const std::set & signatureIds) const { //return Memory::computeLikelihood(signature, signatureIds); // TODO cleanup , old way... if(_tfIdfLikelihoodUsed) { UTimer timer; timer.start(); std::map likelihood; std::map calculatedWordsRatio; const KeypointSignature * newSurf = dynamic_cast(signature); if(!newSurf) { ULOGGER_ERROR("The signature is not a KeypointSignature"); return likelihood; // Must be a KeypointSignature * } if(signatureIds.size() == 0) { const std::map & wm = this->getWorkingMem(); for(std::map::const_iterator iter = wm.begin(); iter!=wm.end(); ++iter) { likelihood.insert(likelihood.end(), std::pair(iter->first, 0)); if(_tfIdfNormalized) { const KeypointSignature * s = dynamic_cast(this->getSignature(iter->first)); float wordsCountRatio = -1; // default invalid if(s) { if(s->getWords().size() > newSurf->getWords().size()) { wordsCountRatio = float(newSurf->getWords().size()) / float(s->getWords().size()); } else if(newSurf->getWords().size()) { wordsCountRatio = float(s->getWords().size()) / float(newSurf->getWords().size()); } calculatedWordsRatio.insert(std::pair(iter->first, wordsCountRatio)); } else { calculatedWordsRatio.insert(std::pair(iter->first, wordsCountRatio)); } } } } else { for(std::set::const_iterator i=signatureIds.begin(); i != signatureIds.end(); ++i) { likelihood.insert(likelihood.end(), std::pair(*i, 0)); if(_tfIdfNormalized) { const KeypointSignature * s = dynamic_cast(this->getSignature(*i)); float wordsCountRatio = -1; // default invalid if(s) { if(s->getWords().size() > newSurf->getWords().size()) { wordsCountRatio = float(newSurf->getWords().size()) / float(s->getWords().size()); } else if(newSurf->getWords().size()) { wordsCountRatio = float(s->getWords().size()) / float(newSurf->getWords().size()); } calculatedWordsRatio.insert(std::pair(*i, wordsCountRatio)); } else { calculatedWordsRatio.insert(std::pair(*i, wordsCountRatio)); } } } } const std::list & wordIds = uUniqueKeys(newSurf->getWords()); float nwi; // nwi is the number of a specific word referenced by a place float ni; // ni is the total of words referenced by a place float nw; // nw is the number of places referenced by a specific word float N; // N is the total number of places float logNnw; const VisualWord * vw; float normalizationRatio; N = likelihood.size(); if(N) { ULOGGER_DEBUG("processing... "); // Pour chaque mot dans la signature SURF for(std::list::const_iterator i=wordIds.begin(); i!=wordIds.end(); ++i) { // "Inverted index" - Pour chaque endroit contenu dans chaque mot vw = _vwd->getWord(*i); if(vw) { const std::map & refs = vw->getReferences(); nw = refs.size(); if(nw) { logNnw = log10(N/nw); if(logNnw) { for(std::map::const_iterator j=refs.begin(); j!=refs.end(); ++j) { std::map::iterator iter = likelihood.find(j->first); if(iter != likelihood.end()) { nwi = j->second; ni = this->getNi(j->first); if(ni != 0) { //ULOGGER_DEBUG("%d, %f %f %f %f", vw->id(), logNnw, nwi, ni, ( nwi * logNnw ) / ni); if(_tfIdfNormalized) { normalizationRatio = uValue(calculatedWordsRatio, iter->first, -1.0f); if(normalizationRatio >= 0) { iter->second += (( nwi * logNnw ) / ni) * normalizationRatio; UWARN("for id=%d, normalizationRatio=%f", iter->first, normalizationRatio); } else { UWARN("not found calculatedWordsRatio for id=%d", iter->first); iter->second += ( nwi * logNnw ) / ni; } } else { iter->second += ( nwi * logNnw ) / ni; } } } } } } } } } ULOGGER_DEBUG("compute likelihood... %f s", timer.ticks()); return likelihood; } else { return Memory::computeLikelihood(signature, signatureIds); } } int KeypointMemory::getNi(int signatureId) const { int ni = 0; const Signature * s = this->getSignature(signatureId); if(s) // Must be a SurfSignature { ni = ((KeypointSignature *)s)->getWords().size(); } else { _dbDriver->getSurfNi(signatureId, ni); } return ni; } class PreUpdateThread : public UThreadNode { public: PreUpdateThread(VWDictionary * vwp) : _vwp(vwp) {} ~PreUpdateThread() {} private: void mainLoop() { if(_vwp) { _vwp->update(); } this->kill(); } VWDictionary * _vwp; }; Signature * KeypointMemory::createSignature(int id, const SMState * smState, bool keepRawData) { PreUpdateThread preUpdateThread(_vwd); UTimer timer; timer.start(); std::list keypoints; std::list > descriptors; const IplImage * image = 0; if(smState) { int treeSize= this->getWorkingMemSize() + this->getStMemSize(); int nbCommonWords = 0; if(treeSize > 0) { nbCommonWords = _vwd->getTotalActiveReferences() / treeSize; } if(_parallelized) { preUpdateThread.start(); } if(smState->getSensors().empty()) { image = smState->getImage(); if(image && _keypointDetector) { keypoints = _keypointDetector->generateKeypoints(image); ULOGGER_DEBUG("time keypoints = %fs", timer.ticks()); } ULOGGER_DEBUG("ratio=%f, treeSize=%d, nbCommonWords=%d", _badSignRatio, treeSize, nbCommonWords); if(keypoints.size() && keypoints.size() >= _badSignRatio * nbCommonWords) { descriptors = _keypointDescriptor->generateDescriptors(image, keypoints); } } else { if(smState->getSensors().size() >= _badSignRatio * nbCommonWords) { descriptors = smState->getSensors(); keypoints = smState->getKeypoints(); } image = smState->getImage(); } } if(_parallelized) { preUpdateThread.join(); // Wait the dictionary to be updated } std::list wordIds; if(descriptors.size()) { unsigned int descriptorSize = descriptors.begin()->size(); if(_parallelized) { ULOGGER_DEBUG("time descriptor and memory update (%d of size=%d) = %fs", (int)descriptors.size(), (int)descriptorSize, timer.ticks()); } else { ULOGGER_DEBUG("time descriptor (%d of size=%d) = %fs", (int)descriptors.size(), (int)descriptorSize, timer.ticks()); } //append actuators if(!_sensorStateOnly && smState->getActuators().size()) { const std::list > & actuators = smState->getActuators(); unsigned int actuatorSize = actuators.begin()->size(); if(actuatorSize > descriptorSize) { UERROR("Actuator's size (%d) is larger than descriptor size (%d)", actuatorSize, descriptorSize); } for(std::list >::const_iterator iter = actuators.begin(); iter!=actuators.end(); ++iter) { std::vector descriptor(descriptorSize); // normalize actuator values std::vector actuatorNormalized = uNormalize(*iter); for(unsigned int i=0; iaddNewWords(descriptors, descriptorSize, id); ULOGGER_DEBUG("time addNewWords %fs", timer.ticks()); } else { ULOGGER_WARN("id %d is a bad signature", id); } std::multimap words; if(wordIds.size() > 0) { std::list::iterator kpIter = keypoints.begin(); for(std::list::iterator iter=wordIds.begin(); iter!=wordIds.end(); ++iter) { if(kpIter != keypoints.end()) { words.insert(std::pair(*iter, *kpIter)); ++kpIter; } else { words.insert(std::pair(*iter, cv::KeyPoint())); } } } KeypointSignature * ks = new KeypointSignature(words, id, image, keepRawData); ULOGGER_DEBUG("time new signature (id=%d) %fs", id, timer.ticks()); if(words.size()) { ks->setEnabled(true); // All references are already activated in the dictionary at this point (see _vwd->addNewWords()) } return ks; } Signature * KeypointMemory::getSignatureLtMem(int id) { Signature * s = Memory::getSignatureLtMem(id); if(s) { std::list lid; lid.push_back(id); this->enableWordsRef(lid); } return s; } void KeypointMemory::disableWordsRef(int signatureId) { ULOGGER_DEBUG("id=%d", signatureId); KeypointSignature * ss = dynamic_cast(this->_getSignature(signatureId)); if(ss && ss->isEnabled()) { const std::multimap & words = ss->getWords(); const std::list & keys = uUniqueKeys(words); int count = _vwd->getTotalActiveReferences(); // First remove all references for(std::list::const_iterator i=keys.begin(); i!=keys.end(); ++i) { _vwd->removeAllWordRef(*i, signatureId); } count -= _vwd->getTotalActiveReferences(); ss->setEnabled(false); ULOGGER_DEBUG("%d words total ref removed from signature %d...", count, ss->id()); } } void KeypointMemory::cleanUnusedWords() { ULOGGER_DEBUG(""); if(_vwd->isIncremental()) { std::vector removedWords = _vwd->getUnusedWords(); if(removedWords.size()) { // remove them from the dictionary _vwd->removeWords(removedWords); for(unsigned int i=0; iasyncSave(removedWords[i]); } else { delete removedWords[i]; } } } ULOGGER_DEBUG("%d words removed...", removedWords.size()); } } void KeypointMemory::enableWordsRef(const std::list & signatureIds) { ULOGGER_DEBUG("size=%d", signatureIds.size()); UTimer timer; timer.start(); std::map refsToChange; // std::set oldWordIds; std::list surfSigns; for(std::list::const_iterator i=signatureIds.begin(); i!=signatureIds.end(); ++i) { KeypointSignature * ss = dynamic_cast(this->_getSignature(*i)); if(ss && !ss->isEnabled()) { surfSigns.push_back(ss); std::list uniqueKeys = uUniqueKeys(ss->getWords()); //Find words in the signature which they are not in the current dictionary for(std::list::const_iterator k=uniqueKeys.begin(); k!=uniqueKeys.end(); ++k) { if(_vwd->getWord(*k) == 0) { //std::map::iterator iter = _wordRefsToChange.find(*k); //if(iter != _wordRefsToChange.end()) //{ // ss->changeWordsRef(iter->first, iter->second); // uniqueKeys.push_back(iter->second); //} //else if(oldWordIds.find(*k) == oldWordIds.end()) { oldWordIds.insert(oldWordIds.end(), *k); } else { //UDEBUG("*k=%d", *k); } } } } } ULOGGER_DEBUG("oldWordIds.size()=%d, getOldIds time=%fs", oldWordIds.size(), timer.ticks()); // the words were deleted, so try to math it with an active word std::list vws; if(oldWordIds.size() && _dbDriver) { _dbDriver->loadWords(std::list(oldWordIds.begin(), oldWordIds.end()), vws); // get the descriptors } ULOGGER_DEBUG("loading words(%d) time=%fs", oldWordIds.size(), timer.ticks()); if(vws.size()) { //Search in the dictionary std::vector vwActiveIds = _vwd->findNN(vws, _reactivatedWordsComparedToNewWords); ULOGGER_DEBUG("find active ids (number=%d) time=%fs", vws.size(), timer.ticks()); int i=0; for(std::list::iterator iterVws=vws.begin(); iterVws!=vws.end(); ++iterVws) { if(vwActiveIds[i] > 0) { //ULOGGER_DEBUG("Match found %d with %d", (*iterVws)->id(), vwActiveIds[i]); refsToChange.insert(refsToChange.end(), std::pair((*iterVws)->id(), vwActiveIds[i])); if((*iterVws)->isSaved() || !_dbDriver) { delete (*iterVws); } else { _dbDriver->asyncSave(*iterVws); } } else { //add to dictionary _vwd->addWord(*iterVws); } ++i; } ULOGGER_DEBUG("Added %d to dictionary, time=%fs", vws.size()-refsToChange.size(), timer.ticks()); //update the global references map and update the signatures reactivated for(std::map::const_iterator iter=refsToChange.begin(); iter != refsToChange.end(); ++iter) { //uInsert(_wordRefsToChange, (const std::pair)*iter); // This will be used to change references in the database for(std::list::iterator j=surfSigns.begin(); j!=surfSigns.end(); ++j) { (*j)->changeWordsRef(iter->first, iter->second); } } ULOGGER_DEBUG("changing ref, total=%d, time=%fs", refsToChange.size(), timer.ticks()); } int count = _vwd->getTotalActiveReferences(); // Reactivate references and signatures for(std::list::iterator j=surfSigns.begin(); j!=surfSigns.end(); ++j) { const std::list & keys = uKeys((*j)->getWords()); // Add all references for(std::list::const_iterator i=keys.begin(); i!=keys.end(); ++i) { _vwd->addWordRef(*i, (*j)->id()); } if(keys.size()) { (*j)->setEnabled(true); } } count = _vwd->getTotalActiveReferences() - count; ULOGGER_DEBUG("%d words total ref added from %d signatures, time=%fs...", count, surfSigns.size(), timer.ticks()); } int KeypointMemory::forget(const std::list & ignoredIds) { ULOGGER_DEBUG(""); int signaturesRemoved = 0; if(_vwd->isIncremental()) { int newWords = 0; int wordsRemoved = 0; // Get how many new words added for the last run... newWords = _vwd->getNotIndexedWordsCount(); // So we need to remove at least "newWords" words from the // dictionary to respect the limit. while(wordsRemoved < newWords) { KeypointSignature * s = dynamic_cast(this->getRemovableSignature(ignoredIds)); if(s) { ++signaturesRemoved; this->moveToTrash(s); wordsRemoved = _vwd->getUnusedWordsSize(); } else { break; } } ULOGGER_DEBUG("newWords=%d, wordsRemoved=%d", newWords, wordsRemoved); } else { signaturesRemoved = Memory::forget(ignoredIds) ; } ULOGGER_DEBUG("signatures removed = %d", signaturesRemoved); return signaturesRemoved; } int KeypointMemory::reactivateSignatures(const std::list & ids, unsigned int maxLoaded, unsigned int maxTouched) { // get the signatures, if not in the working memory, they // will be loaded from the database in an more efficient way // than how it is done in the Memory ULOGGER_DEBUG(""); UTimer timer; std::list idsToLoad; unsigned int touched = 0; std::map::iterator wmIter; for(std::list::const_iterator i=ids.begin(); i!=ids.end(); ++i) { if(!this->getSignature(*i) && !uContains(idsToLoad, *i)) { if(!maxLoaded || idsToLoad.size() < maxLoaded) { //When loaded from the long-term memory, the signature // is automatically added on top of the working memory idsToLoad.push_back(*i); } } else if(touched < maxTouched) { this->touch(*i); } ++touched; } ULOGGER_DEBUG("idsToLoad = %d", idsToLoad.size()); std::list reactivatedSigns; if(_dbDriver) { _dbDriver->loadKeypointSignatures(idsToLoad, reactivatedSigns, true); } std::list idsLoaded; for(std::list::iterator i=reactivatedSigns.begin(); i!=reactivatedSigns.end(); ++i) { idsLoaded.push_back((*i)->id()); //append to working memory this->addSignatureToWm(*i); } this->enableWordsRef(idsLoaded); ULOGGER_DEBUG("time = %fs", timer.ticks()); return reactivatedSigns.size(); } void KeypointMemory::moveToTrash(Signature * s) { if(s) { this->disableWordsRef(s->id()); } Memory::moveToTrash(s); } void KeypointMemory::dumpMemory(std::string directory) const { this->dumpDictionary((directory+"DumpMemoryWordRef.txt").c_str(), (directory+"DumpMemoryWordDesc.txt").c_str()); Memory::dumpMemory(directory); } void KeypointMemory::dumpDictionary(const char * fileNameRef, const char * fileNameDesc) const { if(_vwd) { _vwd->exportDictionary(fileNameRef, fileNameDesc); } } void KeypointMemory::dumpSignatures(const char * fileNameSign) const { FILE* foutSign = 0; #ifdef _MSC_VER fopen_s(&foutSign, fileNameSign, "w"); #else foutSign = fopen(fileNameSign, "w"); #endif if(foutSign) { fprintf(foutSign, "SignatureID WordsID...\n"); const std::map & signatures = this->getSignatures(); for(std::map::const_iterator iter=signatures.begin(); iter!=signatures.end(); ++iter) { fprintf(foutSign, "%d ", iter->first); const KeypointSignature * ss = dynamic_cast(iter->second); if(ss) { const std::multimap & ref = ss->getWords(); for(std::multimap::const_iterator jter=ref.begin(); jter!=ref.end(); ++jter) { fprintf(foutSign, "%d ", (*jter).first); } } fprintf(foutSign, "\n"); } fclose(foutSign); } } } // namespace rtabmap