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 44 using namespace std; 45 using namespace cv; 46 47 const string FEATURES2D_DIR = "features2d"; 48 const string IMAGE_FILENAME = "tsukuba.png"; 49 const string DESCRIPTOR_DIR = FEATURES2D_DIR + "/descriptor_extractors"; 50 51 /****************************************************************************************\ 52 * Regression tests for descriptor extractors. * 53 \****************************************************************************************/ 54 static void writeMatInBin( const Mat& mat, const string& filename ) 55 { 56 FILE* f = fopen( filename.c_str(), "wb"); 57 if( f ) 58 { 59 int type = mat.type(); 60 fwrite( (void*)&mat.rows, sizeof(int), 1, f ); 61 fwrite( (void*)&mat.cols, sizeof(int), 1, f ); 62 fwrite( (void*)&type, sizeof(int), 1, f ); 63 int dataSize = (int)(mat.step * mat.rows * mat.channels()); 64 fwrite( (void*)&dataSize, sizeof(int), 1, f ); 65 fwrite( (void*)mat.ptr(), 1, dataSize, f ); 66 fclose(f); 67 } 68 } 69 70 static Mat readMatFromBin( const string& filename ) 71 { 72 FILE* f = fopen( filename.c_str(), "rb" ); 73 if( f ) 74 { 75 int rows, cols, type, dataSize; 76 size_t elements_read1 = fread( (void*)&rows, sizeof(int), 1, f ); 77 size_t elements_read2 = fread( (void*)&cols, sizeof(int), 1, f ); 78 size_t elements_read3 = fread( (void*)&type, sizeof(int), 1, f ); 79 size_t elements_read4 = fread( (void*)&dataSize, sizeof(int), 1, f ); 80 CV_Assert(elements_read1 == 1 && elements_read2 == 1 && elements_read3 == 1 && elements_read4 == 1); 81 82 int step = dataSize / rows / CV_ELEM_SIZE(type); 83 CV_Assert(step >= cols); 84 85 Mat m = Mat(rows, step, type).colRange(0, cols); 86 87 size_t elements_read = fread( m.ptr(), 1, dataSize, f ); 88 CV_Assert(elements_read == (size_t)(dataSize)); 89 fclose(f); 90 91 return m; 92 } 93 return Mat(); 94 } 95 96 template<class Distance> 97 class CV_DescriptorExtractorTest : public cvtest::BaseTest 98 { 99 public: 100 typedef typename Distance::ValueType ValueType; 101 typedef typename Distance::ResultType DistanceType; 102 103 CV_DescriptorExtractorTest( const string _name, DistanceType _maxDist, const Ptr<DescriptorExtractor>& _dextractor, 104 Distance d = Distance(), Ptr<FeatureDetector> _detector = Ptr<FeatureDetector>()): 105 name(_name), maxDist(_maxDist), dextractor(_dextractor), distance(d) , detector(_detector) {} 106 107 ~CV_DescriptorExtractorTest() 108 { 109 } 110 protected: 111 virtual void createDescriptorExtractor() {} 112 113 void compareDescriptors( const Mat& validDescriptors, const Mat& calcDescriptors ) 114 { 115 if( validDescriptors.size != calcDescriptors.size || validDescriptors.type() != calcDescriptors.type() ) 116 { 117 ts->printf(cvtest::TS::LOG, "Valid and computed descriptors matrices must have the same size and type.\n"); 118 ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); 119 return; 120 } 121 122 CV_Assert( DataType<ValueType>::type == validDescriptors.type() ); 123 124 int dimension = validDescriptors.cols; 125 DistanceType curMaxDist = std::numeric_limits<DistanceType>::min(); 126 for( int y = 0; y < validDescriptors.rows; y++ ) 127 { 128 DistanceType dist = distance( validDescriptors.ptr<ValueType>(y), calcDescriptors.ptr<ValueType>(y), dimension ); 129 if( dist > curMaxDist ) 130 curMaxDist = dist; 131 } 132 133 stringstream ss; 134 ss << "Max distance between valid and computed descriptors " << curMaxDist; 135 if( curMaxDist <= maxDist ) 136 ss << "." << endl; 137 else 138 { 139 ss << ">" << maxDist << " - bad accuracy!"<< endl; 140 ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); 141 } 142 ts->printf(cvtest::TS::LOG, ss.str().c_str() ); 143 } 144 145 void emptyDataTest() 146 { 147 assert( dextractor ); 148 149 // One image. 150 Mat image; 151 vector<KeyPoint> keypoints; 152 Mat descriptors; 153 154 try 155 { 156 dextractor->compute( image, keypoints, descriptors ); 157 } 158 catch(...) 159 { 160 ts->printf( cvtest::TS::LOG, "compute() on empty image and empty keypoints must not generate exception (1).\n"); 161 ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); 162 } 163 164 image.create( 50, 50, CV_8UC3 ); 165 try 166 { 167 dextractor->compute( image, keypoints, descriptors ); 168 } 169 catch(...) 170 { 171 ts->printf( cvtest::TS::LOG, "compute() on nonempty image and empty keypoints must not generate exception (1).\n"); 172 ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); 173 } 174 175 // Several images. 176 vector<Mat> images; 177 vector<vector<KeyPoint> > keypointsCollection; 178 vector<Mat> descriptorsCollection; 179 try 180 { 181 dextractor->compute( images, keypointsCollection, descriptorsCollection ); 182 } 183 catch(...) 184 { 185 ts->printf( cvtest::TS::LOG, "compute() on empty images and empty keypoints collection must not generate exception (2).\n"); 186 ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); 187 } 188 } 189 190 void regressionTest() 191 { 192 assert( dextractor ); 193 194 // Read the test image. 195 string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME; 196 Mat img = imread( imgFilename ); 197 if( img.empty() ) 198 { 199 ts->printf( cvtest::TS::LOG, "Image %s can not be read.\n", imgFilename.c_str() ); 200 ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); 201 return; 202 } 203 vector<KeyPoint> keypoints; 204 FileStorage fs( string(ts->get_data_path()) + FEATURES2D_DIR + "/keypoints.xml.gz", FileStorage::READ ); 205 if(!detector.empty()) { 206 detector->detect(img, keypoints); 207 } else { 208 read( fs.getFirstTopLevelNode(), keypoints ); 209 } 210 if(!keypoints.empty()) 211 { 212 Mat calcDescriptors; 213 double t = (double)getTickCount(); 214 dextractor->compute( img, keypoints, calcDescriptors ); 215 t = getTickCount() - t; 216 ts->printf(cvtest::TS::LOG, "\nAverage time of computing one descriptor = %g ms.\n", t/((double)getTickFrequency()*1000.)/calcDescriptors.rows); 217 218 if( calcDescriptors.rows != (int)keypoints.size() ) 219 { 220 ts->printf( cvtest::TS::LOG, "Count of computed descriptors and keypoints count must be equal.\n" ); 221 ts->printf( cvtest::TS::LOG, "Count of keypoints is %d.\n", (int)keypoints.size() ); 222 ts->printf( cvtest::TS::LOG, "Count of computed descriptors is %d.\n", calcDescriptors.rows ); 223 ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); 224 return; 225 } 226 227 if( calcDescriptors.cols != dextractor->descriptorSize() || calcDescriptors.type() != dextractor->descriptorType() ) 228 { 229 ts->printf( cvtest::TS::LOG, "Incorrect descriptor size or descriptor type.\n" ); 230 ts->printf( cvtest::TS::LOG, "Expected size is %d.\n", dextractor->descriptorSize() ); 231 ts->printf( cvtest::TS::LOG, "Calculated size is %d.\n", calcDescriptors.cols ); 232 ts->printf( cvtest::TS::LOG, "Expected type is %d.\n", dextractor->descriptorType() ); 233 ts->printf( cvtest::TS::LOG, "Calculated type is %d.\n", calcDescriptors.type() ); 234 ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); 235 return; 236 } 237 238 // TODO read and write descriptor extractor parameters and check them 239 Mat validDescriptors = readDescriptors(); 240 if( !validDescriptors.empty() ) 241 compareDescriptors( validDescriptors, calcDescriptors ); 242 else 243 { 244 if( !writeDescriptors( calcDescriptors ) ) 245 { 246 ts->printf( cvtest::TS::LOG, "Descriptors can not be written.\n" ); 247 ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); 248 return; 249 } 250 } 251 } 252 if(!fs.isOpened()) 253 { 254 ts->printf( cvtest::TS::LOG, "Compute and write keypoints.\n" ); 255 fs.open( string(ts->get_data_path()) + FEATURES2D_DIR + "/keypoints.xml.gz", FileStorage::WRITE ); 256 if( fs.isOpened() ) 257 { 258 Ptr<ORB> fd = ORB::create(); 259 fd->detect(img, keypoints); 260 write( fs, "keypoints", keypoints ); 261 } 262 else 263 { 264 ts->printf(cvtest::TS::LOG, "File for writting keypoints can not be opened.\n"); 265 ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); 266 return; 267 } 268 } 269 } 270 271 void run(int) 272 { 273 createDescriptorExtractor(); 274 if( !dextractor ) 275 { 276 ts->printf(cvtest::TS::LOG, "Descriptor extractor is empty.\n"); 277 ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); 278 return; 279 } 280 281 emptyDataTest(); 282 regressionTest(); 283 284 ts->set_failed_test_info( cvtest::TS::OK ); 285 } 286 287 virtual Mat readDescriptors() 288 { 289 Mat res = readMatFromBin( string(ts->get_data_path()) + DESCRIPTOR_DIR + "/" + string(name) ); 290 return res; 291 } 292 293 virtual bool writeDescriptors( Mat& descs ) 294 { 295 writeMatInBin( descs, string(ts->get_data_path()) + DESCRIPTOR_DIR + "/" + string(name) ); 296 return true; 297 } 298 299 string name; 300 const DistanceType maxDist; 301 Ptr<DescriptorExtractor> dextractor; 302 Distance distance; 303 Ptr<FeatureDetector> detector; 304 305 private: 306 CV_DescriptorExtractorTest& operator=(const CV_DescriptorExtractorTest&) { return *this; } 307 }; 308 309 /****************************************************************************************\ 310 * Tests registrations * 311 \****************************************************************************************/ 312 313 TEST( Features2d_DescriptorExtractor_BRISK, regression ) 314 { 315 CV_DescriptorExtractorTest<Hamming> test( "descriptor-brisk", 316 (CV_DescriptorExtractorTest<Hamming>::DistanceType)2.f, 317 BRISK::create() ); 318 test.safe_run(); 319 } 320 321 TEST( Features2d_DescriptorExtractor_ORB, regression ) 322 { 323 // TODO adjust the parameters below 324 CV_DescriptorExtractorTest<Hamming> test( "descriptor-orb", 325 #if CV_NEON 326 (CV_DescriptorExtractorTest<Hamming>::DistanceType)25.f, 327 #else 328 (CV_DescriptorExtractorTest<Hamming>::DistanceType)12.f, 329 #endif 330 ORB::create() ); 331 test.safe_run(); 332 } 333 334 TEST( Features2d_DescriptorExtractor_KAZE, regression ) 335 { 336 CV_DescriptorExtractorTest< L2<float> > test( "descriptor-kaze", 0.03f, 337 KAZE::create(), 338 L2<float>(), KAZE::create() ); 339 test.safe_run(); 340 } 341 342 TEST( Features2d_DescriptorExtractor_AKAZE, regression ) 343 { 344 CV_DescriptorExtractorTest<Hamming> test( "descriptor-akaze", 345 (CV_DescriptorExtractorTest<Hamming>::DistanceType)12.f, 346 AKAZE::create(), 347 Hamming(), AKAZE::create()); 348 test.safe_run(); 349 } 350 351 TEST( Features2d_DescriptorExtractor, batch ) 352 { 353 string path = string(cvtest::TS::ptr()->get_data_path() + "detectors_descriptors_evaluation/images_datasets/graf"); 354 vector<Mat> imgs, descriptors; 355 vector<vector<KeyPoint> > keypoints; 356 int i, n = 6; 357 Ptr<ORB> orb = ORB::create(); 358 359 for( i = 0; i < n; i++ ) 360 { 361 string imgname = format("%s/img%d.png", path.c_str(), i+1); 362 Mat img = imread(imgname, 0); 363 imgs.push_back(img); 364 } 365 366 orb->detect(imgs, keypoints); 367 orb->compute(imgs, keypoints, descriptors); 368 369 ASSERT_EQ((int)keypoints.size(), n); 370 ASSERT_EQ((int)descriptors.size(), n); 371 372 for( i = 0; i < n; i++ ) 373 { 374 EXPECT_GT((int)keypoints[i].size(), 100); 375 EXPECT_GT(descriptors[i].rows, 100); 376 } 377 } 378 379 TEST( Features2d_Feature2d, no_crash ) 380 { 381 const String& pattern = string(cvtest::TS::ptr()->get_data_path() + "shared/*.png"); 382 vector<String> fnames; 383 glob(pattern, fnames, false); 384 sort(fnames.begin(), fnames.end()); 385 386 Ptr<AKAZE> akaze = AKAZE::create(); 387 Ptr<ORB> orb = ORB::create(); 388 Ptr<KAZE> kaze = KAZE::create(); 389 Ptr<BRISK> brisk = BRISK::create(); 390 size_t i, n = fnames.size(); 391 vector<KeyPoint> keypoints; 392 Mat descriptors; 393 orb->setMaxFeatures(5000); 394 395 for( i = 0; i < n; i++ ) 396 { 397 printf("%d. image: %s:\n", (int)i, fnames[i].c_str()); 398 if( strstr(fnames[i].c_str(), "MP.png") != 0 ) 399 continue; 400 bool checkCount = strstr(fnames[i].c_str(), "templ.png") == 0; 401 402 Mat img = imread(fnames[i], -1); 403 printf("\tAKAZE ... "); fflush(stdout); 404 akaze->detectAndCompute(img, noArray(), keypoints, descriptors); 405 printf("(%d keypoints) ", (int)keypoints.size()); fflush(stdout); 406 if( checkCount ) 407 { 408 EXPECT_GT((int)keypoints.size(), 0); 409 } 410 ASSERT_EQ(descriptors.rows, (int)keypoints.size()); 411 printf("ok\n"); 412 413 printf("\tKAZE ... "); fflush(stdout); 414 kaze->detectAndCompute(img, noArray(), keypoints, descriptors); 415 printf("(%d keypoints) ", (int)keypoints.size()); fflush(stdout); 416 if( checkCount ) 417 { 418 EXPECT_GT((int)keypoints.size(), 0); 419 } 420 ASSERT_EQ(descriptors.rows, (int)keypoints.size()); 421 printf("ok\n"); 422 423 printf("\tORB ... "); fflush(stdout); 424 orb->detectAndCompute(img, noArray(), keypoints, descriptors); 425 printf("(%d keypoints) ", (int)keypoints.size()); fflush(stdout); 426 if( checkCount ) 427 { 428 EXPECT_GT((int)keypoints.size(), 0); 429 } 430 ASSERT_EQ(descriptors.rows, (int)keypoints.size()); 431 printf("ok\n"); 432 433 printf("\tBRISK ... "); fflush(stdout); 434 brisk->detectAndCompute(img, noArray(), keypoints, descriptors); 435 printf("(%d keypoints) ", (int)keypoints.size()); fflush(stdout); 436 if( checkCount ) 437 { 438 EXPECT_GT((int)keypoints.size(), 0); 439 } 440 ASSERT_EQ(descriptors.rows, (int)keypoints.size()); 441 printf("ok\n"); 442 } 443 } 444