Path: blob/master/modules/features2d/test/test_matchers_algorithmic.cpp
16354 views
/*M///////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// Intel License Agreement10// For Open Source Computer Vision Library11//12// Copyright (C) 2000, Intel Corporation, all rights reserved.13// Third party copyrights are property of their respective owners.14//15// Redistribution and use in source and binary forms, with or without modification,16// are permitted provided that the following conditions are met:17//18// * Redistribution's of source code must retain the above copyright notice,19// this list of conditions and the following disclaimer.20//21// * Redistribution's in binary form must reproduce the above copyright notice,22// this list of conditions and the following disclaimer in the documentation23// and/or other materials provided with the distribution.24//25// * The name of Intel Corporation may not be used to endorse or promote products26// derived from this software without specific prior written permission.27//28// This software is provided by the copyright holders and contributors "as is" and29// any express or implied warranties, including, but not limited to, the implied30// warranties of merchantability and fitness for a particular purpose are disclaimed.31// In no event shall the Intel Corporation or contributors be liable for any direct,32// indirect, incidental, special, exemplary, or consequential damages33// (including, but not limited to, procurement of substitute goods or services;34// loss of use, data, or profits; or business interruption) however caused35// and on any theory of liability, whether in contract, strict liability,36// or tort (including negligence or otherwise) arising in any way out of37// the use of this software, even if advised of the possibility of such damage.38//39//M*/4041#include "test_precomp.hpp"4243namespace opencv_test { namespace {4445const string FEATURES2D_DIR = "features2d";46const string IMAGE_FILENAME = "tsukuba.png";4748/****************************************************************************************\49* Algorithmic tests for descriptor matchers *50\****************************************************************************************/51class CV_DescriptorMatcherTest : public cvtest::BaseTest52{53public:54CV_DescriptorMatcherTest( const string& _name, const Ptr<DescriptorMatcher>& _dmatcher, float _badPart ) :55badPart(_badPart), name(_name), dmatcher(_dmatcher)56{}57protected:58static const int dim = 500;59static const int queryDescCount = 300; // must be even number because we split train data in some cases in two60static const int countFactor = 4; // do not change it61const float badPart;6263virtual void run( int );64void generateData( Mat& query, Mat& train );6566#if 067void emptyDataTest(); // FIXIT not used68#endif69void matchTest( const Mat& query, const Mat& train );70void knnMatchTest( const Mat& query, const Mat& train );71void radiusMatchTest( const Mat& query, const Mat& train );7273string name;74Ptr<DescriptorMatcher> dmatcher;7576private:77CV_DescriptorMatcherTest& operator=(const CV_DescriptorMatcherTest&) { return *this; }78};7980#if 081void CV_DescriptorMatcherTest::emptyDataTest()82{83assert( !dmatcher.empty() );84Mat queryDescriptors, trainDescriptors, mask;85vector<Mat> trainDescriptorCollection, masks;86vector<DMatch> matches;87vector<vector<DMatch> > vmatches;8889try90{91dmatcher->match( queryDescriptors, trainDescriptors, matches, mask );92}93catch(...)94{95ts->printf( cvtest::TS::LOG, "match() on empty descriptors must not generate exception (1).\n" );96ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );97}9899try100{101dmatcher->knnMatch( queryDescriptors, trainDescriptors, vmatches, 2, mask );102}103catch(...)104{105ts->printf( cvtest::TS::LOG, "knnMatch() on empty descriptors must not generate exception (1).\n" );106ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );107}108109try110{111dmatcher->radiusMatch( queryDescriptors, trainDescriptors, vmatches, 10.f, mask );112}113catch(...)114{115ts->printf( cvtest::TS::LOG, "radiusMatch() on empty descriptors must not generate exception (1).\n" );116ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );117}118119try120{121dmatcher->add( trainDescriptorCollection );122}123catch(...)124{125ts->printf( cvtest::TS::LOG, "add() on empty descriptors must not generate exception.\n" );126ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );127}128129try130{131dmatcher->match( queryDescriptors, matches, masks );132}133catch(...)134{135ts->printf( cvtest::TS::LOG, "match() on empty descriptors must not generate exception (2).\n" );136ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );137}138139try140{141dmatcher->knnMatch( queryDescriptors, vmatches, 2, masks );142}143catch(...)144{145ts->printf( cvtest::TS::LOG, "knnMatch() on empty descriptors must not generate exception (2).\n" );146ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );147}148149try150{151dmatcher->radiusMatch( queryDescriptors, vmatches, 10.f, masks );152}153catch(...)154{155ts->printf( cvtest::TS::LOG, "radiusMatch() on empty descriptors must not generate exception (2).\n" );156ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );157}158159}160#endif161162void CV_DescriptorMatcherTest::generateData( Mat& query, Mat& train )163{164RNG& rng = theRNG();165166// Generate query descriptors randomly.167// Descriptor vector elements are integer values.168Mat buf( queryDescCount, dim, CV_32SC1 );169rng.fill( buf, RNG::UNIFORM, Scalar::all(0), Scalar(3) );170buf.convertTo( query, CV_32FC1 );171172// Generate train descriptors as follows:173// copy each query descriptor to train set countFactor times174// and perturb some one element of the copied descriptors in175// in ascending order. General boundaries of the perturbation176// are (0.f, 1.f).177train.create( query.rows*countFactor, query.cols, CV_32FC1 );178float step = 1.f / countFactor;179for( int qIdx = 0; qIdx < query.rows; qIdx++ )180{181Mat queryDescriptor = query.row(qIdx);182for( int c = 0; c < countFactor; c++ )183{184int tIdx = qIdx * countFactor + c;185Mat trainDescriptor = train.row(tIdx);186queryDescriptor.copyTo( trainDescriptor );187int elem = rng(dim);188float diff = rng.uniform( step*c, step*(c+1) );189trainDescriptor.at<float>(0, elem) += diff;190}191}192}193194void CV_DescriptorMatcherTest::matchTest( const Mat& query, const Mat& train )195{196dmatcher->clear();197198// test const version of match()199{200vector<DMatch> matches;201dmatcher->match( query, train, matches );202203if( (int)matches.size() != queryDescCount )204{205ts->printf(cvtest::TS::LOG, "Incorrect matches count while test match() function (1).\n");206ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );207}208else209{210int badCount = 0;211for( size_t i = 0; i < matches.size(); i++ )212{213DMatch& match = matches[i];214if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor) || (match.imgIdx != 0) )215badCount++;216}217if( (float)badCount > (float)queryDescCount*badPart )218{219ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test match() function (1).\n",220(float)badCount/(float)queryDescCount );221ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );222}223}224}225226// test const version of match() for the same query and test descriptors227{228vector<DMatch> matches;229dmatcher->match( query, query, matches );230231if( (int)matches.size() != query.rows )232{233ts->printf(cvtest::TS::LOG, "Incorrect matches count while test match() function for the same query and test descriptors (1).\n");234ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );235}236else237{238for( size_t i = 0; i < matches.size(); i++ )239{240DMatch& match = matches[i];241//std::cout << match.distance << std::endl;242243if( match.queryIdx != (int)i || match.trainIdx != (int)i || std::abs(match.distance) > FLT_EPSILON )244{245ts->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",246i, match.queryIdx, match.trainIdx, match.distance );247ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );248}249}250}251}252253// test version of match() with add()254{255vector<DMatch> matches;256// make add() twice to test such case257dmatcher->add( vector<Mat>(1,train.rowRange(0, train.rows/2)) );258dmatcher->add( vector<Mat>(1,train.rowRange(train.rows/2, train.rows)) );259// prepare masks (make first nearest match illegal)260vector<Mat> masks(2);261for(int mi = 0; mi < 2; mi++ )262{263masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1));264for( int di = 0; di < queryDescCount/2; di++ )265masks[mi].col(di*countFactor).setTo(Scalar::all(0));266}267268dmatcher->match( query, matches, masks );269270if( (int)matches.size() != queryDescCount )271{272ts->printf(cvtest::TS::LOG, "Incorrect matches count while test match() function (2).\n");273ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );274}275else276{277int badCount = 0;278for( size_t i = 0; i < matches.size(); i++ )279{280DMatch& match = matches[i];281int shift = dmatcher->isMaskSupported() ? 1 : 0;282{283if( i < queryDescCount/2 )284{285if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + shift) || (match.imgIdx != 0) )286badCount++;287}288else289{290if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + shift) || (match.imgIdx != 1) )291badCount++;292}293}294}295if( (float)badCount > (float)queryDescCount*badPart )296{297ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test match() function (2).\n",298(float)badCount/(float)queryDescCount );299ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );300}301}302}303}304305void CV_DescriptorMatcherTest::knnMatchTest( const Mat& query, const Mat& train )306{307dmatcher->clear();308309// test const version of knnMatch()310{311const int knn = 3;312313vector<vector<DMatch> > matches;314dmatcher->knnMatch( query, train, matches, knn );315316if( (int)matches.size() != queryDescCount )317{318ts->printf(cvtest::TS::LOG, "Incorrect matches count while test knnMatch() function (1).\n");319ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );320}321else322{323int badCount = 0;324for( size_t i = 0; i < matches.size(); i++ )325{326if( (int)matches[i].size() != knn )327badCount++;328else329{330int localBadCount = 0;331for( int k = 0; k < knn; k++ )332{333DMatch& match = matches[i][k];334if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor+k) || (match.imgIdx != 0) )335localBadCount++;336}337badCount += localBadCount > 0 ? 1 : 0;338}339}340if( (float)badCount > (float)queryDescCount*badPart )341{342ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test knnMatch() function (1).\n",343(float)badCount/(float)queryDescCount );344ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );345}346}347}348349// test version of knnMatch() with add()350{351const int knn = 2;352vector<vector<DMatch> > matches;353// make add() twice to test such case354dmatcher->add( vector<Mat>(1,train.rowRange(0, train.rows/2)) );355dmatcher->add( vector<Mat>(1,train.rowRange(train.rows/2, train.rows)) );356// prepare masks (make first nearest match illegal)357vector<Mat> masks(2);358for(int mi = 0; mi < 2; mi++ )359{360masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1));361for( int di = 0; di < queryDescCount/2; di++ )362masks[mi].col(di*countFactor).setTo(Scalar::all(0));363}364365dmatcher->knnMatch( query, matches, knn, masks );366367if( (int)matches.size() != queryDescCount )368{369ts->printf(cvtest::TS::LOG, "Incorrect matches count while test knnMatch() function (2).\n");370ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );371}372else373{374int badCount = 0;375int shift = dmatcher->isMaskSupported() ? 1 : 0;376for( size_t i = 0; i < matches.size(); i++ )377{378if( (int)matches[i].size() != knn )379badCount++;380else381{382int localBadCount = 0;383for( int k = 0; k < knn; k++ )384{385DMatch& match = matches[i][k];386{387if( i < queryDescCount/2 )388{389if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + k + shift) ||390(match.imgIdx != 0) )391localBadCount++;392}393else394{395if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + k + shift) ||396(match.imgIdx != 1) )397localBadCount++;398}399}400}401badCount += localBadCount > 0 ? 1 : 0;402}403}404if( (float)badCount > (float)queryDescCount*badPart )405{406ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test knnMatch() function (2).\n",407(float)badCount/(float)queryDescCount );408ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );409}410}411}412}413414void CV_DescriptorMatcherTest::radiusMatchTest( const Mat& query, const Mat& train )415{416dmatcher->clear();417// test const version of match()418{419const float radius = 1.f/countFactor;420vector<vector<DMatch> > matches;421dmatcher->radiusMatch( query, train, matches, radius );422423if( (int)matches.size() != queryDescCount )424{425ts->printf(cvtest::TS::LOG, "Incorrect matches count while test radiusMatch() function (1).\n");426ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );427}428else429{430int badCount = 0;431for( size_t i = 0; i < matches.size(); i++ )432{433if( (int)matches[i].size() != 1 )434badCount++;435else436{437DMatch& match = matches[i][0];438if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor) || (match.imgIdx != 0) )439badCount++;440}441}442if( (float)badCount > (float)queryDescCount*badPart )443{444ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test radiusMatch() function (1).\n",445(float)badCount/(float)queryDescCount );446ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );447}448}449}450451// test version of match() with add()452{453int n = 3;454const float radius = 1.f/countFactor * n;455vector<vector<DMatch> > matches;456// make add() twice to test such case457dmatcher->add( vector<Mat>(1,train.rowRange(0, train.rows/2)) );458dmatcher->add( vector<Mat>(1,train.rowRange(train.rows/2, train.rows)) );459// prepare masks (make first nearest match illegal)460vector<Mat> masks(2);461for(int mi = 0; mi < 2; mi++ )462{463masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1));464for( int di = 0; di < queryDescCount/2; di++ )465masks[mi].col(di*countFactor).setTo(Scalar::all(0));466}467468dmatcher->radiusMatch( query, matches, radius, masks );469470//int curRes = cvtest::TS::OK;471if( (int)matches.size() != queryDescCount )472{473ts->printf(cvtest::TS::LOG, "Incorrect matches count while test radiusMatch() function (1).\n");474ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );475}476477int badCount = 0;478int shift = dmatcher->isMaskSupported() ? 1 : 0;479int needMatchCount = dmatcher->isMaskSupported() ? n-1 : n;480for( size_t i = 0; i < matches.size(); i++ )481{482if( (int)matches[i].size() != needMatchCount )483badCount++;484else485{486int localBadCount = 0;487for( int k = 0; k < needMatchCount; k++ )488{489DMatch& match = matches[i][k];490{491if( i < queryDescCount/2 )492{493if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + k + shift) ||494(match.imgIdx != 0) )495localBadCount++;496}497else498{499if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + k + shift) ||500(match.imgIdx != 1) )501localBadCount++;502}503}504}505badCount += localBadCount > 0 ? 1 : 0;506}507}508if( (float)badCount > (float)queryDescCount*badPart )509{510//curRes = cvtest::TS::FAIL_INVALID_OUTPUT;511ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test radiusMatch() function (2).\n",512(float)badCount/(float)queryDescCount );513ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );514}515}516}517518void CV_DescriptorMatcherTest::run( int )519{520Mat query, train;521generateData( query, train );522523matchTest( query, train );524525knnMatchTest( query, train );526527radiusMatchTest( query, train );528}529530/****************************************************************************************\531* Tests registrations *532\****************************************************************************************/533534TEST( Features2d_DescriptorMatcher_BruteForce, regression )535{536CV_DescriptorMatcherTest test( "descriptor-matcher-brute-force",537DescriptorMatcher::create("BruteForce"), 0.01f );538test.safe_run();539}540541#ifdef HAVE_OPENCV_FLANN542TEST( Features2d_DescriptorMatcher_FlannBased, regression )543{544CV_DescriptorMatcherTest test( "descriptor-matcher-flann-based",545DescriptorMatcher::create("FlannBased"), 0.04f );546test.safe_run();547}548#endif549550TEST( Features2d_DMatch, read_write )551{552FileStorage fs(".xml", FileStorage::WRITE + FileStorage::MEMORY);553vector<DMatch> matches;554matches.push_back(DMatch(1,2,3,4.5f));555fs << "Match" << matches;556String str = fs.releaseAndGetString();557ASSERT_NE( strstr(str.c_str(), "4.5"), (char*)0 );558}559560}} // namespace561562563