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