Merge pull request #3829 from ippei-i:2.4-flann-lsh-addable-index

pull/4030/head
Vadim Pisarevsky 10 years ago
commit b9792fdc14
  1. 14
      modules/features2d/src/matchers.cpp
  2. 533
      modules/features2d/test/test_lshindex_flannbased_matcher.cpp
  3. 1
      modules/features2d/test/test_precomp.hpp
  4. 7
      modules/flann/include/opencv2/flann/autotuned_index.h
  5. 7
      modules/flann/include/opencv2/flann/composite_index.h
  6. 10
      modules/flann/include/opencv2/flann/flann_base.hpp
  7. 8
      modules/flann/include/opencv2/flann/hierarchical_clustering_index.h
  8. 7
      modules/flann/include/opencv2/flann/kdtree_index.h
  9. 7
      modules/flann/include/opencv2/flann/kdtree_single_index.h
  10. 7
      modules/flann/include/opencv2/flann/kmeans_index.h
  11. 7
      modules/flann/include/opencv2/flann/linear_index.h
  12. 20
      modules/flann/include/opencv2/flann/lsh_index.h
  13. 5
      modules/flann/include/opencv2/flann/lsh_table.h
  14. 3
      modules/flann/include/opencv2/flann/miniflann.hpp
  15. 5
      modules/flann/include/opencv2/flann/nn_index.h
  16. 55
      modules/flann/src/miniflann.cpp
  17. 2
      modules/ts/CMakeLists.txt

@ -531,10 +531,20 @@ void FlannBasedMatcher::clear()
void FlannBasedMatcher::train()
{
if( flannIndex.empty() || mergedDescriptors.size() < addedDescCount )
int trained = mergedDescriptors.size();
if (flannIndex.empty() || trained < addedDescCount)
{
mergedDescriptors.set( trainDescCollection );
flannIndex = new flann::Index( mergedDescriptors.getDescriptors(), *indexParams );
// construct flannIndex class, if empty or Algorithm not equal FLANN_INDEX_LSH
if (flannIndex.empty() || flannIndex->getAlgorithm() != cvflann::FLANN_INDEX_LSH)
{
flannIndex = new flann::Index(mergedDescriptors.getDescriptors(), *indexParams);
}
else
{
flannIndex->build(mergedDescriptors.getDescriptors(), mergedDescriptors.getDescriptors().rowRange(trained, mergedDescriptors.size()), *indexParams, cvflann::FLANN_DIST_HAMMING);
}
}
}

