/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // Intel License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000, Intel Corporation, all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of Intel Corporation may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the Intel Corporation or contributors be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. // //M*/ #include "cvtest.h" #include "opencv2/core/core.hpp" using namespace std; using namespace cv; const string FEATURES2D_DIR = "features2d"; const string DETECTOR_DIR = FEATURES2D_DIR + "/feature_detectors"; const string DESCRIPTOR_DIR = FEATURES2D_DIR + "/descriptor_extractors"; const string IMAGE_FILENAME = "tsukuba.png"; /****************************************************************************************\ * Regression tests for feature detectors comparing keypoints. * \****************************************************************************************/ class CV_FeatureDetectorTest : public CvTest { public: CV_FeatureDetectorTest( const char* testName, const Ptr& _fdetector ) : CvTest( testName, "cv::FeatureDetector::detect"), fdetector(_fdetector) {} protected: virtual void run( int start_from ) { const float maxPtDif = 1.f; const float maxSizeDif = 1.f; const float maxAngleDif = 2.f; const float maxResponseDif = 0.1f; string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME; string resFilename = string(ts->get_data_path()) + DETECTOR_DIR + "/" + string(name) + ".xml.gz"; if( fdetector.empty() ) { ts->printf( CvTS::LOG, "Feature detector is empty" ); ts->set_failed_test_info( CvTS::FAIL_INVALID_TEST_DATA ); return; } Mat image = imread( imgFilename, 0 ); if( image.empty() ) { ts->printf( CvTS::LOG, "image %s can not be read \n", imgFilename.c_str() ); ts->set_failed_test_info( CvTS::FAIL_INVALID_TEST_DATA ); return; } FileStorage fs( resFilename, FileStorage::READ ); vector calcKeypoints; fdetector->detect( image, calcKeypoints ); if( fs.isOpened() ) // compare computed and valid keypoints { // TODO compare saved feature detector params with current ones vector validKeypoints; read( fs["keypoints"], validKeypoints ); if( validKeypoints.empty() ) { ts->printf( CvTS::LOG, "Keypoints can nod be read\n" ); ts->set_failed_test_info( CvTS::FAIL_INVALID_TEST_DATA ); return; } int progress = 0, progressCount = validKeypoints.size() * calcKeypoints.size(); int badPointCount = 0, commonPointCount = max(validKeypoints.size(), calcKeypoints.size()); for( size_t v = 0; v < validKeypoints.size(); v++ ) { int nearestIdx = -1; float minDist = std::numeric_limits::max(); for( size_t c = 0; c < calcKeypoints.size(); c++ ) { progress = update_progress( progress, v*calcKeypoints.size() + c, progressCount, 0 ); float curDist = norm( calcKeypoints[c].pt - validKeypoints[v].pt ); if( curDist < minDist ) { minDist = curDist; nearestIdx = c; } } if( minDist > maxPtDif || fabs(calcKeypoints[nearestIdx].size - validKeypoints[v].size) > maxSizeDif || abs(calcKeypoints[nearestIdx].angle - validKeypoints[v].angle) > maxAngleDif || abs(calcKeypoints[nearestIdx].response - validKeypoints[v].response) > maxResponseDif || calcKeypoints[nearestIdx].octave != validKeypoints[v].octave // TODO !!!!!!! /*|| calcKeypoints[nearestIdx].class_id != validKeypoints[v].class_id*/ ) { badPointCount++; } } ts->printf( CvTS::LOG, "badPointCount = %d; validPointCount = %d; calcPointCount = %d\n", badPointCount, validKeypoints.size(), calcKeypoints.size() ); if( badPointCount > 0.9 * commonPointCount ) { ts->printf( CvTS::LOG, "Bad accuracy!\n" ); ts->set_failed_test_info( CvTS::FAIL_BAD_ACCURACY ); return; } } else // write { fs.open( resFilename, FileStorage::WRITE ); if( !fs.isOpened() ) { ts->printf( CvTS::LOG, "file %s can not be opened to write\n", resFilename.c_str() ); ts->set_failed_test_info( CvTS::FAIL_INVALID_TEST_DATA ); return; } else { fs << "detector_params" << "{"; fdetector->write( fs ); fs << "}"; write( fs, "keypoints", calcKeypoints ); } } ts->set_failed_test_info( CvTS::OK ); } Ptr fdetector; }; CV_FeatureDetectorTest fastTest( "detector-fast", createFeatureDetector("FAST") ); CV_FeatureDetectorTest gfttTest( "detector-gftt", createFeatureDetector("GFTT") ); CV_FeatureDetectorTest harrisTest( "detector-harris", createFeatureDetector("HARRIS") ); CV_FeatureDetectorTest mserTest( "detector-mser", createFeatureDetector("MSER") ); CV_FeatureDetectorTest siftTest( "detector-sift", createFeatureDetector("SIFT") ); CV_FeatureDetectorTest starTest( "detector-star", createFeatureDetector("STAR") ); CV_FeatureDetectorTest surfTest( "detector-surf", createFeatureDetector("SURF") ); /****************************************************************************************\ * Regression tests for descriptor extractors. * \****************************************************************************************/ static void writeMatInBin( const Mat& mat, const string& filename ) { FILE* f = fopen( filename.c_str(), "wb"); if( f ) { int type = mat.type(); fwrite( (void*)&mat.rows, sizeof(int), 1, f ); fwrite( (void*)&mat.cols, sizeof(int), 1, f ); fwrite( (void*)&type, sizeof(int), 1, f ); fwrite( (void*)&mat.step, sizeof(int), 1, f ); fwrite( (void*)mat.data, 1, mat.step*mat.rows, f ); fclose(f); } } static Mat readMatFromBin( const string& filename ) { FILE* f = fopen( filename.c_str(), "rb" ); if( f ) { int rows, cols, type, step; fread( (void*)&rows, sizeof(int), 1, f ); fread( (void*)&cols, sizeof(int), 1, f ); fread( (void*)&type, sizeof(int), 1, f ); fread( (void*)&step, sizeof(int), 1, f ); uchar* data = (uchar*)cvAlloc(step*rows); fread( (void*)data, 1, step*rows, f ); fclose(f); return Mat( rows, cols, type, data ); } return Mat(); } class CV_DescriptorExtractorTest : public CvTest { public: CV_DescriptorExtractorTest( const char* testName, float _maxNormDif, const Ptr& _dextractor, float _prevTime ) : CvTest( testName, "cv::DescriptorExtractor::compute" ), maxNormDif(_maxNormDif), prevTime(_prevTime), dextractor(_dextractor) {} protected: virtual void createDescriptorExtractor() {} void run(int) { createDescriptorExtractor(); if( dextractor.empty() ) { ts->printf(CvTS::LOG, "Descriptor extractor is empty\n"); ts->set_failed_test_info( CvTS::FAIL_INVALID_TEST_DATA ); return; } string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME; Mat img = imread( imgFilename, 0 ); if( img.empty() ) { ts->printf( CvTS::LOG, "image %s can not be read\n", imgFilename.c_str() ); ts->set_failed_test_info( CvTS::FAIL_INVALID_TEST_DATA ); return; } vector keypoints; FileStorage fs( string(ts->get_data_path()) + FEATURES2D_DIR + "/keypoints.xml.gz", FileStorage::READ ); if( fs.isOpened() ) read( fs.getFirstTopLevelNode(), keypoints ); else { ts->printf( CvTS::LOG, "Compute and write keypoints\n" ); fs.open( string(ts->get_data_path()) + FEATURES2D_DIR + "/keypoints.xml.gz", FileStorage::WRITE ); if( fs.isOpened() ) { SurfFeatureDetector fd; fd.detect(img, keypoints); write( fs, "keypoints", keypoints ); } else { ts->printf(CvTS::LOG, "File for writting keypoints can not be opened\n"); ts->set_failed_test_info( CvTS::FAIL_INVALID_TEST_DATA ); return; } } Mat calcDescriptors; double t = (double)getTickCount(); dextractor->compute( img, keypoints, calcDescriptors ); t = getTickCount() - t; ts->printf(CvTS::LOG, "\nAverage time of computiting one descriptor = %g ms (previous time = %g ms)\n", t/((double)cvGetTickFrequency()*1000.)/calcDescriptors.rows, prevTime ); // TODO read and write descriptor extractor parameters and check them Mat validDescriptors = readDescriptors(); if( !validDescriptors.empty() ) { double normValue = norm( calcDescriptors, validDescriptors, NORM_INF ); ts->printf( CvTS::LOG, "nofm (inf) BTW valid and calculated float descriptors = %f\n", normValue ); if( normValue > maxNormDif ) ts->set_failed_test_info( CvTS::FAIL_BAD_ACCURACY ); } else { if( !writeDescriptors( calcDescriptors ) ) { ts->printf( CvTS::LOG, "Descriptors can not be written\n" ); ts->set_failed_test_info( CvTS::FAIL_INVALID_TEST_DATA ); return; } } } virtual Mat readDescriptors() { Mat res = readMatFromBin( string(ts->get_data_path()) + DESCRIPTOR_DIR + "/" + string(name) ); return res; } virtual bool writeDescriptors( Mat& descs ) { writeMatInBin( descs, string(ts->get_data_path()) + DESCRIPTOR_DIR + "/" + string(name) ); return true; } const float maxNormDif; const float prevTime; Ptr dextractor; }; template class CV_CalonderDescriptorExtractorTest : public CV_DescriptorExtractorTest { public: CV_CalonderDescriptorExtractorTest( const char* testName, float _normDif, float _prevTime ) : CV_DescriptorExtractorTest( testName, _normDif, Ptr(), _prevTime ) {} virtual void createDescriptorExtractor() { dextractor = new CalonderDescriptorExtractor( string(ts->get_data_path()) + FEATURES2D_DIR + "/calonder_classifier.rtc"); } }; CV_DescriptorExtractorTest siftDescriptorTest( "descriptor-sift", 0.03f, createDescriptorExtractor("SIFT"), 8.06652f ); CV_DescriptorExtractorTest surfDescriptorTest( "descriptor-surf", 0.035f, createDescriptorExtractor("SURF"), 0.147372f ); //CV_DescriptorExtractorTest oppSiftDescriptorTest( "descriptor-opponent-sift", 0.008f, // createDescriptorExtractor("OpponentSIFT"), 8.06652f ); //CV_DescriptorExtractorTest oppurfDescriptorTest( "descriptor-opponent-surf", 0.02f, // createDescriptorExtractor("OpponentSURF"), 0.147372f ); #if CV_SSE2 CV_CalonderDescriptorExtractorTest ucharCalonderTest( "descriptor-calonder-uchar", std::numeric_limits::epsilon() + 1, 0.0132175f ); CV_CalonderDescriptorExtractorTest floatCalonderTest( "descriptor-calonder-float", std::numeric_limits::epsilon(), 0.0221308f ); #endif // CV_SSE2