38 #include <opencv2/opencv_modules.hpp>
40 #if CV_MAJOR_VERSION < 3
41 #ifdef HAVE_OPENCV_GPU
42 #include <opencv2/gpu/gpu.hpp>
45 #include <opencv2/core/cuda.hpp>
46 #ifdef HAVE_OPENCV_CUDAFEATURES2D
47 #include <opencv2/cudafeatures2d.hpp>
64 _totalActiveReferences(0),
65 _incrementalDictionary(
Parameters::defaultKpIncrementalDictionary()),
66 _incrementalFlann(
Parameters::defaultKpIncrementalFlann()),
67 _rebalancingFactor(
Parameters::defaultKpFlannRebalancingFactor()),
68 _byteToFloat(
Parameters::defaultKpByteToFloat()),
70 _newDictionaryPath(
Parameters::defaultKpDictionaryPath()),
71 _newWordsComparedTogether(
Parameters::defaultKpNewWordsComparedTogether()),
73 useDistanceL1_(
false),
75 _strategy(kNNBruteForce)
89 ParametersMap::const_iterator
iter;
100 if((
iter=parameters.find(Parameters::kKpDictionaryPath())) != parameters.end())
104 if((
iter=parameters.find(Parameters::kKpIncrementalDictionary())) != parameters.end())
106 incrementalDictionary =
uStr2Bool((*iter).second.c_str());
110 bool treeUpdated =
false;
111 if((
iter=parameters.find(Parameters::kKpNNStrategy())) != parameters.end())
118 UINFO(
"KDTree: Binary to Float conversion approach has changed, re-initialize kd-tree.");
125 if(incrementalDictionary)
139 UWARN(
"Incremental dictionary set: already loaded visual words (%d) from the fixed dictionary will be included in the incremental one.",
_visualWords.size());
149 if(!dictionaryPath.empty())
154 UINFO(
"incremental=%d, oldPath=%s newPath=%s, visual words=%d",
159 UWARN(
"Loading fixed vocabulary \"%s\", this may take a while...", dictionaryPath.c_str());
163 driver->
load(
this,
false);
166 iter->second->setSaved(
true);
173 UERROR(
"Could not load dictionary from database %s", dictionaryPath.c_str());
179 UWARN(
"Loading fixed vocabulary \"%s\", this may take a while...", dictionaryPath.c_str());
181 file.open(dictionaryPath.c_str(), std::ifstream::in);
184 UDEBUG(
"Deleting old dictionary and loading the new one from \"%s\"", dictionaryPath.c_str());
189 std::list<std::string> strList;
193 for(std::list<std::string>::iterator
iter = strList.begin();
iter != strList.end(); ++
iter)
197 dimension = std::atoi(
iter->c_str());
201 UDEBUG(
"descriptor dimension = %d", dimension);
203 if(dimension <= 0 || dimension > 1000)
205 UERROR(
"Invalid dictionary file, visual word dimension (%d) is not valid, \"%s\"", dimension, dictionaryPath.c_str());
214 if((
int)strList.size() == dimension+1)
217 std::list<std::string>::iterator
iter = strList.begin();
218 int id = std::atoi(
iter->c_str());
224 for(;
i<dimension &&
iter != strList.end(); ++
i, ++
iter)
230 UERROR(
"Loaded word has not the same size (%d) than descriptor size previously detected (%d).",
i, dimension);
239 else if(!
str.empty())
241 UWARN(
"Cannot parse line \"%s\"",
str.c_str());
252 UERROR(
"Cannot open dictionary file \"%s\"", dictionaryPath.c_str());
260 UWARN(
"No words loaded, cannot set a fixed dictionary.", (
int)
_visualWords.size());
273 UDEBUG(
"Dictionary \"%s\" already loaded...", dictionaryPath.c_str());
277 UERROR(
"Cannot change to a fixed dictionary if there are already words (%d) in the incremental one.",
_visualWords.size());
282 UWARN(
"Cannot change to fixed dictionary, %d words already loaded as incremental", (
int)
_visualWords.size());
294 #if CV_MAJOR_VERSION < 3
295 #ifdef HAVE_OPENCV_GPU
298 UERROR(
"Nearest neighobr strategy \"kNNBruteForceGPU\" chosen but no CUDA devices found! Doing \"kNNBruteForce\" instead.");
304 UERROR(
"Nearest neighobr strategy \"kNNBruteForceGPU\" chosen but OpenCV is not built with GPU/cuda module! Doing \"kNNBruteForce\" instead.");
309 #ifdef HAVE_OPENCV_CUDAFEATURES2D
312 UERROR(
"Nearest neighobr strategy \"kNNBruteForceGPU\" chosen but no CUDA devices found! Doing \"kNNBruteForce\" instead.");
318 UERROR(
"Nearest neighobr strategy \"kNNBruteForceGPU\" chosen but OpenCV cudafeatures2d module is not found! Doing \"kNNBruteForce\" instead.");
326 UERROR(
"Nearest neighobr strategy \"%d\" chosen but this strategy cannot be used with a dictionary! Doing \"kNNBruteForce\" instead.");
336 UINFO(
"Nearest neighbor strategy has changed, re-initialize search tree.");
376 memoryUsage +=
_visualWords.size()*(
sizeof(
int) +
_visualWords.rbegin()->second->getMemoryUsed() +
sizeof(std::map<int, VisualWord *>::iterator)) +
sizeof(std::map<int, VisualWord *>);
378 _visualWords.begin()->second->getDescriptor().type() == CV_8U &&
388 memoryUsage +=
_unusedWords.size()*(
sizeof(
int) +
sizeof(
VisualWord *)+
sizeof(std::map<int, VisualWord *>::iterator)) +
sizeof(std::map<int, VisualWord *>);
390 memoryUsage +=
_mapIndexId.size() * (
sizeof(
int)*2+
sizeof(std::map<int ,int>::iterator)) +
sizeof(std::map<int ,int>);
391 memoryUsage +=
_mapIdIndex.size() * (
sizeof(
int)*2+
sizeof(std::map<int ,int>::iterator)) +
sizeof(std::map<int ,int>);
392 memoryUsage +=
_notIndexedWords.size() * (
sizeof(
int)+
sizeof(std::set<int>::iterator)) +
sizeof(std::set<int>);
393 memoryUsage +=
_removedIndexedWords.size() * (
sizeof(
int)+
sizeof(std::set<int>::iterator)) +
sizeof(std::set<int>);
402 cv::Mat descriptorsOut;
403 descriptorsIn.convertTo(descriptorsOut, CV_32F);
404 return descriptorsOut;
409 UASSERT(descriptorsIn.type() == CV_8UC1);
410 cv::Mat descriptorsOut(descriptorsIn.rows, descriptorsIn.cols*8, CV_32FC1);
411 for(
int i=0;
i<descriptorsIn.rows; ++
i)
413 const unsigned char * ptrIn = descriptorsIn.ptr(
i);
414 float * ptrOut = descriptorsOut.ptr<
float>(
i);
415 for(
int j=0;
j<descriptorsIn.cols; ++
j)
418 ptrOut[jo] = (ptrIn[
j] & 1) == 1?1.0
f:0.0
f;
419 ptrOut[jo+1] = (ptrIn[
j] & (1<<1)) != 0?1.0f:0.0f;
420 ptrOut[jo+2] = (ptrIn[
j] & (1<<2)) != 0?1.0f:0.0f;
421 ptrOut[jo+3] = (ptrIn[
j] & (1<<3)) != 0?1.0f:0.0f;
422 ptrOut[jo+4] = (ptrIn[
j] & (1<<4)) != 0?1.0f:0.0f;
423 ptrOut[jo+5] = (ptrIn[
j] & (1<<5)) != 0?1.0f:0.0f;
424 ptrOut[jo+6] = (ptrIn[
j] & (1<<6)) != 0?1.0f:0.0f;
425 ptrOut[jo+7] = (ptrIn[
j] & (1<<7)) != 0?1.0f:0.0f;
428 return descriptorsOut;
437 cv::Mat descriptorsOut;
438 descriptorsIn.convertTo(descriptorsOut, CV_8UC1);
439 return descriptorsOut;
444 UASSERT(descriptorsIn.type() == CV_32FC1 && descriptorsIn.cols % 8 == 0);
445 cv::Mat descriptorsOut(descriptorsIn.rows, descriptorsIn.cols/8, CV_8UC1);
446 for(
int i=0;
i<descriptorsIn.rows; ++
i)
448 const float * ptrIn = descriptorsIn.ptr<
float>(
i);
449 unsigned char * ptrOut = descriptorsOut.ptr(
i);
450 for(
int j=0;
j<descriptorsOut.cols; ++
j)
454 (
unsigned char)(ptrIn[jo] == 0?0:1) |
455 (ptrIn[jo+1] == 0?0:(1<<1)) |
456 (ptrIn[jo+2] == 0?0:(1<<2)) |
457 (ptrIn[jo+3] == 0?0:(1<<3)) |
458 (ptrIn[jo+4] == 0?0:(1<<4)) |
459 (ptrIn[jo+5] == 0?0:(1<<5)) |
460 (ptrIn[jo+6] == 0?0:(1<<6)) |
461 (ptrIn[jo+7] == 0?0:(1<<7));
464 return descriptorsOut;
511 if(
w->getDescriptor().type() == CV_8U)
531 UDEBUG(
"Building FLANN index...");
538 UASSERT_MSG(
descriptor.type() == CV_32F,
"To use KdTree dictionary, float descriptors are required!");
546 UFATAL(
"Not supposed to be here!");
549 UDEBUG(
"Building FLANN index... done!");
558 std::pair<std::map<int, int>::iterator,
bool> inserted;
559 inserted =
_mapIndexId.insert(std::pair<int, int>(index,
w->id()));
561 inserted =
_mapIdIndex.insert(std::pair<int, int>(
w->id(), index));
584 std::pair<std::map<int, int>::iterator,
bool> inserted =
_mapIdIndex.insert(std::pair<int, int>(
w->id(),
i));
603 if(
_visualWords.begin()->second->getDescriptor().type() == CV_8U)
633 if(
iter->second->getDescriptor().type() == CV_8U)
666 UASSERT_MSG(
type == CV_32F,
"To use KdTree dictionary, float descriptors are required!");
670 UASSERT_MSG(
type == CV_8U,
"To use LSH dictionary, binary descriptors are required!");
680 UDEBUG(
"Dictionary updated! (size=%d added=%d removed=%d)",
685 UDEBUG(
"Dictionary has not changed, so no need to update it! (size=%d)",
_dataTree.rows);
695 if(printWarningsIfNotEmpty)
699 UWARN(
"Visual dictionary would be already empty here (%d words still in dictionary).", (
int)
_visualWords.size());
703 UWARN(
"Not indexed words should be empty here (%d words still not indexed)", (
int)
_notIndexedWords.size());
760 const cv::Mat & descriptorsIn,
763 UDEBUG(
"id=%d descriptors=%d", signatureId, descriptorsIn.rows);
765 std::list<int> wordIds;
766 if(descriptorsIn.rows == 0 || descriptorsIn.cols == 0)
768 UERROR(
"Descriptors size is null!");
774 UERROR(
"Dictionary mode is set to fixed but no words are in it!");
787 if(
dim &&
dim != descriptorsIn.cols)
789 UERROR(
"Descriptors (size=%d) are not the same size as already added words in dictionary(size=%d)", descriptorsIn.cols,
dim);
792 if(
type>=0 &&
type != descriptorsIn.type())
794 UERROR(
"Descriptors (type=%d) are not the same type as already added words in dictionary(type=%d)", descriptorsIn.type(),
type);
800 if(descriptorsIn.type() == CV_8U)
809 descriptors = descriptorsIn;
814 descriptors = descriptorsIn;
825 if(
dim &&
dim != descriptors.cols)
827 UERROR(
"Descriptors (size=%d) are not the same size as already added words in dictionary(size=%d)", descriptors.cols,
dim);
831 if(
type>=0 &&
type != descriptors.type())
833 UERROR(
"Descriptors (type=%d) are not the same type as already added words in dictionary(type=%d)", descriptors.type(),
type);
837 int dupWordsCountFromDict= 0;
838 int dupWordsCountFromLast= 0;
843 std::vector<int> newWordsId;
847 std::vector<std::vector<cv::DMatch> >
matches;
848 bool bruteForce =
false;
849 bool isL2NotSqr =
false;
857 UDEBUG(
"newPts.total()=%d ", descriptors.rows);
866 cv::BFMatcher
matcher(descriptors.type()==CV_8U?cv::NORM_HAMMING:cv::NORM_L2SQR);
872 #if CV_MAJOR_VERSION < 3
873 #ifdef HAVE_OPENCV_GPU
874 cv::gpu::GpuMat newDescriptorsGpu(descriptors);
875 cv::gpu::GpuMat lastDescriptorsGpu(
_dataTree);
876 if(descriptors.type()==CV_8U)
878 cv::gpu::BruteForceMatcher_GPU<cv::Hamming> gpuMatcher;
879 gpuMatcher.knnMatch(newDescriptorsGpu, lastDescriptorsGpu,
matches, k);
883 cv::gpu::BruteForceMatcher_GPU<cv::L2<float> > gpuMatcher;
884 gpuMatcher.knnMatch(newDescriptorsGpu, lastDescriptorsGpu,
matches, k);
888 UERROR(
"Cannot use brute Force GPU because OpenCV is not built with gpu module.");
891 #ifdef HAVE_OPENCV_CUDAFEATURES2D
892 cv::cuda::GpuMat newDescriptorsGpu(descriptors);
893 cv::cuda::GpuMat lastDescriptorsGpu(
_dataTree);
894 cv::Ptr<cv::cuda::DescriptorMatcher> gpuMatcher;
895 if(descriptors.type()==CV_8U)
897 gpuMatcher = cv::cuda::DescriptorMatcher::createBFMatcher(cv::NORM_HAMMING);
898 gpuMatcher->knnMatch(newDescriptorsGpu, lastDescriptorsGpu,
matches, k);
902 gpuMatcher = cv::cuda::DescriptorMatcher::createBFMatcher(cv::NORM_L2);
903 gpuMatcher->knnMatch(newDescriptorsGpu, lastDescriptorsGpu,
matches, k);
907 UERROR(
"Cannot use brute Force GPU because OpenCV is not built with cuda module.");
917 if(dists.type() == CV_32S)
920 dists.convertTo(temp, CV_32F);
924 UDEBUG(
"Time to find nn = %f s", timerLocal.
ticks());
928 for(
int i = 0;
i < descriptors.rows; ++
i)
930 std::multimap<float, int> fullResults;
931 if(!bruteForce && dists.cols)
933 for(
int j=0;
j<dists.cols; ++
j)
935 float d = dists.at<
float>(
i,
j);
937 if (
sizeof(
size_t) == 8)
939 index = *((
size_t*)&
results.at<
double>(
i,
j));
943 index = *((
size_t*)&
results.at<
int>(
i,
j));
946 if(
d >= 0.0
f &&
id != 0)
948 fullResults.insert(std::pair<float, int>(
d,
id));
956 else if(bruteForce &&
matches.size())
962 if(
d >= 0.0
f &&
id != 0)
969 fullResults.insert(std::pair<float, int>(
d,
id));
981 std::vector<std::vector<cv::DMatch> > matchesNewWords;
982 cv::BFMatcher
matcher(descriptors.type()==CV_8U?cv::NORM_HAMMING:
useDistanceL1_?cv::NORM_L1:cv::NORM_L2SQR);
983 UASSERT(descriptors.cols == newWords.cols && descriptors.type() == newWords.type());
984 matcher.knnMatch(descriptors.row(
i), newWords, matchesNewWords, newWords.rows>1?2:1);
985 UASSERT(matchesNewWords.size() == 1);
986 for(
unsigned int j=0;
j<matchesNewWords.at(0).
size(); ++
j)
988 float d = matchesNewWords.at(0).at(
j).distance;
989 int id = newWordsId[matchesNewWords.at(0).at(
j).trainIdx];
990 if(
d >= 0.0
f &&
id != 0)
992 fullResults.insert(std::pair<float, int>(
d,
id));
1003 bool badDist =
false;
1004 if(fullResults.size() == 0)
1010 if(fullResults.size() >= 2)
1013 if(fullResults.begin()->first >
_nndrRatio * (++fullResults.begin())->first)
1030 newWords.push_back(descriptors.row(
i));
1031 newWordsId.push_back(vw->
id());
1032 wordIds.push_back(vw->
id());
1039 ++dupWordsCountFromLast;
1043 ++dupWordsCountFromDict;
1046 this->
addWordRef(fullResults.begin()->second, signatureId);
1047 wordIds.push_back(fullResults.begin()->second);
1050 else if(fullResults.size())
1053 ++dupWordsCountFromDict;
1054 this->
addWordRef(fullResults.begin()->second, signatureId);
1055 wordIds.push_back(fullResults.begin()->second);
1056 UASSERT(fullResults.begin()->second>0);
1062 ULOGGER_DEBUG(
"%d duplicated words added (from current image = %d)...",
1063 dupWordsCountFromDict+dupWordsCountFromLast, dupWordsCountFromLast);
1077 int type = (*vws.begin())->getDescriptor().type();
1078 int dim = (*vws.begin())->getDescriptor().cols;
1082 UERROR(
"Descriptors (size=%d) are not the same size as already added words in dictionary(size=%d)", (*vws.begin())->getDescriptor().cols,
dim);
1083 return std::vector<int>(vws.size(), 0);
1088 UERROR(
"Descriptors (type=%d) are not the same type as already added words in dictionary(type=%d)", (*vws.begin())->getDescriptor().type(),
type);
1089 return std::vector<int>(vws.size(), 0);
1095 cv::Mat query(vws.size(),
dim,
type);
1096 for(std::list<VisualWord *>::const_iterator
iter=vws.begin();
iter!=vws.end(); ++
iter, ++index)
1110 return std::vector<int>(vws.size(), 0);
1116 std::vector<int> resultIds(queryIn.rows, 0);
1126 if(
dim != queryIn.cols)
1128 UERROR(
"Descriptors (size=%d) are not the same size as already added words in dictionary(size=%d)", queryIn.cols,
dim);
1131 if(
type != queryIn.type())
1133 UERROR(
"Descriptors (type=%d) are not the same type as already added words in dictionary(type=%d)", queryIn.type(),
type);
1139 if(queryIn.type() == CV_8U)
1163 if(
dim &&
dim != query.cols)
1165 UERROR(
"Descriptors (size=%d) are not the same size as already added words in dictionary(size=%d)", query.cols,
dim);
1169 if(
type>=0 &&
type != query.type())
1171 UERROR(
"Descriptors (type=%d) are not the same type as already added words in dictionary(type=%d)", query.type(),
type);
1175 std::vector<std::vector<cv::DMatch> >
matches;
1176 bool bruteForce =
false;
1177 bool isL2NotSqr =
false;
1184 UDEBUG(
"query.rows=%d ", query.rows);
1193 cv::BFMatcher
matcher(query.type()==CV_8U?cv::NORM_HAMMING:cv::NORM_L2SQR);
1199 #if CV_MAJOR_VERSION < 3
1200 #ifdef HAVE_OPENCV_GPU
1201 cv::gpu::GpuMat newDescriptorsGpu(query);
1202 cv::gpu::GpuMat lastDescriptorsGpu(
_dataTree);
1203 if(query.type()==CV_8U)
1205 cv::gpu::BruteForceMatcher_GPU<cv::Hamming> gpuMatcher;
1206 gpuMatcher.knnMatch(newDescriptorsGpu, lastDescriptorsGpu,
matches, k);
1210 cv::gpu::BruteForceMatcher_GPU<cv::L2<float> > gpuMatcher;
1211 gpuMatcher.knnMatch(newDescriptorsGpu, lastDescriptorsGpu,
matches, k);
1215 UERROR(
"Cannot use brute Force GPU because OpenCV is not built with gpu module.");
1218 #ifdef HAVE_OPENCV_CUDAFEATURES2D
1219 cv::cuda::GpuMat newDescriptorsGpu(query);
1220 cv::cuda::GpuMat lastDescriptorsGpu(
_dataTree);
1221 cv::cuda::GpuMat matchesGpu;
1222 cv::Ptr<cv::cuda::DescriptorMatcher> gpuMatcher;
1223 if(query.type()==CV_8U)
1225 gpuMatcher = cv::cuda::DescriptorMatcher::createBFMatcher(cv::NORM_HAMMING);
1226 gpuMatcher->knnMatchAsync(newDescriptorsGpu, lastDescriptorsGpu, matchesGpu, k);
1230 gpuMatcher = cv::cuda::DescriptorMatcher::createBFMatcher(cv::NORM_L2);
1231 gpuMatcher->knnMatchAsync(newDescriptorsGpu, lastDescriptorsGpu, matchesGpu, k);
1234 gpuMatcher->knnMatchConvert(matchesGpu,
matches);
1236 UERROR(
"Cannot use brute Force GPU because OpenCV is not built with cuda module.");
1246 if(dists.type() == CV_32S)
1249 dists.convertTo(temp, CV_32F);
1255 std::map<int, int> mapIndexIdNotIndexed;
1256 std::vector<std::vector<cv::DMatch> > matchesNotIndexed;
1259 cv::Mat dataNotIndexed = cv::Mat::zeros(
_notIndexedWords.size(), query.cols, query.type());
1260 unsigned int index = 0;
1283 descriptor.copyTo(dataNotIndexed.row(index));
1284 mapIndexIdNotIndexed.insert(mapIndexIdNotIndexed.end(), std::pair<int,int>(index, vw->
id()));
1288 cv::BFMatcher
matcher(query.type()==CV_8U?cv::NORM_HAMMING:
useDistanceL1_?cv::NORM_L1:cv::NORM_L2SQR);
1289 matcher.knnMatch(query, dataNotIndexed, matchesNotIndexed, dataNotIndexed.rows>1?2:1);
1293 for(
int i=0;
i<query.rows; ++
i)
1295 std::multimap<float, int> fullResults;
1296 if(!bruteForce && dists.cols)
1298 for(
int j=0;
j<dists.cols; ++
j)
1300 float d = dists.at<
float>(
i,
j);
1303 if (
sizeof(
size_t) == 8)
1305 index = *((
size_t*)&
results.at<
double>(
i,
j));
1309 index = *((
size_t*)&
results.at<
int>(
i,
j));
1312 if(
d >= 0.0
f &&
id != 0)
1314 fullResults.insert(std::pair<float, int>(
d,
id));
1318 else if(bruteForce &&
matches.size())
1324 if(
d >= 0.0
f &&
id != 0)
1331 fullResults.insert(std::pair<float, int>(
d,
id));
1337 if(matchesNotIndexed.size())
1339 for(
unsigned int j=0;
j<matchesNotIndexed.at(
i).
size(); ++
j)
1341 float d = matchesNotIndexed.at(
i).at(
j).distance;
1342 int id =
uValue(mapIndexIdNotIndexed, matchesNotIndexed.at(
i).at(
j).trainIdx);
1343 if(
d >= 0.0
f &&
id != 0)
1345 fullResults.insert(std::pair<float, int>(
d,
id));
1356 bool badDist =
false;
1357 if(fullResults.size() == 0)
1363 if(fullResults.size() >= 2)
1366 if(fullResults.begin()->first >
_nndrRatio * (++fullResults.begin())->first)
1379 resultIds[
i] = fullResults.begin()->second;
1382 else if(fullResults.size())
1385 resultIds[
i] = fullResults.begin()->second;
1407 if(_lastWordId < vw->
id())
1437 for(
unsigned int i=0;
i<words.size(); ++
i)
1452 for(
unsigned int i=0;
i<unusedWords.size(); ++
i)
1454 delete unusedWords[
i];
1463 UWARN(
"Dictionary is empty, cannot export it!");
1466 if(
_visualWords.begin()->second->getDescriptor().type() != CV_32FC1)
1468 UERROR(
"Exporting binary descriptors is not implemented!");
1474 fopen_s(&foutRef, fileNameReferences,
"w");
1475 fopen_s(&foutDesc, fileNameDescriptors,
"w");
1477 foutRef = fopen(fileNameReferences,
"w");
1478 foutDesc = fopen(fileNameDescriptors,
"w");
1483 fprintf(foutRef,
"WordID SignaturesID...\n");
1489 fprintf(foutDesc,
"WordID Descriptors...\n");
1494 fprintf(foutDesc,
"WordID Descriptors...%d\n", (*
_visualWords.begin()).second->getDescriptor().cols);
1504 fprintf(foutRef,
"%d ", (*iter).first);
1505 const std::map<int, int>
ref = (*iter).second->getReferences();
1506 for(std::map<int, int>::const_iterator jter=
ref.begin(); jter!=
ref.end(); ++jter)
1508 for(
int i=0;
i<(*jter).second; ++
i)
1510 fprintf(foutRef,
"%d ", (*jter).first);
1513 fprintf(foutRef,
"\n");
1519 fprintf(foutDesc,
"%d ", (*iter).first);
1520 const float *
desc = (
const float *)(*iter).second->getDescriptor().data;
1521 int dim = (*iter).second->getDescriptor().cols;
1525 fprintf(foutDesc,
"%f ",
desc[
i]);
1527 fprintf(foutDesc,
"\n");