@ -0,0 +1,533 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright (c) 2015 Ippei Ito. All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*************************************************************************/
/*
For OpenCV2.4/OpenCV3.0
Test for Pull Request # 3829
https://github.com/Itseez/opencv/pull/3829
This test code creates brute force matcher for accuracy of reference, and the test target matcher.
Then, add() and train() transformed query image descriptors, and some outlier images descriptors to both matchers.
Then, compared with the query image by match() and findHomography() to detect outlier and calculate accuracy.
And each drawMatches() images are saved, if SAVE_DRAW_MATCHES_IMAGES is true.
Finally, compare accuracies between the brute force matcher and the test target matcher.
The lsh algorithm uses std::random_shuffle in lsh_index.h to make the random indexes table.
So, in relation to default random seed value of the execution environment or by using "srand(time(0)) function",
the match time and accuracy of the match results are different, each time the code ran.
And the match time becomes late in relation to the number of the hash collision times.
*/
#include "test_precomp.hpp"
#include "opencv2/ts.hpp" // for FilePath::CreateFolder()
#include <time.h> // for time()
// If defined, the match time and accuracy of the match results are a little different, each time the code ran.
//#define INIT_RANDOM_SEED
// If defined, some outlier images descriptors add() the matcher.
#define TRAIN_WITH_OUTLIER_IMAGES
// If true, save drawMatches() images.
#define SAVE_DRAW_MATCHES_IMAGES false
// if true, verbose output
#define SHOW_DEBUG_LOG true
#if CV_MAJOR_VERSION==2
#define OrbCreate new cv::ORB(4000)
#elif CV_MAJOR_VERSION==3
#define OrbCreate cv::ORB::create(4000)
#define AKazeCreate cv::AKAZE::create()
#endif
using namespace std;
int testno_for_make_filename = 0;
// --------------------------------------------------------------------------------------
// Parameter class to transform query image
// --------------------------------------------------------------------------------------
class testparam
{
public:
string transname;
void(*transfunc)(float, const cv::Mat&, cv::Mat&);
float from, to, step;
testparam(string _transname, void(*_transfunc)(float, const cv::Mat&, cv::Mat&), float _from, float _to, float _step) :
transname(_transname),
transfunc(_transfunc),
from(_from),
to(_to),
step(_step)
{}
};
// --------------------------------------------------------------------------------------
// from matching_to_many_images.cpp
// --------------------------------------------------------------------------------------
int maskMatchesByTrainImgIdx(const vector<cv::DMatch>& matches, int trainImgIdx, vector<char>& mask)
{
int matchcnt = 0;
mask.resize(matches.size());
fill(mask.begin(), mask.end(), 0);
for (size_t i = 0; i < matches.size(); i++)
{
if (matches[i].imgIdx == trainImgIdx)
{
mask[i] = 1;
matchcnt++;
}
}
return matchcnt;
}
int calcHomographyAndInlierCount(const vector<cv::KeyPoint>& query_kp, const vector<cv::KeyPoint>& train_kp, const vector<cv::DMatch>& match, vector<char> &mask, cv::Mat &homography)
{
// make query and current train image keypoint pairs
std::vector<cv::Point2f> srcPoints, dstPoints;
for (unsigned int i = 0; i < match.size(); ++i)
{
if (mask[i] != 0) // is current train image ?
{
srcPoints.push_back(query_kp[match[i].queryIdx].pt);
dstPoints.push_back(train_kp[match[i].trainIdx].pt);
}
}
// calc homography
vector<uchar> inlierMask;
homography = findHomography(srcPoints, dstPoints, cv::RANSAC, 3.0, inlierMask);
// update outlier mask
int j = 0;
for (unsigned int i = 0; i < match.size(); ++i)
{
if (mask[i] != 0) // is current train image ?
{
if (inlierMask.size() == 0 || inlierMask[j] == 0) // is outlier ?
{
mask[i] = 0;
}
j++;
}
}
// count inlier
int inlierCnt = 0;
for (unsigned int i = 0; i < mask.size(); ++i)
{
if (mask[i] != 0)
{
inlierCnt++;
}
}
return inlierCnt;
}
void drawDetectedRectangle(cv::Mat& imgResult, const cv::Mat& homography, const cv::Mat& imgQuery)
{
std::vector<cv::Point2f> query_corners(4);
query_corners[0] = cv::Point(0, 0);
query_corners[1] = cv::Point(imgQuery.cols, 0);
query_corners[2] = cv::Point(imgQuery.cols, imgQuery.rows);
query_corners[3] = cv::Point(0, imgQuery.rows);
std::vector<cv::Point2f> train_corners(4);
perspectiveTransform(query_corners, train_corners, homography);
line(imgResult, train_corners[0] + query_corners[1], train_corners[1] + query_corners[1], cv::Scalar(0, 255, 0), 4);
line(imgResult, train_corners[1] + query_corners[1], train_corners[2] + query_corners[1], cv::Scalar(0, 255, 0), 4);
line(imgResult, train_corners[2] + query_corners[1], train_corners[3] + query_corners[1], cv::Scalar(0, 255, 0), 4);
line(imgResult, train_corners[3] + query_corners[1], train_corners[0] + query_corners[1], cv::Scalar(0, 255, 0), 4);
}
// --------------------------------------------------------------------------------------
// transform query image, extract&compute, train, matching and save result image function
// --------------------------------------------------------------------------------------
typedef struct tagTrainInfo
{
int traindesccnt;
double traintime;
double matchtime;
double accuracy;
}TrainInfo;
TrainInfo transImgAndTrain(
cv::Feature2D *fe,
cv::DescriptorMatcher *matcher,
const string &matchername,
const cv::Mat& imgQuery, const vector<cv::KeyPoint>& query_kp, const cv::Mat& query_desc,
const vector<cv::Mat>& imgOutliers, const vector<vector<cv::KeyPoint> >& outliers_kp, const vector<cv::Mat>& outliers_desc, const int totalOutlierDescCnt,
const float t, const testparam *tp,
const int testno, const bool bVerboseOutput, const bool bSaveDrawMatches)
{
TrainInfo ti;
// transform query image
cv::Mat imgTransform;
(tp->transfunc)(t, imgQuery, imgTransform);
// extract kp and compute desc from transformed query image
vector<cv::KeyPoint> trans_query_kp;
cv::Mat trans_query_desc;
#if CV_MAJOR_VERSION==2
(*fe)(imgTransform, cv::Mat(), trans_query_kp, trans_query_desc);
#elif CV_MAJOR_VERSION==3
fe->detectAndCompute(imgTransform, Mat(), trans_query_kp, trans_query_desc);
#endif
// add&train transformed query desc and outlier desc
matcher->clear();
matcher->add(vector<cv::Mat>(1, trans_query_desc));
double s = (double)cv::getTickCount();
matcher->train();
ti.traintime = 1000.0*((double)cv::getTickCount() - s) / cv::getTickFrequency();
ti.traindesccnt = trans_query_desc.rows;
#if defined(TRAIN_WITH_OUTLIER_IMAGES)
// same as matcher->add(outliers_desc); matcher->train();
for (unsigned int i = 0; i < outliers_desc.size(); ++i)
{
matcher->add(vector<cv::Mat>(1, outliers_desc[i]));
s = (double)cv::getTickCount();
matcher->train();
ti.traintime += 1000.0*((double)cv::getTickCount() - s) / cv::getTickFrequency();
}
ti.traindesccnt += totalOutlierDescCnt;
#endif
// matching
vector<cv::DMatch> match;
s = (double)cv::getTickCount();
matcher->match(query_desc, match);
ti.matchtime = 1000.0*((double)cv::getTickCount() - s) / cv::getTickFrequency();
// prepare a directory and variables for save matching images
vector<char> mask;
cv::Mat imgResult;
const char resultDir[] = "result";
if (bSaveDrawMatches)
{
testing::internal::FilePath fp = testing::internal::FilePath(resultDir);
fp.CreateFolder();
}
char buff[2048];
int matchcnt;
// save query vs transformed query matching image with detected rectangle
matchcnt = maskMatchesByTrainImgIdx(match, (int)0, mask);
// calc homography and inlier
cv::Mat homography;
int inlierCnt = calcHomographyAndInlierCount(query_kp, trans_query_kp, match, mask, homography);
ti.accuracy = (double)inlierCnt / (double)mask.size()*100.0;
drawMatches(imgQuery, query_kp, imgTransform, trans_query_kp, match, imgResult, cv::Scalar::all(-1), cv::Scalar::all(128), mask, cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
if (inlierCnt)
{
// draw detected rectangle
drawDetectedRectangle(imgResult, homography, imgQuery);
}
// draw status
sprintf(buff, "%s accuracy:%-3.2f%% %d descriptors training time:%-3.2fms matching :%-3.2fms", matchername.c_str(), ti.accuracy, ti.traindesccnt, ti.traintime, ti.matchtime);
putText(imgResult, buff, cv::Point(0, 12), cv::FONT_HERSHEY_PLAIN, 0.8, cv::Scalar(0., 0., 255.));
sprintf(buff, "%s/res%03d_%s_%s%.1f_inlier.png", resultDir, testno, matchername.c_str(), tp->transname.c_str(), t);
if (bSaveDrawMatches && !imwrite(buff, imgResult)) cout << "Image " << buff << " can not be saved (may be because directory " << resultDir << " does not exist)." << endl;
#if defined(TRAIN_WITH_OUTLIER_IMAGES)
// save query vs outlier matching image(s)
for (unsigned int i = 0; i <imgOutliers.size(); ++i)
{
matchcnt = maskMatchesByTrainImgIdx(match, (int)i + 1, mask);
drawMatches(imgQuery, query_kp, imgOutliers[i], outliers_kp[i], match, imgResult, cv::Scalar::all(-1), cv::Scalar::all(128), mask);// , DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
sprintf(buff, "query_num:%d train_num:%d matched:%d %d descriptors training time:%-3.2fms matching :%-3.2fms", (int)query_kp.size(), (int)outliers_kp[i].size(), matchcnt, ti.traindesccnt, ti.traintime, ti.matchtime);
putText(imgResult, buff, cv::Point(0, 12), cv::FONT_HERSHEY_PLAIN, 0.8, cv::Scalar(0., 0., 255.));
sprintf(buff, "%s/res%03d_%s_%s%.1f_outlier%02d.png", resultDir, testno, matchername.c_str(), tp->transname.c_str(), t, i);
if (bSaveDrawMatches && !imwrite(buff, imgResult)) cout << "Image " << buff << " can not be saved (may be because directory " << resultDir << " does not exist)." << endl;
}
#endif
if (bVerboseOutput)
{
cout << tp->transname <<" image matching accuracy:" << ti.accuracy << "% " << ti.traindesccnt << " train:" << ti.traintime << "ms match:" << ti.matchtime << "ms" << endl;
}
return ti;
}
// --------------------------------------------------------------------------------------
// Main Test Class
// --------------------------------------------------------------------------------------
class CV_FeatureDetectorMatcherBaseTest : public cvtest::BaseTest
{
private:
testparam *tp;
double target_accuracy_margin_from_bfmatcher;
cv::Feature2D* fe; // feature detector extractor
cv::DescriptorMatcher* bfmatcher; // brute force matcher for accuracy of reference
cv::DescriptorMatcher* flmatcher; // flann matcher to test
cv::Mat imgQuery; // query image
vector<cv::Mat> imgOutliers; // outlier image
vector<cv::KeyPoint> query_kp; // query key points detect from imgQuery
cv::Mat query_desc; // query descriptors extract from imgQuery
vector<vector<cv::KeyPoint> > outliers_kp;
vector<cv::Mat> outliers_desc;
int totalOutlierDescCnt;
string flmatchername;
public:
//
// constructor
//
CV_FeatureDetectorMatcherBaseTest(testparam* _tp, double _accuracy_margin, cv::Feature2D* _fe, cv::DescriptorMatcher *_flmatcher, string _flmatchername, int norm_type_for_bfmatcher) :
tp(_tp),
target_accuracy_margin_from_bfmatcher(_accuracy_margin),
fe(_fe),
flmatcher(_flmatcher),
flmatchername(_flmatchername)
{
#if defined(INIT_RANDOM_SEED)
// from test/test_eigen.cpp
srand((unsigned int)time(0));
#endif
// create brute force matcher for accuracy of reference
bfmatcher = new cv::BFMatcher(norm_type_for_bfmatcher);
}
//
// Main Test method
//
virtual void run(int)
{
// load query image
string strQueryFile = string(cvtest::TS::ptr()->get_data_path()) + "shared/lena.png";
imgQuery = cv::imread(strQueryFile, 0);
if (imgQuery.empty())
{
ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", strQueryFile.c_str());
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
return;
}
// load outlier images
char* outliers[] = { (char*)"baboon.png", (char*)"fruits.png", (char*)"airplane.png" };
for (unsigned int i = 0; i < sizeof(outliers) / sizeof(char*); i++)
{
string strOutlierFile = string(cvtest::TS::ptr()->get_data_path()) + "shared/" + outliers[i];
cv::Mat imgOutlier = cv::imread(strOutlierFile, 0);
if (imgQuery.empty())
{
ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", strOutlierFile.c_str());
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
return;
}
imgOutliers.push_back(imgOutlier);
}
// extract and compute keypoints and descriptors from query image
#if CV_MAJOR_VERSION==2
(*fe)(imgQuery, cv::Mat(), query_kp, query_desc);
#elif CV_MAJOR_VERSION==3
fe->detectAndCompute(imgQuery, Mat(), query_kp, query_desc);
#endif
// extract and compute keypoints and descriptors from outlier images
fe->detect(imgOutliers, outliers_kp);
((cv::DescriptorExtractor*)fe)->compute(imgOutliers, outliers_kp, outliers_desc);
totalOutlierDescCnt = 0;
for (unsigned int i = 0; i < outliers_desc.size(); ++i) totalOutlierDescCnt += outliers_desc[i].rows;
if (SHOW_DEBUG_LOG)
{
cout << query_kp.size() << " keypoints extracted from query image." << endl;
#if defined(TRAIN_WITH_OUTLIER_IMAGES)
cout << totalOutlierDescCnt << " keypoints extracted from outlier image(s)." << endl;
#endif
}
// compute brute force matcher accuracy for reference
double totalTrainTime = 0.;
double totalMatchTime = 0.;
double totalAccuracy = 0.;
int cnt = 0;
for (float t = tp->from; t <= tp->to; t += tp->step, ++testno_for_make_filename, ++cnt)
{
if (SHOW_DEBUG_LOG) cout << "Test No." << testno_for_make_filename << " BFMatcher " << t;
TrainInfo ti = transImgAndTrain(fe, bfmatcher, "BFMatcher",
imgQuery, query_kp, query_desc,
imgOutliers, outliers_kp, outliers_desc,
totalOutlierDescCnt,
t, tp, testno_for_make_filename, SHOW_DEBUG_LOG, SAVE_DRAW_MATCHES_IMAGES);
totalTrainTime += ti.traintime;
totalMatchTime += ti.matchtime;
totalAccuracy += ti.accuracy;
}
double bf_average_accuracy = totalAccuracy / cnt;
if (SHOW_DEBUG_LOG)
{
cout << "total training time: " << totalTrainTime << "ms" << endl;
cout << "total matching time: " << totalMatchTime << "ms" << endl;
cout << "average accuracy:" << bf_average_accuracy << "%" << endl;
}
// test the target matcher
totalTrainTime = 0.;
totalMatchTime = 0.;
totalAccuracy = 0.;
cnt = 0;
for (float t = tp->from; t <= tp->to; t += tp->step, ++testno_for_make_filename, ++cnt)
{
if (SHOW_DEBUG_LOG) cout << "Test No." << testno_for_make_filename << " " << flmatchername << " " << t;
TrainInfo ti = transImgAndTrain(fe, flmatcher, flmatchername,
imgQuery, query_kp, query_desc,
imgOutliers, outliers_kp, outliers_desc,
totalOutlierDescCnt,
t, tp, testno_for_make_filename, SHOW_DEBUG_LOG, SAVE_DRAW_MATCHES_IMAGES);
totalTrainTime += ti.traintime;
totalMatchTime += ti.matchtime;
totalAccuracy += ti.accuracy;
}
double average_accuracy = totalAccuracy / cnt;
double target_average_accuracy = bf_average_accuracy * target_accuracy_margin_from_bfmatcher;
if (SHOW_DEBUG_LOG)
{
cout << "total training time: " << totalTrainTime << "ms" << endl;
cout << "total matching time: " << totalMatchTime << "ms" << endl;
cout << "average accuracy:" << average_accuracy << "%" << endl;
cout << "threshold of the target matcher average accuracy as error :" << target_average_accuracy << "%" << endl;
cout << "accuracy degraded " << (100.0 - (average_accuracy / bf_average_accuracy *100.0)) << "% from BFMatcher.(lower percentage is better)" << endl;
}
// compare accuracies between the brute force matcher and the test target matcher
if (average_accuracy < target_average_accuracy)
{
ts->printf(cvtest::TS::LOG, "Bad average accuracy %f < %f while test %s %s query\n", average_accuracy, target_average_accuracy, flmatchername.c_str(), tp->transname.c_str());
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
}
return;
}
};
// --------------------------------------------------------------------------------------
// Transform Functions
// --------------------------------------------------------------------------------------
static void rotate(float deg, const cv::Mat& src, cv::Mat& dst)
{
cv::warpAffine(src, dst, getRotationMatrix2D(cv::Point2f(src.cols / 2.0f, src.rows / 2.0f), deg, 1), src.size(), cv::INTER_CUBIC);
}
static void scale(float scale, const cv::Mat& src, cv::Mat& dst)
{
cv::resize(src, dst, cv::Size((int)(src.cols*scale), (int)(src.rows*scale)), cv::INTER_CUBIC);
}
static void blur(float k, const cv::Mat& src, cv::Mat& dst)
{
GaussianBlur(src, dst, cv::Size((int)k, (int)k), 0);
}
// --------------------------------------------------------------------------------------
// Tests Registrations
// --------------------------------------------------------------------------------------
#define SHORT_LSH_KEY_ACCURACY_MARGIN 0.72 // The margin for FlannBasedMatcher. 28% degraded from BFMatcher(Actually, about 10..24% measured.lower percentage is better.) for lsh key size=16.
#define MIDDLE_LSH_KEY_ACCURACY_MARGIN 0.72 // The margin for FlannBasedMatcher. 28% degraded from BFMatcher(Actually, about 7..24% measured.lower percentage is better.) for lsh key size=24.
#define LONG_LSH_KEY_ACCURACY_MARGIN 0.90 // The margin for FlannBasedMatcher. 10% degraded from BFMatcher(Actually, about -29...7% measured.lower percentage is better.) for lsh key size=31.
TEST(BlurredQueryFlannBasedLshShortKeyMatcherAdditionalTrainTest, accuracy)
{
cv::Ptr<cv::Feature2D> fe = OrbCreate;
cv::Ptr<cv::FlannBasedMatcher> fl = cv::makePtr<cv::FlannBasedMatcher>(cv::makePtr<cv::flann::LshIndexParams>(1, 16, 2));
testparam tp("blurred", blur, 1.0f, 11.0f, 2.0f);
CV_FeatureDetectorMatcherBaseTest test(&tp, SHORT_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 16, 2)", cv::NORM_HAMMING);
test.safe_run();
}
TEST(BlurredQueryFlannBasedLshMiddleKeyMatcherAdditionalTrainTest, accuracy)
{
cv::Ptr<cv::Feature2D> fe = OrbCreate;
cv::Ptr<cv::FlannBasedMatcher> fl = cv::makePtr<cv::FlannBasedMatcher>(cv::makePtr<cv::flann::LshIndexParams>(1, 24, 2));
testparam tp("blurred", blur, 1.0f, 11.0f, 2.0f);
CV_FeatureDetectorMatcherBaseTest test(&tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 24, 2)", cv::NORM_HAMMING);
test.safe_run();
}
TEST(BlurredQueryFlannBasedLshLongKeyMatcherAdditionalTrainTest, accuracy)
{
cv::Ptr<cv::Feature2D> fe = OrbCreate;
cv::Ptr<cv::FlannBasedMatcher> fl = cv::makePtr<cv::FlannBasedMatcher>(cv::makePtr<cv::flann::LshIndexParams>(1, 31, 2));
testparam tp("blurred", blur, 1.0f, 11.0f, 2.0f);
CV_FeatureDetectorMatcherBaseTest test(&tp, LONG_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 31, 2)", cv::NORM_HAMMING);
test.safe_run();
}
TEST(ScaledQueryFlannBasedLshShortKeyMatcherAdditionalTrainTest, accuracy)
{
cv::Ptr<cv::Feature2D> fe = OrbCreate;
cv::Ptr<cv::FlannBasedMatcher> fl = cv::makePtr<cv::FlannBasedMatcher>(cv::makePtr<cv::flann::LshIndexParams>(1, 16, 2));
testparam tp("scaled", scale, 0.5f, 1.5f, 0.1f);
CV_FeatureDetectorMatcherBaseTest test(&tp, SHORT_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 16, 2)", cv::NORM_HAMMING);
test.safe_run();
}
TEST(ScaledQueryFlannBasedLshMiddleKeyMatcherAdditionalTrainTest, accuracy)
{
cv::Ptr<cv::Feature2D> fe = OrbCreate;
cv::Ptr<cv::FlannBasedMatcher> fl = cv::makePtr<cv::FlannBasedMatcher>(cv::makePtr<cv::flann::LshIndexParams>(1, 24, 2));
testparam tp("scaled", scale, 0.5f, 1.5f, 0.1f);
CV_FeatureDetectorMatcherBaseTest test(&tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 24, 2)", cv::NORM_HAMMING);
test.safe_run();
}
TEST(ScaledQueryFlannBasedLshLongKeyMatcherAdditionalTrainTest, accuracy)
{
cv::Ptr<cv::Feature2D> fe = OrbCreate;
cv::Ptr<cv::FlannBasedMatcher> fl = cv::makePtr<cv::FlannBasedMatcher>(cv::makePtr<cv::flann::LshIndexParams>(1, 31, 2));
testparam tp("scaled", scale, 0.5f, 1.5f, 0.1f);
CV_FeatureDetectorMatcherBaseTest test(&tp, LONG_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 31, 2)", cv::NORM_HAMMING);
test.safe_run();
}
TEST(RotatedQueryFlannBasedLshShortKeyMatcherAdditionalTrainTest, accuracy)
{
cv::Ptr<cv::Feature2D> fe = OrbCreate;
cv::Ptr<cv::FlannBasedMatcher> fl = cv::makePtr<cv::FlannBasedMatcher>(cv::makePtr<cv::flann::LshIndexParams>(1, 16, 2));
testparam tp("rotated", rotate, 0.0f, 359.0f, 30.0f);
CV_FeatureDetectorMatcherBaseTest test(&tp, SHORT_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 16, 2)", cv::NORM_HAMMING);
test.safe_run();
}
TEST(RotatedQueryFlannBasedLshMiddleKeyMatcherAdditionalTrainTest, accuracy)
{
cv::Ptr<cv::Feature2D> fe = OrbCreate;
cv::Ptr<cv::FlannBasedMatcher> fl = cv::makePtr<cv::FlannBasedMatcher>(cv::makePtr<cv::flann::LshIndexParams>(1, 24, 2));
testparam tp("rotated", rotate, 0.0f, 359.0f, 30.0f);
CV_FeatureDetectorMatcherBaseTest test(&tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 24, 2)", cv::NORM_HAMMING);
test.safe_run();
}
TEST(RotatedQueryFlannBasedLshLongKeyMatcherAdditionalTrainTest, accuracy)
{
cv::Ptr<cv::Feature2D> fe = OrbCreate;
cv::Ptr<cv::FlannBasedMatcher> fl = cv::makePtr<cv::FlannBasedMatcher>(cv::makePtr<cv::flann::LshIndexParams>(1, 31, 2));
testparam tp("rotated", rotate, 0.0f, 359.0f, 30.0f);
CV_FeatureDetectorMatcherBaseTest test(&tp, LONG_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 31, 2)", cv::NORM_HAMMING);
test.safe_run();
}

@ -14,6 +14,7 @@
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include <iostream>
#endif

@ -94,6 +94,13 @@ public:
}
}
/**
* Dummy implementation for other algorithms of addable indexes after that.
*/
void addIndex(const Matrix<ElementType>& /*wholeData*/, const Matrix<ElementType>& /*additionalData*/)
{
}
/**
* Method responsible with building the index.
*/

