123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635 |
- /*M///////////////////////////////////////////////////////////////////////////////////////
- //
- // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
- //
- // By downloading, copying, installing or using the software you agree to this license.
- // If you do not agree to this license, do not download, install,
- // copy or use the software.
- //
- //
- // Intel License Agreement
- // For Open Source Computer Vision Library
- //
- // Copyright (C) 2000, Intel Corporation, all rights reserved.
- // Third party copyrights are property of their respective owners.
- //
- // Redistribution and use in source and binary forms, with or without modification,
- // are permitted provided that the following conditions are met:
- //
- // * Redistribution's of source code must retain the above copyright notice,
- // this list of conditions and the following disclaimer.
- //
- // * Redistribution's 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.
- //
- // * The name of Intel Corporation may not 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 Intel Corporation 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.
- //
- //M*/
- #include "test_precomp.hpp"
- namespace opencv_test { namespace {
- const string FEATURES2D_DIR = "features2d";
- const string IMAGE_FILENAME = "tsukuba.png";
- /****************************************************************************************\
- * Algorithmic tests for descriptor matchers *
- \****************************************************************************************/
- class CV_DescriptorMatcherTest : public cvtest::BaseTest
- {
- public:
- CV_DescriptorMatcherTest( const string& _name, const Ptr<DescriptorMatcher>& _dmatcher, float _badPart ) :
- badPart(_badPart), name(_name), dmatcher(_dmatcher)
- {}
- protected:
- static const int dim = 500;
- static const int queryDescCount = 300; // must be even number because we split train data in some cases in two
- static const int countFactor = 4; // do not change it
- const float badPart;
- virtual void run( int );
- void generateData( Mat& query, Mat& train );
- #if 0
- void emptyDataTest(); // FIXIT not used
- #endif
- void matchTest( const Mat& query, const Mat& train );
- void knnMatchTest( const Mat& query, const Mat& train );
- void radiusMatchTest( const Mat& query, const Mat& train );
- string name;
- Ptr<DescriptorMatcher> dmatcher;
- private:
- CV_DescriptorMatcherTest& operator=(const CV_DescriptorMatcherTest&) { return *this; }
- };
- #if 0
- void CV_DescriptorMatcherTest::emptyDataTest()
- {
- assert( !dmatcher.empty() );
- Mat queryDescriptors, trainDescriptors, mask;
- vector<Mat> trainDescriptorCollection, masks;
- vector<DMatch> matches;
- vector<vector<DMatch> > vmatches;
- try
- {
- dmatcher->match( queryDescriptors, trainDescriptors, matches, mask );
- }
- catch(...)
- {
- ts->printf( cvtest::TS::LOG, "match() on empty descriptors must not generate exception (1).\n" );
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- try
- {
- dmatcher->knnMatch( queryDescriptors, trainDescriptors, vmatches, 2, mask );
- }
- catch(...)
- {
- ts->printf( cvtest::TS::LOG, "knnMatch() on empty descriptors must not generate exception (1).\n" );
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- try
- {
- dmatcher->radiusMatch( queryDescriptors, trainDescriptors, vmatches, 10.f, mask );
- }
- catch(...)
- {
- ts->printf( cvtest::TS::LOG, "radiusMatch() on empty descriptors must not generate exception (1).\n" );
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- try
- {
- dmatcher->add( trainDescriptorCollection );
- }
- catch(...)
- {
- ts->printf( cvtest::TS::LOG, "add() on empty descriptors must not generate exception.\n" );
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- try
- {
- dmatcher->match( queryDescriptors, matches, masks );
- }
- catch(...)
- {
- ts->printf( cvtest::TS::LOG, "match() on empty descriptors must not generate exception (2).\n" );
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- try
- {
- dmatcher->knnMatch( queryDescriptors, vmatches, 2, masks );
- }
- catch(...)
- {
- ts->printf( cvtest::TS::LOG, "knnMatch() on empty descriptors must not generate exception (2).\n" );
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- try
- {
- dmatcher->radiusMatch( queryDescriptors, vmatches, 10.f, masks );
- }
- catch(...)
- {
- ts->printf( cvtest::TS::LOG, "radiusMatch() on empty descriptors must not generate exception (2).\n" );
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- }
- #endif
- void CV_DescriptorMatcherTest::generateData( Mat& query, Mat& train )
- {
- RNG& rng = theRNG();
- // Generate query descriptors randomly.
- // Descriptor vector elements are integer values.
- Mat buf( queryDescCount, dim, CV_32SC1 );
- rng.fill( buf, RNG::UNIFORM, Scalar::all(0), Scalar(3) );
- buf.convertTo( query, CV_32FC1 );
- // Generate train descriptors as follows:
- // copy each query descriptor to train set countFactor times
- // and perturb some one element of the copied descriptors in
- // in ascending order. General boundaries of the perturbation
- // are (0.f, 1.f).
- train.create( query.rows*countFactor, query.cols, CV_32FC1 );
- float step = 1.f / countFactor;
- for( int qIdx = 0; qIdx < query.rows; qIdx++ )
- {
- Mat queryDescriptor = query.row(qIdx);
- for( int c = 0; c < countFactor; c++ )
- {
- int tIdx = qIdx * countFactor + c;
- Mat trainDescriptor = train.row(tIdx);
- queryDescriptor.copyTo( trainDescriptor );
- int elem = rng(dim);
- float diff = rng.uniform( step*c, step*(c+1) );
- trainDescriptor.at<float>(0, elem) += diff;
- }
- }
- }
- void CV_DescriptorMatcherTest::matchTest( const Mat& query, const Mat& train )
- {
- dmatcher->clear();
- // test const version of match()
- {
- vector<DMatch> matches;
- dmatcher->match( query, train, matches );
- if( (int)matches.size() != queryDescCount )
- {
- ts->printf(cvtest::TS::LOG, "Incorrect matches count while test match() function (1).\n");
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- else
- {
- int badCount = 0;
- for( size_t i = 0; i < matches.size(); i++ )
- {
- DMatch& match = matches[i];
- if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor) || (match.imgIdx != 0) )
- badCount++;
- }
- if( (float)badCount > (float)queryDescCount*badPart )
- {
- ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test match() function (1).\n",
- (float)badCount/(float)queryDescCount );
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- }
- }
- // test const version of match() for the same query and test descriptors
- {
- vector<DMatch> matches;
- dmatcher->match( query, query, matches );
- if( (int)matches.size() != query.rows )
- {
- ts->printf(cvtest::TS::LOG, "Incorrect matches count while test match() function for the same query and test descriptors (1).\n");
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- else
- {
- for( size_t i = 0; i < matches.size(); i++ )
- {
- DMatch& match = matches[i];
- //std::cout << match.distance << std::endl;
- if( match.queryIdx != (int)i || match.trainIdx != (int)i || std::abs(match.distance) > FLT_EPSILON )
- {
- ts->printf( cvtest::TS::LOG, "Bad match (i=%d, queryIdx=%d, trainIdx=%d, distance=%f) while test match() function for the same query and test descriptors (1).\n",
- i, match.queryIdx, match.trainIdx, match.distance );
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- }
- }
- }
- // test version of match() with add()
- {
- vector<DMatch> matches;
- // make add() twice to test such case
- dmatcher->add( vector<Mat>(1,train.rowRange(0, train.rows/2)) );
- dmatcher->add( vector<Mat>(1,train.rowRange(train.rows/2, train.rows)) );
- // prepare masks (make first nearest match illegal)
- vector<Mat> masks(2);
- for(int mi = 0; mi < 2; mi++ )
- {
- masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1));
- for( int di = 0; di < queryDescCount/2; di++ )
- masks[mi].col(di*countFactor).setTo(Scalar::all(0));
- }
- dmatcher->match( query, matches, masks );
- if( (int)matches.size() != queryDescCount )
- {
- ts->printf(cvtest::TS::LOG, "Incorrect matches count while test match() function (2).\n");
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- else
- {
- int badCount = 0;
- for( size_t i = 0; i < matches.size(); i++ )
- {
- DMatch& match = matches[i];
- int shift = dmatcher->isMaskSupported() ? 1 : 0;
- {
- if( i < queryDescCount/2 )
- {
- if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + shift) || (match.imgIdx != 0) )
- badCount++;
- }
- else
- {
- if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + shift) || (match.imgIdx != 1) )
- badCount++;
- }
- }
- }
- if( (float)badCount > (float)queryDescCount*badPart )
- {
- ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test match() function (2).\n",
- (float)badCount/(float)queryDescCount );
- ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
- }
- }
- }
- }
- void CV_DescriptorMatcherTest::knnMatchTest( const Mat& query, const Mat& train )
- {
- dmatcher->clear();
- // test const version of knnMatch()
- {
- const int knn = 3;
- vector<vector<DMatch> > matches;
- dmatcher->knnMatch( query, train, matches, knn );
- if( (int)matches.size() != queryDescCount )
- {
- ts->printf(cvtest::TS::LOG, "Incorrect matches count while test knnMatch() function (1).\n");
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- else
- {
- int badCount = 0;
- for( size_t i = 0; i < matches.size(); i++ )
- {
- if( (int)matches[i].size() != knn )
- badCount++;
- else
- {
- int localBadCount = 0;
- for( int k = 0; k < knn; k++ )
- {
- DMatch& match = matches[i][k];
- if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor+k) || (match.imgIdx != 0) )
- localBadCount++;
- }
- badCount += localBadCount > 0 ? 1 : 0;
- }
- }
- if( (float)badCount > (float)queryDescCount*badPart )
- {
- ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test knnMatch() function (1).\n",
- (float)badCount/(float)queryDescCount );
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- }
- }
- // test version of knnMatch() with add()
- {
- const int knn = 2;
- vector<vector<DMatch> > matches;
- // make add() twice to test such case
- dmatcher->add( vector<Mat>(1,train.rowRange(0, train.rows/2)) );
- dmatcher->add( vector<Mat>(1,train.rowRange(train.rows/2, train.rows)) );
- // prepare masks (make first nearest match illegal)
- vector<Mat> masks(2);
- for(int mi = 0; mi < 2; mi++ )
- {
- masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1));
- for( int di = 0; di < queryDescCount/2; di++ )
- masks[mi].col(di*countFactor).setTo(Scalar::all(0));
- }
- dmatcher->knnMatch( query, matches, knn, masks );
- if( (int)matches.size() != queryDescCount )
- {
- ts->printf(cvtest::TS::LOG, "Incorrect matches count while test knnMatch() function (2).\n");
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- else
- {
- int badCount = 0;
- int shift = dmatcher->isMaskSupported() ? 1 : 0;
- for( size_t i = 0; i < matches.size(); i++ )
- {
- if( (int)matches[i].size() != knn )
- badCount++;
- else
- {
- int localBadCount = 0;
- for( int k = 0; k < knn; k++ )
- {
- DMatch& match = matches[i][k];
- {
- if( i < queryDescCount/2 )
- {
- if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + k + shift) ||
- (match.imgIdx != 0) )
- localBadCount++;
- }
- else
- {
- if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + k + shift) ||
- (match.imgIdx != 1) )
- localBadCount++;
- }
- }
- }
- badCount += localBadCount > 0 ? 1 : 0;
- }
- }
- if( (float)badCount > (float)queryDescCount*badPart )
- {
- ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test knnMatch() function (2).\n",
- (float)badCount/(float)queryDescCount );
- ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
- }
- }
- }
- }
- void CV_DescriptorMatcherTest::radiusMatchTest( const Mat& query, const Mat& train )
- {
- dmatcher->clear();
- // test const version of match()
- {
- const float radius = 1.f/countFactor;
- vector<vector<DMatch> > matches;
- dmatcher->radiusMatch( query, train, matches, radius );
- if( (int)matches.size() != queryDescCount )
- {
- ts->printf(cvtest::TS::LOG, "Incorrect matches count while test radiusMatch() function (1).\n");
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- else
- {
- int badCount = 0;
- for( size_t i = 0; i < matches.size(); i++ )
- {
- if( (int)matches[i].size() != 1 )
- badCount++;
- else
- {
- DMatch& match = matches[i][0];
- if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor) || (match.imgIdx != 0) )
- badCount++;
- }
- }
- if( (float)badCount > (float)queryDescCount*badPart )
- {
- ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test radiusMatch() function (1).\n",
- (float)badCount/(float)queryDescCount );
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- }
- }
- // test version of match() with add()
- {
- int n = 3;
- const float radius = 1.f/countFactor * n;
- vector<vector<DMatch> > matches;
- // make add() twice to test such case
- dmatcher->add( vector<Mat>(1,train.rowRange(0, train.rows/2)) );
- dmatcher->add( vector<Mat>(1,train.rowRange(train.rows/2, train.rows)) );
- // prepare masks (make first nearest match illegal)
- vector<Mat> masks(2);
- for(int mi = 0; mi < 2; mi++ )
- {
- masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1));
- for( int di = 0; di < queryDescCount/2; di++ )
- masks[mi].col(di*countFactor).setTo(Scalar::all(0));
- }
- dmatcher->radiusMatch( query, matches, radius, masks );
- //int curRes = cvtest::TS::OK;
- if( (int)matches.size() != queryDescCount )
- {
- ts->printf(cvtest::TS::LOG, "Incorrect matches count while test radiusMatch() function (1).\n");
- ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
- }
- int badCount = 0;
- int shift = dmatcher->isMaskSupported() ? 1 : 0;
- int needMatchCount = dmatcher->isMaskSupported() ? n-1 : n;
- for( size_t i = 0; i < matches.size(); i++ )
- {
- if( (int)matches[i].size() != needMatchCount )
- badCount++;
- else
- {
- int localBadCount = 0;
- for( int k = 0; k < needMatchCount; k++ )
- {
- DMatch& match = matches[i][k];
- {
- if( i < queryDescCount/2 )
- {
- if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + k + shift) ||
- (match.imgIdx != 0) )
- localBadCount++;
- }
- else
- {
- if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + k + shift) ||
- (match.imgIdx != 1) )
- localBadCount++;
- }
- }
- }
- badCount += localBadCount > 0 ? 1 : 0;
- }
- }
- if( (float)badCount > (float)queryDescCount*badPart )
- {
- //curRes = cvtest::TS::FAIL_INVALID_OUTPUT;
- ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test radiusMatch() function (2).\n",
- (float)badCount/(float)queryDescCount );
- ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
- }
- }
- }
- void CV_DescriptorMatcherTest::run( int )
- {
- Mat query, train;
- generateData( query, train );
- matchTest( query, train );
- knnMatchTest( query, train );
- radiusMatchTest( query, train );
- }
- /****************************************************************************************\
- * Tests registrations *
- \****************************************************************************************/
- TEST( Features2d_DescriptorMatcher_BruteForce, regression )
- {
- CV_DescriptorMatcherTest test( "descriptor-matcher-brute-force",
- DescriptorMatcher::create("BruteForce"), 0.01f );
- test.safe_run();
- }
- #ifdef HAVE_OPENCV_FLANN
- TEST( Features2d_DescriptorMatcher_FlannBased, regression )
- {
- CV_DescriptorMatcherTest test( "descriptor-matcher-flann-based",
- DescriptorMatcher::create("FlannBased"), 0.04f );
- test.safe_run();
- }
- #endif
- TEST( Features2d_DMatch, read_write )
- {
- FileStorage fs(".xml", FileStorage::WRITE + FileStorage::MEMORY);
- vector<DMatch> matches;
- matches.push_back(DMatch(1,2,3,4.5f));
- fs << "Match" << matches;
- String str = fs.releaseAndGetString();
- ASSERT_NE( strstr(str.c_str(), "4.5"), (char*)0 );
- }
- #ifdef HAVE_OPENCV_FLANN
- TEST( Features2d_FlannBasedMatcher, read_write )
- {
- static const char* ymlfile = "%YAML:1.0\n---\n"
- "format: 3\n"
- "indexParams:\n"
- " -\n"
- " name: algorithm\n"
- " type: 9\n" // FLANN_INDEX_TYPE_ALGORITHM
- " value: 6\n"// this line is changed!
- " -\n"
- " name: trees\n"
- " type: 4\n"
- " value: 4\n"
- "searchParams:\n"
- " -\n"
- " name: checks\n"
- " type: 4\n"
- " value: 32\n"
- " -\n"
- " name: eps\n"
- " type: 5\n"
- " value: 4.\n"// this line is changed!
- " -\n"
- " name: explore_all_trees\n"
- " type: 8\n"
- " value: 0\n"
- " -\n"
- " name: sorted\n"
- " type: 8\n" // FLANN_INDEX_TYPE_BOOL
- " value: 1\n";
- Ptr<DescriptorMatcher> matcher = FlannBasedMatcher::create();
- FileStorage fs_in(ymlfile, FileStorage::READ + FileStorage::MEMORY);
- matcher->read(fs_in.root());
- FileStorage fs_out(".yml", FileStorage::WRITE + FileStorage::MEMORY);
- matcher->write(fs_out);
- std::string out = fs_out.releaseAndGetString();
- EXPECT_EQ(ymlfile, out);
- }
- #endif
- TEST(Features2d_DMatch, issue_11855)
- {
- Mat sources = (Mat_<uchar>(2, 3) << 1, 1, 0,
- 1, 1, 1);
- Mat targets = (Mat_<uchar>(2, 3) << 1, 1, 1,
- 0, 0, 0);
- Ptr<BFMatcher> bf = BFMatcher::create(NORM_HAMMING, true);
- vector<vector<DMatch> > match;
- bf->knnMatch(sources, targets, match, 1, noArray(), true);
- ASSERT_EQ((size_t)1, match.size());
- ASSERT_EQ((size_t)1, match[0].size());
- EXPECT_EQ(1, match[0][0].queryIdx);
- EXPECT_EQ(0, match[0][0].trainIdx);
- EXPECT_EQ(0.0f, match[0][0].distance);
- }
- TEST(Features2d_DMatch, issue_17771)
- {
- Mat sources = (Mat_<uchar>(2, 3) << 1, 1, 0,
- 1, 1, 1);
- Mat targets = (Mat_<uchar>(2, 3) << 1, 1, 1,
- 0, 0, 0);
- UMat usources = sources.getUMat(ACCESS_READ);
- UMat utargets = targets.getUMat(ACCESS_READ);
- vector<vector<DMatch> > match;
- Ptr<BFMatcher> ubf = BFMatcher::create(NORM_HAMMING);
- Mat mask = (Mat_<uchar>(2, 2) << 1, 0, 0, 1);
- EXPECT_NO_THROW(ubf->knnMatch(usources, utargets, match, 1, mask, true));
- }
- }} // namespace
|