2014-05-11 23:57:08 +00:00
|
|
|
|
/*
|
|
|
|
|
|
* Vocabulary.cpp
|
|
|
|
|
|
*
|
|
|
|
|
|
* Created on: 2014-05-09
|
|
|
|
|
|
* Author: mathieu
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "Vocabulary.h"
|
|
|
|
|
|
#include "Settings.h"
|
|
|
|
|
|
#include <QtCore/QVector>
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
|
|
Vocabulary::Vocabulary()
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Vocabulary::~Vocabulary()
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Vocabulary::clear()
|
|
|
|
|
|
{
|
2014-05-12 22:58:57 +00:00
|
|
|
|
indexedDescriptors_ = cv::Mat();
|
|
|
|
|
|
notIndexedDescriptors_ = cv::Mat();
|
2014-05-11 23:57:08 +00:00
|
|
|
|
wordToObjects_.clear();
|
2014-05-12 22:58:57 +00:00
|
|
|
|
notIndexedWordIds_.clear();
|
2014-05-11 23:57:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QMultiMap<int, int> Vocabulary::addWords(const cv::Mat & descriptors, int objectIndex, bool incremental)
|
|
|
|
|
|
{
|
|
|
|
|
|
QMultiMap<int, int> words;
|
|
|
|
|
|
if (descriptors.empty())
|
|
|
|
|
|
{
|
|
|
|
|
|
return words;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(incremental)
|
|
|
|
|
|
{
|
|
|
|
|
|
int k = 2;
|
2014-05-12 22:58:57 +00:00
|
|
|
|
cv::Mat results;
|
|
|
|
|
|
cv::Mat dists;
|
2014-05-11 23:57:08 +00:00
|
|
|
|
|
|
|
|
|
|
bool globalSearch = false;
|
2014-05-12 22:58:57 +00:00
|
|
|
|
if(!indexedDescriptors_.empty() && indexedDescriptors_.rows >= (int)k)
|
2014-05-11 23:57:08 +00:00
|
|
|
|
{
|
2014-05-12 22:58:57 +00:00
|
|
|
|
Q_ASSERT(indexedDescriptors_.type() == descriptors.type() && indexedDescriptors_.cols == descriptors.cols);
|
2014-05-11 23:57:08 +00:00
|
|
|
|
flannIndex_.knnSearch(descriptors, results, dists, k, Settings::getFlannSearchParams() );
|
2014-05-12 22:58:57 +00:00
|
|
|
|
|
|
|
|
|
|
if( dists.type() == CV_32S )
|
|
|
|
|
|
{
|
|
|
|
|
|
cv::Mat temp;
|
|
|
|
|
|
dists.convertTo(temp, CV_32F);
|
|
|
|
|
|
dists = temp;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-05-11 23:57:08 +00:00
|
|
|
|
globalSearch = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-05-13 14:41:36 +00:00
|
|
|
|
notIndexedWordIds_.reserve(notIndexedWordIds_.size() + descriptors.rows);
|
|
|
|
|
|
notIndexedDescriptors_.reserve(notIndexedDescriptors_.rows + descriptors.rows);
|
2014-05-11 23:57:08 +00:00
|
|
|
|
int matches = 0;
|
|
|
|
|
|
for(int i = 0; i < descriptors.rows; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
QMap<float, int> fullResults; // nearest descriptors sorted by distance
|
2014-05-13 14:41:36 +00:00
|
|
|
|
if(notIndexedDescriptors_.rows)
|
2014-05-11 23:57:08 +00:00
|
|
|
|
{
|
2014-05-12 22:58:57 +00:00
|
|
|
|
Q_ASSERT(newWords.type() == descriptors.type() && newWords.cols == descriptors.cols);
|
|
|
|
|
|
|
2014-05-11 23:57:08 +00:00
|
|
|
|
// Check if this descriptor matches with a word not already added to the vocabulary
|
2014-05-12 22:58:57 +00:00
|
|
|
|
// Do linear search only
|
|
|
|
|
|
cv::Mat tmpResults;
|
|
|
|
|
|
cv::Mat tmpDists;
|
2014-05-11 23:57:08 +00:00
|
|
|
|
if(descriptors.type()==CV_8U)
|
|
|
|
|
|
{
|
2014-05-12 22:58:57 +00:00
|
|
|
|
//normType – One of NORM_L1, NORM_L2, NORM_HAMMING, NORM_HAMMING2. L1 and L2 norms are
|
|
|
|
|
|
// preferable choices for SIFT and SURF descriptors, NORM_HAMMING should be
|
|
|
|
|
|
// used with ORB, BRISK and BRIEF, NORM_HAMMING2 should be used with ORB
|
|
|
|
|
|
// when WTA_K==3 or 4 (see ORB::ORB constructor description).
|
|
|
|
|
|
int normType = cv::NORM_HAMMING;
|
|
|
|
|
|
if(Settings::currentDescriptorType().compare("ORB") &&
|
|
|
|
|
|
(Settings::getFeature2D_ORB_WTA_K()==3 || Settings::getFeature2D_ORB_WTA_K()==4))
|
|
|
|
|
|
{
|
|
|
|
|
|
normType = cv::NORM_HAMMING2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cv::batchDistance( descriptors.row(i),
|
2014-05-13 14:41:36 +00:00
|
|
|
|
notIndexedDescriptors_,
|
2014-05-12 22:58:57 +00:00
|
|
|
|
tmpDists,
|
|
|
|
|
|
CV_32S,
|
|
|
|
|
|
tmpResults,
|
|
|
|
|
|
normType,
|
2014-05-13 14:41:36 +00:00
|
|
|
|
notIndexedDescriptors_.rows>=k?k:1,
|
2014-05-12 22:58:57 +00:00
|
|
|
|
cv::Mat(),
|
|
|
|
|
|
0,
|
|
|
|
|
|
false);
|
2014-05-11 23:57:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2014-05-12 22:58:57 +00:00
|
|
|
|
cv::flann::Index tmpIndex;
|
2014-05-13 14:41:36 +00:00
|
|
|
|
tmpIndex.build(notIndexedDescriptors_, cv::flann::LinearIndexParams(), Settings::getFlannDistanceType());
|
|
|
|
|
|
tmpIndex.knnSearch(descriptors.row(i), tmpResults, tmpDists, notIndexedDescriptors_.rows>1?k:1, Settings::getFlannSearchParams());
|
2014-05-11 23:57:08 +00:00
|
|
|
|
}
|
2014-05-12 22:58:57 +00:00
|
|
|
|
|
|
|
|
|
|
if( tmpDists.type() == CV_32S )
|
2014-05-11 23:57:08 +00:00
|
|
|
|
{
|
2014-05-12 22:58:57 +00:00
|
|
|
|
cv::Mat temp;
|
|
|
|
|
|
tmpDists.convertTo(temp, CV_32F);
|
|
|
|
|
|
tmpDists = temp;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-05-13 14:41:36 +00:00
|
|
|
|
for(int j = 0; j < tmpResults.cols; ++j)
|
2014-05-12 22:58:57 +00:00
|
|
|
|
{
|
|
|
|
|
|
if(tmpResults.at<int>(0,j) >= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
//printf("local i=%d, j=%d, tmpDist=%f tmpResult=%d\n", i ,j, tmpDists.at<float>(0,j), tmpResults.at<int>(0,j));
|
2014-05-13 14:41:36 +00:00
|
|
|
|
fullResults.insert(tmpDists.at<float>(0,j), notIndexedWordIds_.at(tmpResults.at<int>(0,j)));
|
2014-05-12 22:58:57 +00:00
|
|
|
|
}
|
2014-05-11 23:57:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(globalSearch)
|
|
|
|
|
|
{
|
|
|
|
|
|
for(int j=0; j<k; ++j)
|
|
|
|
|
|
{
|
2014-05-12 22:58:57 +00:00
|
|
|
|
if(results.at<int>(i,j) >= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
//printf("global i=%d, j=%d, dist=%f\n", i ,j, dists.at<float>(i,j));
|
|
|
|
|
|
fullResults.insert(dists.at<float>(i,j), results.at<int>(i,j));
|
|
|
|
|
|
}
|
2014-05-11 23:57:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool match = false;
|
|
|
|
|
|
// Apply NNDR
|
|
|
|
|
|
if(fullResults.size() >= 2 &&
|
|
|
|
|
|
fullResults.begin().key() <= Settings::getNearestNeighbor_4nndrRatio() * (++fullResults.begin()).key())
|
|
|
|
|
|
{
|
|
|
|
|
|
match = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(match)
|
|
|
|
|
|
{
|
|
|
|
|
|
words.insert(fullResults.begin().value(), i);
|
|
|
|
|
|
wordToObjects_.insert(fullResults.begin().value(), objectIndex);
|
|
|
|
|
|
++matches;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2014-05-13 14:41:36 +00:00
|
|
|
|
//concatenate new words
|
|
|
|
|
|
notIndexedWordIds_.push_back(indexedDescriptors_.rows + notIndexedDescriptors_.rows);
|
|
|
|
|
|
notIndexedDescriptors_.push_back(descriptors.row(i));
|
|
|
|
|
|
words.insert(notIndexedWordIds_.back(), i);
|
|
|
|
|
|
wordToObjects_.insert(notIndexedWordIds_.back(), objectIndex);
|
2014-05-11 23:57:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
for(int i = 0; i < descriptors.rows; ++i)
|
|
|
|
|
|
{
|
2014-05-12 22:58:57 +00:00
|
|
|
|
wordToObjects_.insert(indexedDescriptors_.rows + notIndexedDescriptors_.rows+i, objectIndex);
|
|
|
|
|
|
words.insert(indexedDescriptors_.rows + notIndexedDescriptors_.rows+i, i);
|
|
|
|
|
|
notIndexedWordIds_.push_back(indexedDescriptors_.rows + notIndexedDescriptors_.rows+i);
|
2014-05-11 23:57:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//just concatenate descriptors
|
2014-05-13 14:41:36 +00:00
|
|
|
|
notIndexedDescriptors_.push_back(descriptors);
|
2014-05-11 23:57:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return words;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Vocabulary::update()
|
|
|
|
|
|
{
|
2014-05-12 22:58:57 +00:00
|
|
|
|
if(!notIndexedDescriptors_.empty())
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_ASSERT(indexedDescriptors_.cols == notIndexedDescriptors_.cols &&
|
|
|
|
|
|
indexedDescriptors_.type() == notIndexedDescriptors_.type() );
|
|
|
|
|
|
|
|
|
|
|
|
//concatenate descriptors
|
2014-05-13 14:41:36 +00:00
|
|
|
|
indexedDescriptors_.push_back(notIndexedDescriptors_);
|
2014-05-12 22:58:57 +00:00
|
|
|
|
|
|
|
|
|
|
notIndexedDescriptors_ = cv::Mat();
|
|
|
|
|
|
notIndexedWordIds_.clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(!indexedDescriptors_.empty())
|
2014-05-11 23:57:08 +00:00
|
|
|
|
{
|
|
|
|
|
|
cv::flann::IndexParams * params = Settings::createFlannIndexParams();
|
2014-05-12 22:58:57 +00:00
|
|
|
|
flannIndex_.build(indexedDescriptors_, *params, Settings::getFlannDistanceType());
|
2014-05-11 23:57:08 +00:00
|
|
|
|
delete params;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Vocabulary::search(const cv::Mat & descriptors, cv::Mat & results, cv::Mat & dists, int k)
|
|
|
|
|
|
{
|
2014-05-12 22:58:57 +00:00
|
|
|
|
Q_ASSERT(notIndexedDescriptors_.empty() && notIndexedWordIds_.size() == 0);
|
|
|
|
|
|
|
|
|
|
|
|
if(!indexedDescriptors_.empty())
|
2014-05-11 23:57:08 +00:00
|
|
|
|
{
|
2014-05-12 22:58:57 +00:00
|
|
|
|
Q_ASSERT(descriptors.type() == indexedDescriptors_.type() && descriptors.cols == indexedDescriptors_.cols);
|
|
|
|
|
|
|
2014-05-11 23:57:08 +00:00
|
|
|
|
flannIndex_.knnSearch(descriptors, results, dists, k, Settings::getFlannSearchParams());
|
2014-05-12 22:58:57 +00:00
|
|
|
|
|
|
|
|
|
|
if( dists.type() == CV_32S )
|
|
|
|
|
|
{
|
|
|
|
|
|
cv::Mat temp;
|
|
|
|
|
|
dists.convertTo(temp, CV_32F);
|
|
|
|
|
|
dists = temp;
|
|
|
|
|
|
}
|
2014-05-11 23:57:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|