@ -130,6 +130,13 @@ public:
return kmeans_index_->usedMemory() + kdtree_index_->usedMemory();
}
/**
* Dummy implementation for other algorithms of addable indexes after that.
*/
void addIndex(const Matrix<ElementType>& /*wholeData*/, const Matrix<ElementType>& /*additionalData*/)
{
}
/**
* \brief Builds the index
*/

@ -124,6 +124,16 @@ public:
delete nnIndex_;
}
/**
* implementation for algorithms of addable indexes after that.
*/
void addIndex(const Matrix<ElementType>& wholeData, const Matrix<ElementType>& additionalData)
{
if (!loaded_) {
nnIndex_->addIndex(wholeData, additionalData);
}
}
/**
* Builds the index.
*/

@ -378,6 +378,14 @@ public:
return pool.usedMemory+pool.wastedMemory+memoryCounter;
}
/**
* Dummy implementation for other algorithms of addable indexes after that.
*/
void addIndex(const Matrix<ElementType>& /*wholeData*/, const Matrix<ElementType>& /*additionalData*/)
{
}
/**
* Builds the index
*/

@ -117,6 +117,13 @@ public:
delete[] var_;
}
/**
* Dummy implementation for other algorithms of addable indexes after that.
*/
void addIndex(const Matrix<ElementType>& /*wholeData*/, const Matrix<ElementType>& /*additionalData*/)
{
}
/**
* Builds the index
*/

