Home | History | Annotate | Download | only in test
      1 /*M///////////////////////////////////////////////////////////////////////////////////////
      2 //
      3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
      4 //
      5 //  By downloading, copying, installing or using the software you agree to this license.
      6 //  If you do not agree to this license, do not download, install,
      7 //  copy or use the software.
      8 //
      9 //
     10 //                        Intel License Agreement
     11 //                For Open Source Computer Vision Library
     12 //
     13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
     14 // Third party copyrights are property of their respective owners.
     15 //
     16 // Redistribution and use in source and binary forms, with or without modification,
     17 // are permitted provided that the following conditions are met:
     18 //
     19 //   * Redistribution's of source code must retain the above copyright notice,
     20 //     this list of conditions and the following disclaimer.
     21 //
     22 //   * Redistribution's in binary form must reproduce the above copyright notice,
     23 //     this list of conditions and the following disclaimer in the documentation
     24 //     and/or other materials provided with the distribution.
     25 //
     26 //   * The name of Intel Corporation may not be used to endorse or promote products
     27 //     derived from this software without specific prior written permission.
     28 //
     29 // This software is provided by the copyright holders and contributors "as is" and
     30 // any express or implied warranties, including, but not limited to, the implied
     31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
     32 // In no event shall the Intel Corporation or contributors be liable for any direct,
     33 // indirect, incidental, special, exemplary, or consequential damages
     34 // (including, but not limited to, procurement of substitute goods or services;
     35 // loss of use, data, or profits; or business interruption) however caused
     36 // and on any theory of liability, whether in contract, strict liability,
     37 // or tort (including negligence or otherwise) arising in any way out of
     38 // the use of this software, even if advised of the possibility of such damage.
     39 //
     40 //M*/
     41 
     42 #include "test_precomp.hpp"
     43 #include "opencv2/highgui.hpp"
     44 
     45 using namespace std;
     46 using namespace cv;
     47 
     48 const string FEATURES2D_DIR = "features2d";
     49 const string IMAGE_FILENAME = "tsukuba.png";
     50 
     51 /****************************************************************************************\
     52 *                       Algorithmic tests for descriptor matchers                        *
     53 \****************************************************************************************/
     54 class CV_DescriptorMatcherTest : public cvtest::BaseTest
     55 {
     56 public:
     57     CV_DescriptorMatcherTest( const string& _name, const Ptr<DescriptorMatcher>& _dmatcher, float _badPart ) :
     58         badPart(_badPart), name(_name), dmatcher(_dmatcher)
     59         {}
     60 protected:
     61     static const int dim = 500;
     62     static const int queryDescCount = 300; // must be even number because we split train data in some cases in two
     63     static const int countFactor = 4; // do not change it
     64     const float badPart;
     65 
     66     virtual void run( int );
     67     void generateData( Mat& query, Mat& train );
     68 
     69     void emptyDataTest();
     70     void matchTest( const Mat& query, const Mat& train );
     71     void knnMatchTest( const Mat& query, const Mat& train );
     72     void radiusMatchTest( const Mat& query, const Mat& train );
     73 
     74     string name;
     75     Ptr<DescriptorMatcher> dmatcher;
     76 
     77 private:
     78     CV_DescriptorMatcherTest& operator=(const CV_DescriptorMatcherTest&) { return *this; }
     79 };
     80 
     81 void CV_DescriptorMatcherTest::emptyDataTest()
     82 {
     83     assert( !dmatcher.empty() );
     84     Mat queryDescriptors, trainDescriptors, mask;
     85     vector<Mat> trainDescriptorCollection, masks;
     86     vector<DMatch> matches;
     87     vector<vector<DMatch> > vmatches;
     88 
     89     try
     90     {
     91         dmatcher->match( queryDescriptors, trainDescriptors, matches, mask );
     92     }
     93     catch(...)
     94     {
     95         ts->printf( cvtest::TS::LOG, "match() on empty descriptors must not generate exception (1).\n" );
     96         ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
     97     }
     98 
     99     try
    100     {
    101         dmatcher->knnMatch( queryDescriptors, trainDescriptors, vmatches, 2, mask );
    102     }
    103     catch(...)
    104     {
    105         ts->printf( cvtest::TS::LOG, "knnMatch() on empty descriptors must not generate exception (1).\n" );
    106         ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    107     }
    108 
    109     try
    110     {
    111         dmatcher->radiusMatch( queryDescriptors, trainDescriptors, vmatches, 10.f, mask );
    112     }
    113     catch(...)
    114     {
    115         ts->printf( cvtest::TS::LOG, "radiusMatch() on empty descriptors must not generate exception (1).\n" );
    116         ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    117     }
    118 
    119     try
    120     {
    121         dmatcher->add( trainDescriptorCollection );
    122     }
    123     catch(...)
    124     {
    125         ts->printf( cvtest::TS::LOG, "add() on empty descriptors must not generate exception.\n" );
    126         ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    127     }
    128 
    129     try
    130     {
    131         dmatcher->match( queryDescriptors, matches, masks );
    132     }
    133     catch(...)
    134     {
    135         ts->printf( cvtest::TS::LOG, "match() on empty descriptors must not generate exception (2).\n" );
    136         ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    137     }
    138 
    139     try
    140     {
    141         dmatcher->knnMatch( queryDescriptors, vmatches, 2, masks );
    142     }
    143     catch(...)
    144     {
    145         ts->printf( cvtest::TS::LOG, "knnMatch() on empty descriptors must not generate exception (2).\n" );
    146         ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    147     }
    148 
    149     try
    150     {
    151         dmatcher->radiusMatch( queryDescriptors, vmatches, 10.f, masks );
    152     }
    153     catch(...)
    154     {
    155         ts->printf( cvtest::TS::LOG, "radiusMatch() on empty descriptors must not generate exception (2).\n" );
    156         ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    157     }
    158 
    159 }
    160 
    161 void CV_DescriptorMatcherTest::generateData( Mat& query, Mat& train )
    162 {
    163     RNG& rng = theRNG();
    164 
    165     // Generate query descriptors randomly.
    166     // Descriptor vector elements are integer values.
    167     Mat buf( queryDescCount, dim, CV_32SC1 );
    168     rng.fill( buf, RNG::UNIFORM, Scalar::all(0), Scalar(3) );
    169     buf.convertTo( query, CV_32FC1 );
    170 
    171     // Generate train decriptors as follows:
    172     // copy each query descriptor to train set countFactor times
    173     // and perturb some one element of the copied descriptors in
    174     // in ascending order. General boundaries of the perturbation
    175     // are (0.f, 1.f).
    176     train.create( query.rows*countFactor, query.cols, CV_32FC1 );
    177     float step = 1.f / countFactor;
    178     for( int qIdx = 0; qIdx < query.rows; qIdx++ )
    179     {
    180         Mat queryDescriptor = query.row(qIdx);
    181         for( int c = 0; c < countFactor; c++ )
    182         {
    183             int tIdx = qIdx * countFactor + c;
    184             Mat trainDescriptor = train.row(tIdx);
    185             queryDescriptor.copyTo( trainDescriptor );
    186             int elem = rng(dim);
    187             float diff = rng.uniform( step*c, step*(c+1) );
    188             trainDescriptor.at<float>(0, elem) += diff;
    189         }
    190     }
    191 }
    192 
    193 void CV_DescriptorMatcherTest::matchTest( const Mat& query, const Mat& train )
    194 {
    195     dmatcher->clear();
    196 
    197     // test const version of match()
    198     {
    199         vector<DMatch> matches;
    200         dmatcher->match( query, train, matches );
    201 
    202         if( (int)matches.size() != queryDescCount )
    203         {
    204             ts->printf(cvtest::TS::LOG, "Incorrect matches count while test match() function (1).\n");
    205             ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    206         }
    207         else
    208         {
    209             int badCount = 0;
    210             for( size_t i = 0; i < matches.size(); i++ )
    211             {
    212                 DMatch& match = matches[i];
    213                 if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor) || (match.imgIdx != 0) )
    214                     badCount++;
    215             }
    216             if( (float)badCount > (float)queryDescCount*badPart )
    217             {
    218                 ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test match() function (1).\n",
    219                             (float)badCount/(float)queryDescCount );
    220                 ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    221             }
    222         }
    223     }
    224 
    225     // test const version of match() for the same query and test descriptors
    226     {
    227         vector<DMatch> matches;
    228         dmatcher->match( query, query, matches );
    229 
    230         if( (int)matches.size() != query.rows )
    231         {
    232             ts->printf(cvtest::TS::LOG, "Incorrect matches count while test match() function for the same query and test descriptors (1).\n");
    233             ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    234         }
    235         else
    236         {
    237             for( size_t i = 0; i < matches.size(); i++ )
    238             {
    239                 DMatch& match = matches[i];
    240                 //std::cout << match.distance << std::endl;
    241 
    242                 if( match.queryIdx != (int)i || match.trainIdx != (int)i || std::abs(match.distance) > FLT_EPSILON )
    243                 {
    244                     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",
    245                                 i, match.queryIdx, match.trainIdx, match.distance );
    246                     ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    247                 }
    248             }
    249         }
    250     }
    251 
    252     // test version of match() with add()
    253     {
    254         vector<DMatch> matches;
    255         // make add() twice to test such case
    256         dmatcher->add( vector<Mat>(1,train.rowRange(0, train.rows/2)) );
    257         dmatcher->add( vector<Mat>(1,train.rowRange(train.rows/2, train.rows)) );
    258         // prepare masks (make first nearest match illegal)
    259         vector<Mat> masks(2);
    260         for(int mi = 0; mi < 2; mi++ )
    261         {
    262             masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1));
    263             for( int di = 0; di < queryDescCount/2; di++ )
    264                 masks[mi].col(di*countFactor).setTo(Scalar::all(0));
    265         }
    266 
    267         dmatcher->match( query, matches, masks );
    268 
    269         if( (int)matches.size() != queryDescCount )
    270         {
    271             ts->printf(cvtest::TS::LOG, "Incorrect matches count while test match() function (2).\n");
    272             ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    273         }
    274         else
    275         {
    276             int badCount = 0;
    277             for( size_t i = 0; i < matches.size(); i++ )
    278             {
    279                 DMatch& match = matches[i];
    280                 int shift = dmatcher->isMaskSupported() ? 1 : 0;
    281                 {
    282                     if( i < queryDescCount/2 )
    283                     {
    284                         if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + shift) || (match.imgIdx != 0) )
    285                             badCount++;
    286                     }
    287                     else
    288                     {
    289                         if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + shift) || (match.imgIdx != 1) )
    290                             badCount++;
    291                     }
    292                 }
    293             }
    294             if( (float)badCount > (float)queryDescCount*badPart )
    295             {
    296                 ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test match() function (2).\n",
    297                             (float)badCount/(float)queryDescCount );
    298                 ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
    299             }
    300         }
    301     }
    302 }
    303 
    304 void CV_DescriptorMatcherTest::knnMatchTest( const Mat& query, const Mat& train )
    305 {
    306     dmatcher->clear();
    307 
    308     // test const version of knnMatch()
    309     {
    310         const int knn = 3;
    311 
    312         vector<vector<DMatch> > matches;
    313         dmatcher->knnMatch( query, train, matches, knn );
    314 
    315         if( (int)matches.size() != queryDescCount )
    316         {
    317             ts->printf(cvtest::TS::LOG, "Incorrect matches count while test knnMatch() function (1).\n");
    318             ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    319         }
    320         else
    321         {
    322             int badCount = 0;
    323             for( size_t i = 0; i < matches.size(); i++ )
    324             {
    325                 if( (int)matches[i].size() != knn )
    326                     badCount++;
    327                 else
    328                 {
    329                     int localBadCount = 0;
    330                     for( int k = 0; k < knn; k++ )
    331                     {
    332                         DMatch& match = matches[i][k];
    333                         if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor+k) || (match.imgIdx != 0) )
    334                             localBadCount++;
    335                     }
    336                     badCount += localBadCount > 0 ? 1 : 0;
    337                 }
    338             }
    339             if( (float)badCount > (float)queryDescCount*badPart )
    340             {
    341                 ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test knnMatch() function (1).\n",
    342                             (float)badCount/(float)queryDescCount );
    343                 ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    344             }
    345         }
    346     }
    347 
    348     // test version of knnMatch() with add()
    349     {
    350         const int knn = 2;
    351         vector<vector<DMatch> > matches;
    352         // make add() twice to test such case
    353         dmatcher->add( vector<Mat>(1,train.rowRange(0, train.rows/2)) );
    354         dmatcher->add( vector<Mat>(1,train.rowRange(train.rows/2, train.rows)) );
    355         // prepare masks (make first nearest match illegal)
    356         vector<Mat> masks(2);
    357         for(int mi = 0; mi < 2; mi++ )
    358         {
    359             masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1));
    360             for( int di = 0; di < queryDescCount/2; di++ )
    361                 masks[mi].col(di*countFactor).setTo(Scalar::all(0));
    362         }
    363 
    364         dmatcher->knnMatch( query, matches, knn, masks );
    365 
    366         if( (int)matches.size() != queryDescCount )
    367         {
    368             ts->printf(cvtest::TS::LOG, "Incorrect matches count while test knnMatch() function (2).\n");
    369             ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    370         }
    371         else
    372         {
    373             int badCount = 0;
    374             int shift = dmatcher->isMaskSupported() ? 1 : 0;
    375             for( size_t i = 0; i < matches.size(); i++ )
    376             {
    377                 if( (int)matches[i].size() != knn )
    378                     badCount++;
    379                 else
    380                 {
    381                     int localBadCount = 0;
    382                     for( int k = 0; k < knn; k++ )
    383                     {
    384                         DMatch& match = matches[i][k];
    385                         {
    386                             if( i < queryDescCount/2 )
    387                             {
    388                                 if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + k + shift) ||
    389                                     (match.imgIdx != 0) )
    390                                     localBadCount++;
    391                             }
    392                             else
    393                             {
    394                                 if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + k + shift) ||
    395                                     (match.imgIdx != 1) )
    396                                     localBadCount++;
    397                             }
    398                         }
    399                     }
    400                     badCount += localBadCount > 0 ? 1 : 0;
    401                 }
    402             }
    403             if( (float)badCount > (float)queryDescCount*badPart )
    404             {
    405                 ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test knnMatch() function (2).\n",
    406                             (float)badCount/(float)queryDescCount );
    407                 ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
    408             }
    409         }
    410     }
    411 }
    412 
    413 void CV_DescriptorMatcherTest::radiusMatchTest( const Mat& query, const Mat& train )
    414 {
    415     dmatcher->clear();
    416     // test const version of match()
    417     {
    418         const float radius = 1.f/countFactor;
    419         vector<vector<DMatch> > matches;
    420         dmatcher->radiusMatch( query, train, matches, radius );
    421 
    422         if( (int)matches.size() != queryDescCount )
    423         {
    424             ts->printf(cvtest::TS::LOG, "Incorrect matches count while test radiusMatch() function (1).\n");
    425             ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    426         }
    427         else
    428         {
    429             int badCount = 0;
    430             for( size_t i = 0; i < matches.size(); i++ )
    431             {
    432                 if( (int)matches[i].size() != 1 )
    433                     badCount++;
    434                 else
    435                 {
    436                     DMatch& match = matches[i][0];
    437                     if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor) || (match.imgIdx != 0) )
    438                         badCount++;
    439                 }
    440             }
    441             if( (float)badCount > (float)queryDescCount*badPart )
    442             {
    443                 ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test radiusMatch() function (1).\n",
    444                             (float)badCount/(float)queryDescCount );
    445                 ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    446             }
    447         }
    448     }
    449 
    450     // test version of match() with add()
    451     {
    452         int n = 3;
    453         const float radius = 1.f/countFactor * n;
    454         vector<vector<DMatch> > matches;
    455         // make add() twice to test such case
    456         dmatcher->add( vector<Mat>(1,train.rowRange(0, train.rows/2)) );
    457         dmatcher->add( vector<Mat>(1,train.rowRange(train.rows/2, train.rows)) );
    458         // prepare masks (make first nearest match illegal)
    459         vector<Mat> masks(2);
    460         for(int mi = 0; mi < 2; mi++ )
    461         {
    462             masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1));
    463             for( int di = 0; di < queryDescCount/2; di++ )
    464                 masks[mi].col(di*countFactor).setTo(Scalar::all(0));
    465         }
    466 
    467         dmatcher->radiusMatch( query, matches, radius, masks );
    468 
    469         //int curRes = cvtest::TS::OK;
    470         if( (int)matches.size() != queryDescCount )
    471         {
    472             ts->printf(cvtest::TS::LOG, "Incorrect matches count while test radiusMatch() function (1).\n");
    473             ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
    474         }
    475 
    476         int badCount = 0;
    477         int shift = dmatcher->isMaskSupported() ? 1 : 0;
    478         int needMatchCount = dmatcher->isMaskSupported() ? n-1 : n;
    479         for( size_t i = 0; i < matches.size(); i++ )
    480         {
    481             if( (int)matches[i].size() != needMatchCount )
    482                 badCount++;
    483             else
    484             {
    485                 int localBadCount = 0;
    486                 for( int k = 0; k < needMatchCount; k++ )
    487                 {
    488                     DMatch& match = matches[i][k];
    489                     {
    490                         if( i < queryDescCount/2 )
    491                         {
    492                             if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + k + shift) ||
    493                                 (match.imgIdx != 0) )
    494                                 localBadCount++;
    495                         }
    496                         else
    497                         {
    498                             if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + k + shift) ||
    499                                 (match.imgIdx != 1) )
    500                                 localBadCount++;
    501                         }
    502                     }
    503                 }
    504                 badCount += localBadCount > 0 ? 1 : 0;
    505             }
    506         }
    507         if( (float)badCount > (float)queryDescCount*badPart )
    508         {
    509             //curRes = cvtest::TS::FAIL_INVALID_OUTPUT;
    510             ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test radiusMatch() function (2).\n",
    511                         (float)badCount/(float)queryDescCount );
    512             ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
    513         }
    514     }
    515 }
    516 
    517 void CV_DescriptorMatcherTest::run( int )
    518 {
    519     Mat query, train;
    520     generateData( query, train );
    521 
    522     matchTest( query, train );
    523 
    524     knnMatchTest( query, train );
    525 
    526     radiusMatchTest( query, train );
    527 }
    528 
    529 /****************************************************************************************\
    530 *                                Tests registrations                                     *
    531 \****************************************************************************************/
    532 
    533 TEST( Features2d_DescriptorMatcher_BruteForce, regression )
    534 {
    535     CV_DescriptorMatcherTest test( "descriptor-matcher-brute-force",
    536                                   DescriptorMatcher::create("BruteForce"), 0.01f );
    537     test.safe_run();
    538 }
    539 
    540 TEST( Features2d_DescriptorMatcher_FlannBased, regression )
    541 {
    542     CV_DescriptorMatcherTest test( "descriptor-matcher-flann-based",
    543                                   DescriptorMatcher::create("FlannBased"), 0.04f );
    544     test.safe_run();
    545 }
    546 
    547 TEST( Features2d_DMatch, read_write )
    548 {
    549     FileStorage fs(".xml", FileStorage::WRITE + FileStorage::MEMORY);
    550     vector<DMatch> matches;
    551     matches.push_back(DMatch(1,2,3,4.5f));
    552     fs << "Match" << matches;
    553     String str = fs.releaseAndGetString();
    554     ASSERT_NE( strstr(str.c_str(), "4.5"), (char*)0 );
    555 }
    556