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  //                           License Agreement
11  //                For Open Source Computer Vision Library
12  //
13  // Copyright (C) 2014, Biagio Montesano, 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 the copyright holders 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 
44 namespace opencv_test { namespace {
45 
46 class CV_BinaryDescriptorMatcherTest : public cvtest::BaseTest
47 {
48  public:
CV_BinaryDescriptorMatcherTest(float _badPart)49   CV_BinaryDescriptorMatcherTest( float _badPart ) :
50       badPart( _badPart )
51   {
52     dmatcher = BinaryDescriptorMatcher::createBinaryDescriptorMatcher();
53   }
54 
55  protected:
56   static const int dim = 32;
57   static const int queryDescCount = 300;  // must be even number because we split train data in some cases in two
58   static const int countFactor = 4;  // do not change it
59   const float badPart;
60 
61   virtual void run( int );
62   void generateData( Mat& query, Mat& train );
63   uchar invertSingleBits( uchar dividend_char, int numBits );
64   void emptyDataTest();
65   void matchTest( const Mat& query, const Mat& train );
66   void knnMatchTest( const Mat& query, const Mat& train );
67   void radiusMatchTest( const Mat& query, const Mat& train );
68 
69   std::string name;
70   Ptr<BinaryDescriptorMatcher> dmatcher;
71 
72  private:
operator =(const CV_BinaryDescriptorMatcherTest &)73   CV_BinaryDescriptorMatcherTest& operator=( const CV_BinaryDescriptorMatcherTest& )
74   {
75     return *this;
76   }
77 
78 };
79 
80 /* invert numBits bits in input char */
invertSingleBits(uchar dividend_char,int numBits)81 uchar CV_BinaryDescriptorMatcherTest::invertSingleBits( uchar dividend_char, int numBits )
82 {
83   std::vector<int> bin_vector;
84   long dividend;
85   long bin_num;
86 
87   /* convert input char to a long */
88   dividend = (long) dividend_char;
89 
90   /*if a 0 has been obtained, just generate a 8-bit long vector of zeros */
91   if( dividend == 0 )
92     bin_vector = std::vector<int>( 8, 0 );
93 
94   /* else, apply classic decimal to binary conversion */
95   else
96   {
97     while ( dividend >= 1 )
98     {
99       bin_num = dividend % 2;
100       dividend /= 2;
101       bin_vector.push_back( bin_num );
102     }
103   }
104 
105   /* ensure that binary vector always has length 8 */
106   if( bin_vector.size() < 8 )
107   {
108     std::vector<int> zeros( 8 - bin_vector.size(), 0 );
109     bin_vector.insert( bin_vector.end(), zeros.begin(), zeros.end() );
110   }
111 
112   /* invert numBits bits */
113   for ( int index = 0; index < numBits; index++ )
114   {
115     if( bin_vector[index] == 0 )
116       bin_vector[index] = 1;
117 
118     else
119       bin_vector[index] = 0;
120   }
121 
122   /* reconvert to decimal */
123   uchar result = 0;
124   for ( int i = (int) bin_vector.size() - 1; i >= 0; i-- )
125     result += (uchar) ( bin_vector[i] * ( 1 << i ) );
126 
127   return result;
128 }
129 
emptyDataTest()130 void CV_BinaryDescriptorMatcherTest::emptyDataTest()
131 {
132   Mat queryDescriptors, trainDescriptors, mask;
133   std::vector<Mat> trainDescriptorCollection, masks;
134   std::vector<DMatch> matches;
135   std::vector<std::vector<DMatch> > vmatches;
136 
137   try
138   {
139     dmatcher->match( queryDescriptors, trainDescriptors, matches, mask );
140   }
141 
142   catch ( ... )
143   {
144     ts->printf( cvtest::TS::LOG, "match() on empty descriptors must not generate exception (1).\n" );
145     ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
146   }
147 
148   try
149   {
150     dmatcher->knnMatch( queryDescriptors, trainDescriptors, vmatches, 2, mask );
151   }
152 
153   catch ( ... )
154   {
155     ts->printf( cvtest::TS::LOG, "knnMatch() on empty descriptors must not generate exception (1).\n" );
156     ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
157   }
158 
159   try
160   {
161     dmatcher->radiusMatch( queryDescriptors, trainDescriptors, vmatches, 10.f, mask );
162   }
163 
164   catch ( ... )
165   {
166     ts->printf( cvtest::TS::LOG, "radiusMatch() on empty descriptors must not generate exception (1).\n" );
167     ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
168   }
169 
170   try
171   {
172     dmatcher->add( trainDescriptorCollection );
173   }
174 
175   catch ( ... )
176   {
177     ts->printf( cvtest::TS::LOG, "add() on empty descriptors must not generate exception.\n" );
178     ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
179   }
180 
181   try
182   {
183     dmatcher->match( queryDescriptors, matches, masks );
184   }
185 
186   catch ( ... )
187   {
188     ts->printf( cvtest::TS::LOG, "match() on empty descriptors must not generate exception (2).\n" );
189     ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
190   }
191 
192   try
193   {
194     dmatcher->knnMatch( queryDescriptors, vmatches, 2, masks );
195   }
196 
197   catch ( ... )
198   {
199     ts->printf( cvtest::TS::LOG, "knnMatch() on empty descriptors must not generate exception (2).\n" );
200     ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
201   }
202 
203   try
204   {
205     dmatcher->radiusMatch( queryDescriptors, vmatches, 10.f, masks );
206   }
207 
208   catch ( ... )
209   {
210     ts->printf( cvtest::TS::LOG, "radiusMatch() on empty descriptors must not generate exception (2).\n" );
211     ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
212   }
213 
214 }
215 
generateData(Mat & query,Mat & train)216 void CV_BinaryDescriptorMatcherTest::generateData( Mat& query, Mat& train )
217 {
218   RNG& rng = theRNG();
219 
220   /* Generate query descriptors randomly.
221    Descriptor vector elements are binary values. */
222   Mat buf( queryDescCount, dim, CV_8UC1 );
223   rng.fill( buf, RNG::UNIFORM, Scalar( 0 ), Scalar( 255 ) );
224   buf.convertTo( query, CV_8UC1 );
225 
226   for ( int i = 0; i < query.rows; i++ )
227   {
228     for ( int j = 0; j < countFactor; j++ )
229     {
230       train.push_back( query.row( i ) );
231       int randCol = rand() % 32;
232       uchar u = query.at<uchar>( i, randCol );
233       uchar modified_u = invertSingleBits( u, j + 1 );
234       train.at<uchar>( i * countFactor + j, randCol ) = modified_u;
235     }
236   }
237 }
238 
matchTest(const Mat & query,const Mat & train)239 void CV_BinaryDescriptorMatcherTest::matchTest( const Mat& query, const Mat& train )
240 {
241   dmatcher->clear();
242 
243   // test const version of match()
244   {
245     std::vector<DMatch> matches;
246     dmatcher->match( query, train, matches );
247 
248     if( (int) matches.size() != queryDescCount )
249     {
250       ts->printf( cvtest::TS::LOG, "Incorrect matches count while test match() function (1).\n" );
251       ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
252     }
253 
254     else
255     {
256       int badCount = 0;
257       for ( size_t i = 0; i < matches.size(); i++ )
258       {
259         DMatch& match = matches[i];
260         if( ( match.queryIdx != (int) i ) || ( match.trainIdx != (int) i * countFactor ) || ( match.imgIdx != 0 ) )
261           badCount++;
262       }
263       if( (float) badCount > (float) queryDescCount * badPart )
264       {
265         ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test match() function (1).\n",
266                     (float) badCount / (float) queryDescCount );
267         ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
268       }
269     }
270   }
271 
272   // test const version of match() for the same query and test descriptors
273   {
274     std::vector<DMatch> matches;
275     dmatcher->match( query, query, matches );
276 
277     if( (int) matches.size() != query.rows )
278     {
279       ts->printf( cvtest::TS::LOG, "Incorrect matches count while test match() function for the same query and test descriptors (1).\n" );
280       ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
281     }
282     else
283     {
284       for ( size_t i = 0; i < matches.size(); i++ )
285       {
286         DMatch& match = matches[i];
287         if( match.queryIdx != (int) i || match.trainIdx != (int) i || std::abs( match.distance ) > FLT_EPSILON )
288         {
289           ts->printf(
290               cvtest::TS::LOG,
291               "Bad match (i=%d, queryIdx=%d, trainIdx=%d, distance=%f) while test match() function for the same query and test descriptors (1).\n", i,
292               match.queryIdx, match.trainIdx, match.distance );
293           ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
294         }
295       }
296     }
297   }
298 
299   // test version of match() with add()
300   {
301     dmatcher->clear();
302     std::vector<DMatch> matches;
303 
304     // make add() twice to test such case
305     dmatcher->add( std::vector<Mat>( 1, train.rowRange( 0, train.rows / 2 ) ) );
306     dmatcher->add( std::vector<Mat>( 1, train.rowRange( train.rows / 2, train.rows ) ) );
307 
308     // prepare masks (make first nearest match illegal)
309     std::vector<Mat> masks( 2 );
310     for ( int mi = 0; mi < 2; mi++ )
311       masks[mi] = Mat::ones( query.rows, 1/*train.rows / 2*/, CV_8UC1 );
312 
313     dmatcher->match( query, matches, masks );
314     if( (int) matches.size() != queryDescCount )
315     {
316       ts->printf( cvtest::TS::LOG, "Incorrect matches count while test match() function (2).\n" );
317       ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
318     }
319 
320     else
321     {
322       int badCount = 0;
323       for ( size_t i = 0; i < matches.size(); i++ )
324       {
325         DMatch& match = matches[i];
326 
327         if( ( match.queryIdx != (int) i ) || ( match.trainIdx != (int) i * countFactor /*+ shift*/) || ( match.imgIdx > 1 ) )
328           badCount++;
329       }
330 
331       if( (float) badCount > (float) queryDescCount * badPart )
332       {
333         ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test match() function (2).\n",
334                     (float) badCount / (float) queryDescCount );
335         ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
336       }
337     }
338   }
339 }
340 
knnMatchTest(const Mat & query,const Mat & train)341 void CV_BinaryDescriptorMatcherTest::knnMatchTest( const Mat& query, const Mat& train )
342 {
343   dmatcher->clear();
344 
345   // test const version of knnMatch()
346   {
347     const int knn = 3;
348 
349     std::vector<std::vector<DMatch> > matches;
350     dmatcher->knnMatch( query, train, matches, knn );
351 
352     if( (int) matches.size() != queryDescCount )
353     {
354       ts->printf( cvtest::TS::LOG, "Incorrect matches count while test knnMatch() function (1).\n" );
355       ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
356     }
357 
358     else
359     {
360       int badCount = 0;
361       for ( size_t i = 0; i < matches.size(); i++ )
362       {
363         if( (int) matches[i].size() != knn )
364           badCount++;
365 
366         else
367         {
368           int localBadCount = 0;
369           for ( int k = 0; k < knn; k++ )
370           {
371             DMatch& match = matches[i][k];
372             if( ( match.queryIdx != (int) i ) || ( match.trainIdx != (int) i * countFactor + k ) || ( match.imgIdx != 0 ) )
373               localBadCount++;
374           }
375           badCount += localBadCount > 0 ? 1 : 0;
376         }
377 
378       }
379 
380       if( (float) badCount > (float) queryDescCount * badPart )
381       {
382         ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test knnMatch() function (1).\n",
383                     (float) badCount / (float) queryDescCount );
384         ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
385       }
386     }
387   }
388 
389 //  // test version of knnMatch() with add()
390   {
391     const int knn = 2;
392     std::vector<std::vector<DMatch> > matches;
393 
394     // make add() twice to test such case
395     dmatcher->add( std::vector<Mat>( 1, train.rowRange( 0, train.rows / 2 ) ) );
396     dmatcher->add( std::vector<Mat>( 1, train.rowRange( train.rows / 2, train.rows ) ) );
397 
398     // prepare masks (make first nearest match illegal)
399     std::vector<Mat> masks( 2 );
400     for ( int mi = 0; mi < 2; mi++ )
401     {
402       masks[mi] = Mat::ones( query.rows, 1, CV_8UC1 );
403     }
404 
405     dmatcher->knnMatch( query, matches, knn, masks );
406 
407     if( (int) matches.size() != queryDescCount )
408     {
409       ts->printf( cvtest::TS::LOG, "Incorrect matches count while test knnMatch() function (2).\n" );
410       ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
411     }
412 
413     else
414     {
415       int badCount = 0;
416       for ( size_t i = 0; i < matches.size(); i++ )
417       {
418         if( (int) matches[i].size() != knn )
419           badCount++;
420 
421         else
422         {
423           int localBadCount = 0;
424           for ( int k = 0; k < knn; k++ )
425           {
426             DMatch& match = matches[i][k];
427             {
428               if( i < queryDescCount / 2 )
429               {
430                 if( ( match.queryIdx != (int) i ) || ( match.trainIdx != (int) i * countFactor + k ) || ( match.imgIdx != 0 ) )
431                   localBadCount++;
432               }
433 
434               else
435               {
436                 if( ( match.queryIdx != (int) i ) || ( match.trainIdx != (int) i * countFactor + k ) || ( match.imgIdx != 1 ) )
437                   localBadCount++;
438               }
439             }
440           }
441 
442           badCount += localBadCount > 0 ? 1 : 0;
443         }
444       }
445 
446       if( (float) badCount > (float) queryDescCount * badPart )
447       {
448         ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test knnMatch() function (2).\n",
449                     (float) badCount / (float) queryDescCount );
450         ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
451       }
452     }
453   }
454 }
455 
radiusMatchTest(const Mat & query,const Mat & train)456 void CV_BinaryDescriptorMatcherTest::radiusMatchTest( const Mat& query, const Mat& train )
457 {
458   dmatcher->clear();
459   // test const version of match()
460   {
461     const float radius = 1;
462     std::vector<std::vector<DMatch> > matches;
463     dmatcher->radiusMatch( query, train, matches, radius );
464 
465     if( (int) matches.size() != queryDescCount )
466     {
467       ts->printf( cvtest::TS::LOG, "Incorrect matches count while test radiusMatch() function (1).\n" );
468       ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
469     }
470     else
471     {
472       int badCount = 0;
473       for ( size_t i = 0; i < matches.size(); i++ )
474       {
475 
476         if( (int) matches[i].size() != 1 )
477         {
478           badCount++;
479         }
480 
481         else
482         {
483           DMatch& match = matches[i][0];
484           if( ( match.queryIdx != (int) i ) || ( match.trainIdx != (int) i * countFactor ) || ( match.imgIdx != 0 ) )
485             badCount++;
486         }
487       }
488 
489       if( (float) badCount > (float) queryDescCount * badPart )
490       {
491         ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test radiusMatch() function (1).\n",
492                     (float) badCount / (float) queryDescCount );
493         ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
494       }
495     }
496   }
497 
498   {
499     const float radius = 3;
500     std::vector<std::vector<DMatch> > matches;
501     // make add() twice to test such case
502     dmatcher->add( std::vector<Mat>( 1, train.rowRange( 0, train.rows / 2 ) ) );
503     dmatcher->add( std::vector<Mat>( 1, train.rowRange( train.rows / 2, train.rows ) ) );
504 
505     // prepare masks
506     std::vector<Mat> masks( 2 );
507     for ( int mi = 0; mi < 2; mi++ )
508       masks[mi] = Mat::ones( query.rows, 1, CV_8UC1 );
509 
510     dmatcher->radiusMatch( query, matches, radius, masks );
511 
512     //int curRes = cvtest::TS::OK;
513     if( (int) matches.size() != queryDescCount )
514     {
515       ts->printf( cvtest::TS::LOG, "Incorrect matches count while test radiusMatch() function (1).\n" );
516       ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
517     }
518 
519     int badCount = 0;
520     for ( size_t i = 0; i < matches.size(); i++ )
521     {
522       if( (int) matches[i].size() != radius )
523         badCount++;
524 
525       else
526       {
527         int localBadCount = 0;
528         for ( int k = 0; k < radius; k++ )
529         {
530           DMatch& match = matches[i][k];
531           {
532             if( i < queryDescCount / 2 )
533             {
534               if( ( match.queryIdx != (int) i ) || ( match.trainIdx != (int) i * countFactor + k ) || ( match.imgIdx != 0 ) )
535                 localBadCount++;
536             }
537 
538             else
539             {
540               if( ( match.queryIdx != (int) i ) || ( match.trainIdx != (int) i * countFactor + k ) || ( match.imgIdx != 1 ) )
541                 localBadCount++;
542             }
543           }
544         }
545 
546         badCount += localBadCount > 0 ? 1 : 0;
547       }
548     }
549 
550     if( (float) badCount > (float) queryDescCount * badPart )
551     {
552       //curRes = cvtest::TS::FAIL_INVALID_OUTPUT;
553       ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test radiusMatch() function (2).\n",
554                   (float) badCount / (float) queryDescCount );
555       ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
556     }
557   }
558 }
559 
run(int)560 void CV_BinaryDescriptorMatcherTest::run( int )
561 {
562   Mat query, train;
563   emptyDataTest();
564   generateData( query, train );
565   matchTest( query, train );
566   knnMatchTest( query, train );
567   radiusMatchTest( query, train );
568 }
569 
570 /****************************************************************************************\
571 *                                Tests registrations                                     *
572  \****************************************************************************************/
573 
TEST(BinaryDescriptor_Matcher,regression)574 TEST( BinaryDescriptor_Matcher, regression)
575 {
576   CV_BinaryDescriptorMatcherTest test( 0.01f );
577   test.safe_run();
578 }
579 
580 }} // namespace
581