@ -110,6 +110,13 @@ public:
if (reorder_) delete[] data_.data;
}
/**
* Dummy implementation for other algorithms of addable indexes after that.
*/
void addIndex(const Matrix<ElementType>& /*wholeData*/, const Matrix<ElementType>& /*additionalData*/)
{
}
/**
* Builds the index
*/

@ -362,6 +362,13 @@ public:
return pool_.usedMemory+pool_.wastedMemory+memoryCounter_;
}
/**
* Dummy implementation for other algorithms of addable indexes after that.
*/
void addIndex(const Matrix<ElementType>& /*wholeData*/, const Matrix<ElementType>& /*additionalData*/)
{
}
/**
* Builds the index
*/

@ -85,6 +85,13 @@ public:
return 0;
}
/**
* Dummy implementation for other algorithms of addable indexes after that.
*/
void addIndex(const Matrix<ElementType>& /*wholeData*/, const Matrix<ElementType>& /*additionalData*/)
{
}
void buildIndex()
{
/* nothing to do here for linear search */

@ -104,6 +104,22 @@ public:
LshIndex(const LshIndex&);
LshIndex& operator=(const LshIndex&);
/**
* Implementation for the LSH addable indexes after that.
* @param wholeData whole dataset with the input features
* @param additionalData additional dataset with the input features
*/
void addIndex(const Matrix<ElementType>& wholeData, const Matrix<ElementType>& additionalData)
{
tables_.resize(table_number_);
for (unsigned int i = 0; i < table_number_; ++i) {
lsh::LshTable<ElementType>& table = tables_[i];
// Add the features to the table with indexed offset
table.add((int)(wholeData.rows - additionalData.rows), additionalData);
}
dataset_ = wholeData;
}
/**
* Builds the index
*/
@ -126,8 +142,8 @@ public:
lsh::LshTable<ElementType>& table = tables_[i];
table = lsh::LshTable<ElementType>(feature_size_, key_size_, indices);
// Add the features to the table
table.add(dataset_);
// Add the features to the table with offset 0
table.add(0, dataset_);
}
}

@ -192,15 +192,16 @@ public:
}
/** Add a set of features to the table
* @param indexed_ofst previous indexed offset
* @param dataset the values to store
*/
void add(Matrix<ElementType> dataset)
void add(int indexed_ofst, Matrix<ElementType> dataset)
{
#if USE_UNORDERED_MAP
buckets_space_.rehash((buckets_space_.size() + dataset.rows) * 1.2);
#endif
// Add the features to the table
for (unsigned int i = 0; i < dataset.rows; ++i) add(i, dataset[i]);
for (unsigned int i = 0; i < dataset.rows; ++i) add(i + indexed_ofst, dataset[i]);
// Now that the table is full, optimize it for speed/space
optimize();
}

@ -134,7 +134,8 @@ public:
CV_WRAP Index(InputArray features, const IndexParams& params, cvflann::flann_distance_t distType=cvflann::FLANN_DIST_L2);
virtual ~Index();
CV_WRAP virtual void build(InputArray features, const IndexParams& params, cvflann::flann_distance_t distType=cvflann::FLANN_DIST_L2);
CV_WRAP virtual void build(InputArray wholefeatures, InputArray additionalfeatures, const IndexParams& params, cvflann::flann_distance_t distType=cvflann::FLANN_DIST_L2);
CV_WRAP virtual void knnSearch(InputArray query, OutputArray indices,
OutputArray dists, int knn, const SearchParams& params=SearchParams());

@ -59,6 +59,11 @@ public:
*/
virtual void buildIndex() = 0;
/**
* \brief implementation for algorithms of addable indexes after that.
*/
virtual void addIndex(const Matrix<ElementType>& wholeData, const Matrix<ElementType>& additionalData) = 0;
/**
* \brief Perform k-nearest neighbor search
* \param[in] queries The query points for which to find the nearest neighbors

@ -308,7 +308,7 @@ SearchParams::SearchParams( int checks, float eps, bool sorted )
template<typename Distance, typename IndexType> void
buildIndex_(void*& index, const Mat& data, const IndexParams& params, const Distance& dist = Distance())
buildIndex_(void*& index, const Mat& wholedata, const Mat& data, const IndexParams& params, const Distance& dist = Distance())
{
typedef typename Distance::ElementType ElementType;
if(DataType<ElementType>::type != data.type())
@ -317,15 +317,25 @@ buildIndex_(void*& index, const Mat& data, const IndexParams& params, const Dist
CV_Error(CV_StsBadArg, "Only continuous arrays are supported");
::cvflann::Matrix<ElementType> dataset((ElementType*)data.data, data.rows, data.cols);
IndexType* _index = new IndexType(dataset, get_params(params), dist);
_index->buildIndex();
index = _index;
IndexType* _index = NULL;
if( !index || getParam<flann_algorithm_t>(params, "algorithm", FLANN_INDEX_LINEAR) != FLANN_INDEX_LSH) // currently, additional index support is the lsh algorithm only.
{
_index = new IndexType(dataset, get_params(params), dist);
_index->buildIndex();
index = _index;
}
else // build additional lsh index
{
::cvflann::Matrix<ElementType> wholedataset((ElementType*)wholedata.data, wholedata.rows, wholedata.cols);
((IndexType*)index)->addIndex(wholedataset, dataset);
}
}
template<typename Distance> void
buildIndex(void*& index, const Mat& data, const IndexParams& params, const Distance& dist = Distance())
buildIndex(void*& index, const Mat& wholedata, const Mat& data, const IndexParams& params, const Distance& dist = Distance())
{
buildIndex_<Distance, ::cvflann::Index<Distance> >(index, data, params, dist);
buildIndex_<Distance, ::cvflann::Index<Distance> >(index, wholedata, data, params, dist);
}
#if CV_NEON
@ -348,21 +358,28 @@ Index::Index(InputArray _data, const IndexParams& params, flann_distance_t _dist
featureType = CV_32F;
algo = FLANN_INDEX_LINEAR;
distType = FLANN_DIST_L2;
build(_data, params, _distType);
build(_data, _data, params, _distType);
}
void Index::build(InputArray _data, const IndexParams& params, flann_distance_t _distType)
void Index::build(InputArray _wholedata, InputArray _data, const IndexParams& params, flann_distance_t _distType)
{
release();
algo = getParam<flann_algorithm_t>(params, "algorithm", FLANN_INDEX_LINEAR);
if( algo == FLANN_INDEX_SAVED )
if (algo != FLANN_INDEX_LSH) // do not release if algo == FLANN_INDEX_LSH
{
release();
}
if (algo == FLANN_INDEX_SAVED)
{
load(_data, getParam<std::string>(params, "filename", std::string()));
return;
}
Mat data = _data.getMat();
index = 0;
if (algo != FLANN_INDEX_LSH) // do not clear if algo == FLANN_INDEX_LSH
{
index = 0;
}
featureType = data.type();
distType = _distType;
@ -374,29 +391,29 @@ void Index::build(InputArray _data, const IndexParams& params, flann_distance_t
switch( distType )
{
case FLANN_DIST_HAMMING:
buildIndex< HammingDistance >(index, data, params);
buildIndex< HammingDistance >(index, _wholedata.getMat(), data, params);
break;
case FLANN_DIST_L2:
buildIndex< ::cvflann::L2<float> >(index, data, params);
buildIndex< ::cvflann::L2<float> >(index, _wholedata.getMat(), data, params);
break;
case FLANN_DIST_L1:
buildIndex< ::cvflann::L1<float> >(index, data, params);
buildIndex< ::cvflann::L1<float> >(index, _wholedata.getMat(), data, params);
break;
#if MINIFLANN_SUPPORT_EXOTIC_DISTANCE_TYPES
case FLANN_DIST_MAX:
buildIndex< ::cvflann::MaxDistance<float> >(index, data, params);
buildIndex< ::cvflann::MaxDistance<float> >(index, _wholedata.getMat(), data, params);
break;
case FLANN_DIST_HIST_INTERSECT:
buildIndex< ::cvflann::HistIntersectionDistance<float> >(index, data, params);
buildIndex< ::cvflann::HistIntersectionDistance<float> >(index, _wholedata.getMat(), data, params);
break;
case FLANN_DIST_HELLINGER:
buildIndex< ::cvflann::HellingerDistance<float> >(index, data, params);
buildIndex< ::cvflann::HellingerDistance<float> >(index, _wholedata.getMat(), data, params);
break;
case FLANN_DIST_CHI_SQUARE:
buildIndex< ::cvflann::ChiSquareDistance<float> >(index, data, params);
buildIndex< ::cvflann::ChiSquareDistance<float> >(index, _wholedata.getMat(), data, params);
break;
case FLANN_DIST_KL:
buildIndex< ::cvflann::KL_Divergence<float> >(index, data, params);
buildIndex< ::cvflann::KL_Divergence<float> >(index, _wholedata.getMat(), data, params);
break;
#endif
default:

@ -9,7 +9,7 @@ set(OPENCV_MODULE_IS_PART_OF_WORLD FALSE)
ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef)
ocv_add_module(ts opencv_core opencv_features2d opencv_highgui opencv_imgproc opencv_video)
ocv_add_module(ts opencv_core opencv_features2d opencv_highgui opencv_imgproc opencv_video opencv_calib3d)
ocv_glob_module_sources()
ocv_module_include_directories()

Loading…
Cancel
Save