From 507f546158cf9dfd280345da5d3c81d4f3fbb4f1 Mon Sep 17 00:00:00 2001 From: Maria Dimashova Date: Thu, 12 Jul 2012 13:57:17 +0000 Subject: [PATCH] split file of features2d tests --- .../test/test_descriptors_regression.cpp | 332 ++++++++++++ .../features2d/test/test_detectors_regression.cpp | 296 +++++++++++ ...eatures2d.cpp => test_matchers_algorithmic.cpp} | 581 --------------------- modules/features2d/test/test_orb.cpp | 92 ++++ 4 files changed, 720 insertions(+), 581 deletions(-) create mode 100644 modules/features2d/test/test_descriptors_regression.cpp create mode 100644 modules/features2d/test/test_detectors_regression.cpp rename modules/features2d/test/{test_features2d.cpp => test_matchers_algorithmic.cpp} (50%) create mode 100644 modules/features2d/test/test_orb.cpp diff --git a/modules/features2d/test/test_descriptors_regression.cpp b/modules/features2d/test/test_descriptors_regression.cpp new file mode 100644 index 0000000..b53ef57 --- /dev/null +++ b/modules/features2d/test/test_descriptors_regression.cpp @@ -0,0 +1,332 @@ +/*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 "test_precomp.hpp" +#include "opencv2/highgui/highgui.hpp" + +using namespace std; +using namespace cv; + +const string FEATURES2D_DIR = "features2d"; +const string IMAGE_FILENAME = "tsukuba.png"; +const string DESCRIPTOR_DIR = FEATURES2D_DIR + "/descriptor_extractors"; + +/****************************************************************************************\ +* 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 ); + int dataSize = (int)(mat.step * mat.rows * mat.channels()); + fwrite( (void*)&dataSize, sizeof(int), 1, f ); + fwrite( (void*)mat.data, 1, dataSize, f ); + fclose(f); + } +} + +static Mat readMatFromBin( const string& filename ) +{ + FILE* f = fopen( filename.c_str(), "rb" ); + if( f ) + { + int rows, cols, type, dataSize; + size_t elements_read1 = fread( (void*)&rows, sizeof(int), 1, f ); + size_t elements_read2 = fread( (void*)&cols, sizeof(int), 1, f ); + size_t elements_read3 = fread( (void*)&type, sizeof(int), 1, f ); + size_t elements_read4 = fread( (void*)&dataSize, sizeof(int), 1, f ); + CV_Assert(elements_read1 == 1 && elements_read2 == 1 && elements_read3 == 1 && elements_read4 == 1); + + uchar* data = (uchar*)cvAlloc(dataSize); + size_t elements_read = fread( (void*)data, 1, dataSize, f ); + CV_Assert(elements_read == (size_t)(dataSize)); + fclose(f); + + return Mat( rows, cols, type, data ); + } + return Mat(); +} + +template +class CV_DescriptorExtractorTest : public cvtest::BaseTest +{ +public: + typedef typename Distance::ValueType ValueType; + typedef typename Distance::ResultType DistanceType; + + CV_DescriptorExtractorTest( const string _name, DistanceType _maxDist, const Ptr& _dextractor, + Distance d = Distance() ): + name(_name), maxDist(_maxDist), dextractor(_dextractor), distance(d) {} +protected: + virtual void createDescriptorExtractor() {} + + void compareDescriptors( const Mat& validDescriptors, const Mat& calcDescriptors ) + { + if( validDescriptors.size != calcDescriptors.size || validDescriptors.type() != calcDescriptors.type() ) + { + ts->printf(cvtest::TS::LOG, "Valid and computed descriptors matrices must have the same size and type.\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + CV_Assert( DataType::type == validDescriptors.type() ); + + int dimension = validDescriptors.cols; + DistanceType curMaxDist = std::numeric_limits::min(); + for( int y = 0; y < validDescriptors.rows; y++ ) + { + DistanceType dist = distance( validDescriptors.ptr(y), calcDescriptors.ptr(y), dimension ); + if( dist > curMaxDist ) + curMaxDist = dist; + } + + stringstream ss; + ss << "Max distance between valid and computed descriptors " << curMaxDist; + if( curMaxDist < maxDist ) + ss << "." << endl; + else + { + ss << ">" << maxDist << " - bad accuracy!"<< endl; + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + } + ts->printf(cvtest::TS::LOG, ss.str().c_str() ); + } + + void emptyDataTest() + { + assert( !dextractor.empty() ); + + // One image. + Mat image; + vector keypoints; + Mat descriptors; + + try + { + dextractor->compute( image, keypoints, descriptors ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "compute() on empty image and empty keypoints must not generate exception (1).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + } + + image.create( 50, 50, CV_8UC3 ); + try + { + dextractor->compute( image, keypoints, descriptors ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "compute() on nonempty image and empty keypoints must not generate exception (1).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + } + + // Several images. + vector images; + vector > keypointsCollection; + vector descriptorsCollection; + try + { + dextractor->compute( images, keypointsCollection, descriptorsCollection ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "compute() on empty images and empty keypoints collection must not generate exception (2).\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + } + } + + void regressionTest() + { + assert( !dextractor.empty() ); + + // Read the test image. + string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME; + + Mat img = imread( imgFilename ); + if( img.empty() ) + { + ts->printf( cvtest::TS::LOG, "Image %s can not be read.\n", imgFilename.c_str() ); + ts->set_failed_test_info( cvtest::TS::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 ); + + Mat calcDescriptors; + double t = (double)getTickCount(); + dextractor->compute( img, keypoints, calcDescriptors ); + t = getTickCount() - t; + ts->printf(cvtest::TS::LOG, "\nAverage time of computing one descriptor = %g ms.\n", t/((double)cvGetTickFrequency()*1000.)/calcDescriptors.rows); + + if( calcDescriptors.rows != (int)keypoints.size() ) + { + ts->printf( cvtest::TS::LOG, "Count of computed descriptors and keypoints count must be equal.\n" ); + ts->printf( cvtest::TS::LOG, "Count of keypoints is %d.\n", (int)keypoints.size() ); + ts->printf( cvtest::TS::LOG, "Count of computed descriptors is %d.\n", calcDescriptors.rows ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + if( calcDescriptors.cols != dextractor->descriptorSize() || calcDescriptors.type() != dextractor->descriptorType() ) + { + ts->printf( cvtest::TS::LOG, "Incorrect descriptor size or descriptor type.\n" ); + ts->printf( cvtest::TS::LOG, "Expected size is %d.\n", dextractor->descriptorSize() ); + ts->printf( cvtest::TS::LOG, "Calculated size is %d.\n", calcDescriptors.cols ); + ts->printf( cvtest::TS::LOG, "Expected type is %d.\n", dextractor->descriptorType() ); + ts->printf( cvtest::TS::LOG, "Calculated type is %d.\n", calcDescriptors.type() ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + // TODO read and write descriptor extractor parameters and check them + Mat validDescriptors = readDescriptors(); + if( !validDescriptors.empty() ) + compareDescriptors( validDescriptors, calcDescriptors ); + else + { + if( !writeDescriptors( calcDescriptors ) ) + { + ts->printf( cvtest::TS::LOG, "Descriptors can not be written.\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + } + } + else + { + ts->printf( cvtest::TS::LOG, "Compute and write keypoints.\n" ); + fs.open( string(ts->get_data_path()) + FEATURES2D_DIR + "/keypoints.xml.gz", FileStorage::WRITE ); + if( fs.isOpened() ) + { + ORB fd; + fd.detect(img, keypoints); + write( fs, "keypoints", keypoints ); + } + else + { + ts->printf(cvtest::TS::LOG, "File for writting keypoints can not be opened.\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + } + } + + void run(int) + { + createDescriptorExtractor(); + if( dextractor.empty() ) + { + ts->printf(cvtest::TS::LOG, "Descriptor extractor is empty.\n"); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + emptyDataTest(); + regressionTest(); + + ts->set_failed_test_info( cvtest::TS::OK ); + } + + 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; + } + + string name; + const DistanceType maxDist; + Ptr dextractor; + Distance distance; + +private: + CV_DescriptorExtractorTest& operator=(const CV_DescriptorExtractorTest&) { return *this; } +}; + +/****************************************************************************************\ +* Tests registrations * +\****************************************************************************************/ + +TEST( Features2d_DescriptorExtractor_ORB, regression ) +{ + // TODO adjust the parameters below + CV_DescriptorExtractorTest test( "descriptor-orb", (CV_DescriptorExtractorTest::DistanceType)12.f, + DescriptorExtractor::create("ORB") ); + test.safe_run(); +} + +TEST( Features2d_DescriptorExtractor_FREAK, regression ) +{ + // TODO adjust the parameters below + CV_DescriptorExtractorTest test( "descriptor-freak", (CV_DescriptorExtractorTest::DistanceType)12.f, + DescriptorExtractor::create("FREAK") ); + test.safe_run(); +} + +TEST( Features2d_DescriptorExtractor_BRIEF, regression ) +{ + CV_DescriptorExtractorTest test( "descriptor-brief", 1, + DescriptorExtractor::create("BRIEF") ); + test.safe_run(); +} + +TEST( Features2d_DescriptorExtractor_OpponentBRIEF, regression ) +{ + CV_DescriptorExtractorTest test( "descriptor-opponent-brief", 1, + DescriptorExtractor::create("OpponentBRIEF") ); + test.safe_run(); +} diff --git a/modules/features2d/test/test_detectors_regression.cpp b/modules/features2d/test/test_detectors_regression.cpp new file mode 100644 index 0000000..0077f84 --- /dev/null +++ b/modules/features2d/test/test_detectors_regression.cpp @@ -0,0 +1,296 @@ +/*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 "test_precomp.hpp" +#include "opencv2/highgui/highgui.hpp" + +using namespace std; +using namespace cv; + +const string FEATURES2D_DIR = "features2d"; +const string IMAGE_FILENAME = "tsukuba.png"; +const string DETECTOR_DIR = FEATURES2D_DIR + "/feature_detectors"; + +/****************************************************************************************\ +* Regression tests for feature detectors comparing keypoints. * +\****************************************************************************************/ + +class CV_FeatureDetectorTest : public cvtest::BaseTest +{ +public: + CV_FeatureDetectorTest( const string& _name, const Ptr& _fdetector ) : + name(_name), fdetector(_fdetector) {} + +protected: + bool isSimilarKeypoints( const KeyPoint& p1, const KeyPoint& p2 ); + void compareKeypointSets( const vector& validKeypoints, const vector& calcKeypoints ); + + void emptyDataTest(); + void regressionTest(); // TODO test of detect() with mask + + virtual void run( int ); + + string name; + Ptr fdetector; +}; + +void CV_FeatureDetectorTest::emptyDataTest() +{ + // One image. + Mat image; + vector keypoints; + try + { + fdetector->detect( image, keypoints ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "detect() on empty image must not generate exception (1).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + if( !keypoints.empty() ) + { + ts->printf( cvtest::TS::LOG, "detect() on empty image must return empty keypoints vector (1).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + // Several images. + vector images; + vector > keypointCollection; + try + { + fdetector->detect( images, keypointCollection ); + } + catch(...) + { + ts->printf( cvtest::TS::LOG, "detect() on empty image vector must not generate exception (2).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } +} + +bool CV_FeatureDetectorTest::isSimilarKeypoints( const KeyPoint& p1, const KeyPoint& p2 ) +{ + const float maxPtDif = 1.f; + const float maxSizeDif = 1.f; + const float maxAngleDif = 2.f; + const float maxResponseDif = 0.1f; + + float dist = (float)norm( p1.pt - p2.pt ); + return (dist < maxPtDif && + fabs(p1.size - p2.size) < maxSizeDif && + abs(p1.angle - p2.angle) < maxAngleDif && + abs(p1.response - p2.response) < maxResponseDif && + p1.octave == p2.octave && + p1.class_id == p2.class_id ); +} + +void CV_FeatureDetectorTest::compareKeypointSets( const vector& validKeypoints, const vector& calcKeypoints ) +{ + const float maxCountRatioDif = 0.01f; + + // Compare counts of validation and calculated keypoints. + float countRatio = (float)validKeypoints.size() / (float)calcKeypoints.size(); + if( countRatio < 1 - maxCountRatioDif || countRatio > 1.f + maxCountRatioDif ) + { + ts->printf( cvtest::TS::LOG, "Bad keypoints count ratio (validCount = %d, calcCount = %d).\n", + validKeypoints.size(), calcKeypoints.size() ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + int progress = 0, progressCount = (int)(validKeypoints.size() * calcKeypoints.size()); + int badPointCount = 0, commonPointCount = max((int)validKeypoints.size(), (int)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, (int)(v*calcKeypoints.size() + c), progressCount, 0 ); + float curDist = (float)norm( calcKeypoints[c].pt - validKeypoints[v].pt ); + if( curDist < minDist ) + { + minDist = curDist; + nearestIdx = (int)c; + } + } + + assert( minDist >= 0 ); + if( !isSimilarKeypoints( validKeypoints[v], calcKeypoints[nearestIdx] ) ) + badPointCount++; + } + ts->printf( cvtest::TS::LOG, "badPointCount = %d; validPointCount = %d; calcPointCount = %d\n", + badPointCount, validKeypoints.size(), calcKeypoints.size() ); + if( badPointCount > 0.9 * commonPointCount ) + { + ts->printf( cvtest::TS::LOG, " - Bad accuracy!\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + return; + } + ts->printf( cvtest::TS::LOG, " - OK\n" ); +} + +void CV_FeatureDetectorTest::regressionTest() +{ + assert( !fdetector.empty() ); + string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME; + string resFilename = string(ts->get_data_path()) + DETECTOR_DIR + "/" + string(name) + ".xml.gz"; + + // Read the test image. + Mat image = imread( imgFilename ); + if( image.empty() ) + { + ts->printf( cvtest::TS::LOG, "Image %s can not be read.\n", imgFilename.c_str() ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + FileStorage fs( resFilename, FileStorage::READ ); + + // Compute keypoints. + vector calcKeypoints; + fdetector->detect( image, calcKeypoints ); + + if( fs.isOpened() ) // Compare computed and valid keypoints. + { + // TODO compare saved feature detector params with current ones + + // Read validation keypoints set. + vector validKeypoints; + read( fs["keypoints"], validKeypoints ); + if( validKeypoints.empty() ) + { + ts->printf( cvtest::TS::LOG, "Keypoints can not be read.\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + compareKeypointSets( validKeypoints, calcKeypoints ); + } + else // Write detector parameters and computed keypoints as validation data. + { + fs.open( resFilename, FileStorage::WRITE ); + if( !fs.isOpened() ) + { + ts->printf( cvtest::TS::LOG, "File %s can not be opened to write.\n", resFilename.c_str() ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + else + { + fs << "detector_params" << "{"; + fdetector->write( fs ); + fs << "}"; + + write( fs, "keypoints", calcKeypoints ); + } + } +} + +void CV_FeatureDetectorTest::run( int /*start_from*/ ) +{ + if( fdetector.empty() ) + { + ts->printf( cvtest::TS::LOG, "Feature detector is empty.\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + emptyDataTest(); + regressionTest(); + + ts->set_failed_test_info( cvtest::TS::OK ); +} + +/****************************************************************************************\ +* Tests registrations * +\****************************************************************************************/ + +TEST( Features2d_Detector_FAST, regression ) +{ + CV_FeatureDetectorTest test( "detector-fast", FeatureDetector::create("FAST") ); + test.safe_run(); +} + +TEST( Features2d_Detector_GFTT, regression ) +{ + CV_FeatureDetectorTest test( "detector-gftt", FeatureDetector::create("GFTT") ); + test.safe_run(); +} + +TEST( Features2d_Detector_Harris, regression ) +{ + CV_FeatureDetectorTest test( "detector-harris", FeatureDetector::create("HARRIS") ); + test.safe_run(); +} + +TEST( Features2d_Detector_MSER, DISABLED_regression ) +{ + CV_FeatureDetectorTest test( "detector-mser", FeatureDetector::create("MSER") ); + test.safe_run(); +} + +TEST( Features2d_Detector_STAR, regression ) +{ + CV_FeatureDetectorTest test( "detector-star", FeatureDetector::create("STAR") ); + test.safe_run(); +} + +TEST( Features2d_Detector_ORB, regression ) +{ + CV_FeatureDetectorTest test( "detector-orb", FeatureDetector::create("ORB") ); + test.safe_run(); +} + +TEST( Features2d_Detector_GridFAST, regression ) +{ + CV_FeatureDetectorTest test( "detector-grid-fast", FeatureDetector::create("GridFAST") ); + test.safe_run(); +} + +TEST( Features2d_Detector_PyramidFAST, regression ) +{ + CV_FeatureDetectorTest test( "detector-pyramid-fast", FeatureDetector::create("PyramidFAST") ); + test.safe_run(); +} diff --git a/modules/features2d/test/test_features2d.cpp b/modules/features2d/test/test_matchers_algorithmic.cpp similarity index 50% rename from modules/features2d/test/test_features2d.cpp rename to modules/features2d/test/test_matchers_algorithmic.cpp index 813a8e2..a3a5efe 100644 --- a/modules/features2d/test/test_features2d.cpp +++ b/modules/features2d/test/test_matchers_algorithmic.cpp @@ -46,453 +46,9 @@ 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::BaseTest -{ -public: - CV_FeatureDetectorTest( const string& _name, const Ptr& _fdetector ) : - name(_name), fdetector(_fdetector) {} - -protected: - bool isSimilarKeypoints( const KeyPoint& p1, const KeyPoint& p2 ); - void compareKeypointSets( const vector& validKeypoints, const vector& calcKeypoints ); - - void emptyDataTest(); - void regressionTest(); // TODO test of detect() with mask - - virtual void run( int ); - - string name; - Ptr fdetector; -}; - -void CV_FeatureDetectorTest::emptyDataTest() -{ - // One image. - Mat image; - vector keypoints; - try - { - fdetector->detect( image, keypoints ); - } - catch(...) - { - ts->printf( cvtest::TS::LOG, "detect() on empty image must not generate exception (1).\n" ); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); - } - - if( !keypoints.empty() ) - { - ts->printf( cvtest::TS::LOG, "detect() on empty image must return empty keypoints vector (1).\n" ); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); - return; - } - - // Several images. - vector images; - vector > keypointCollection; - try - { - fdetector->detect( images, keypointCollection ); - } - catch(...) - { - ts->printf( cvtest::TS::LOG, "detect() on empty image vector must not generate exception (2).\n" ); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); - } -} - -bool CV_FeatureDetectorTest::isSimilarKeypoints( const KeyPoint& p1, const KeyPoint& p2 ) -{ - const float maxPtDif = 1.f; - const float maxSizeDif = 1.f; - const float maxAngleDif = 2.f; - const float maxResponseDif = 0.1f; - - float dist = (float)norm( p1.pt - p2.pt ); - return (dist < maxPtDif && - fabs(p1.size - p2.size) < maxSizeDif && - abs(p1.angle - p2.angle) < maxAngleDif && - abs(p1.response - p2.response) < maxResponseDif && - p1.octave == p2.octave && - p1.class_id == p2.class_id ); -} - -void CV_FeatureDetectorTest::compareKeypointSets( const vector& validKeypoints, const vector& calcKeypoints ) -{ - const float maxCountRatioDif = 0.01f; - - // Compare counts of validation and calculated keypoints. - float countRatio = (float)validKeypoints.size() / (float)calcKeypoints.size(); - if( countRatio < 1 - maxCountRatioDif || countRatio > 1.f + maxCountRatioDif ) - { - ts->printf( cvtest::TS::LOG, "Bad keypoints count ratio (validCount = %d, calcCount = %d).\n", - validKeypoints.size(), calcKeypoints.size() ); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); - return; - } - - int progress = 0, progressCount = (int)(validKeypoints.size() * calcKeypoints.size()); - int badPointCount = 0, commonPointCount = max((int)validKeypoints.size(), (int)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, (int)(v*calcKeypoints.size() + c), progressCount, 0 ); - float curDist = (float)norm( calcKeypoints[c].pt - validKeypoints[v].pt ); - if( curDist < minDist ) - { - minDist = curDist; - nearestIdx = (int)c; - } - } - - assert( minDist >= 0 ); - if( !isSimilarKeypoints( validKeypoints[v], calcKeypoints[nearestIdx] ) ) - badPointCount++; - } - ts->printf( cvtest::TS::LOG, "badPointCount = %d; validPointCount = %d; calcPointCount = %d\n", - badPointCount, validKeypoints.size(), calcKeypoints.size() ); - if( badPointCount > 0.9 * commonPointCount ) - { - ts->printf( cvtest::TS::LOG, " - Bad accuracy!\n" ); - ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); - return; - } - ts->printf( cvtest::TS::LOG, " - OK\n" ); -} - -void CV_FeatureDetectorTest::regressionTest() -{ - assert( !fdetector.empty() ); - string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME; - string resFilename = string(ts->get_data_path()) + DETECTOR_DIR + "/" + string(name) + ".xml.gz"; - - // Read the test image. - Mat image = imread( imgFilename ); - if( image.empty() ) - { - ts->printf( cvtest::TS::LOG, "Image %s can not be read.\n", imgFilename.c_str() ); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); - return; - } - - FileStorage fs( resFilename, FileStorage::READ ); - - // Compute keypoints. - vector calcKeypoints; - fdetector->detect( image, calcKeypoints ); - - if( fs.isOpened() ) // Compare computed and valid keypoints. - { - // TODO compare saved feature detector params with current ones - - // Read validation keypoints set. - vector validKeypoints; - read( fs["keypoints"], validKeypoints ); - if( validKeypoints.empty() ) - { - ts->printf( cvtest::TS::LOG, "Keypoints can not be read.\n" ); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); - return; - } - - compareKeypointSets( validKeypoints, calcKeypoints ); - } - else // Write detector parameters and computed keypoints as validation data. - { - fs.open( resFilename, FileStorage::WRITE ); - if( !fs.isOpened() ) - { - ts->printf( cvtest::TS::LOG, "File %s can not be opened to write.\n", resFilename.c_str() ); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); - return; - } - else - { - fs << "detector_params" << "{"; - fdetector->write( fs ); - fs << "}"; - - write( fs, "keypoints", calcKeypoints ); - } - } -} - -void CV_FeatureDetectorTest::run( int /*start_from*/ ) -{ - if( fdetector.empty() ) - { - ts->printf( cvtest::TS::LOG, "Feature detector is empty.\n" ); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); - return; - } - - emptyDataTest(); - regressionTest(); - - ts->set_failed_test_info( cvtest::TS::OK ); -} - -/****************************************************************************************\ -* 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 ); - int dataSize = (int)(mat.step * mat.rows * mat.channels()); - fwrite( (void*)&dataSize, sizeof(int), 1, f ); - fwrite( (void*)mat.data, 1, dataSize, f ); - fclose(f); - } -} - -static Mat readMatFromBin( const string& filename ) -{ - FILE* f = fopen( filename.c_str(), "rb" ); - if( f ) - { - int rows, cols, type, dataSize; - size_t elements_read1 = fread( (void*)&rows, sizeof(int), 1, f ); - size_t elements_read2 = fread( (void*)&cols, sizeof(int), 1, f ); - size_t elements_read3 = fread( (void*)&type, sizeof(int), 1, f ); - size_t elements_read4 = fread( (void*)&dataSize, sizeof(int), 1, f ); - CV_Assert(elements_read1 == 1 && elements_read2 == 1 && elements_read3 == 1 && elements_read4 == 1); - - uchar* data = (uchar*)cvAlloc(dataSize); - size_t elements_read = fread( (void*)data, 1, dataSize, f ); - CV_Assert(elements_read == (size_t)(dataSize)); - fclose(f); - - return Mat( rows, cols, type, data ); - } - return Mat(); -} - -template -class CV_DescriptorExtractorTest : public cvtest::BaseTest -{ -public: - typedef typename Distance::ValueType ValueType; - typedef typename Distance::ResultType DistanceType; - - CV_DescriptorExtractorTest( const string _name, DistanceType _maxDist, const Ptr& _dextractor, - Distance d = Distance() ): - name(_name), maxDist(_maxDist), dextractor(_dextractor), distance(d) {} -protected: - virtual void createDescriptorExtractor() {} - - void compareDescriptors( const Mat& validDescriptors, const Mat& calcDescriptors ) - { - if( validDescriptors.size != calcDescriptors.size || validDescriptors.type() != calcDescriptors.type() ) - { - ts->printf(cvtest::TS::LOG, "Valid and computed descriptors matrices must have the same size and type.\n"); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); - return; - } - - CV_Assert( DataType::type == validDescriptors.type() ); - - int dimension = validDescriptors.cols; - DistanceType curMaxDist = std::numeric_limits::min(); - for( int y = 0; y < validDescriptors.rows; y++ ) - { - DistanceType dist = distance( validDescriptors.ptr(y), calcDescriptors.ptr(y), dimension ); - if( dist > curMaxDist ) - curMaxDist = dist; - } - - stringstream ss; - ss << "Max distance between valid and computed descriptors " << curMaxDist; - if( curMaxDist < maxDist ) - ss << "." << endl; - else - { - ss << ">" << maxDist << " - bad accuracy!"<< endl; - ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); - } - ts->printf(cvtest::TS::LOG, ss.str().c_str() ); - } - - void emptyDataTest() - { - assert( !dextractor.empty() ); - - // One image. - Mat image; - vector keypoints; - Mat descriptors; - - try - { - dextractor->compute( image, keypoints, descriptors ); - } - catch(...) - { - ts->printf( cvtest::TS::LOG, "compute() on empty image and empty keypoints must not generate exception (1).\n"); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); - } - - image.create( 50, 50, CV_8UC3 ); - try - { - dextractor->compute( image, keypoints, descriptors ); - } - catch(...) - { - ts->printf( cvtest::TS::LOG, "compute() on nonempty image and empty keypoints must not generate exception (1).\n"); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); - } - - // Several images. - vector images; - vector > keypointsCollection; - vector descriptorsCollection; - try - { - dextractor->compute( images, keypointsCollection, descriptorsCollection ); - } - catch(...) - { - ts->printf( cvtest::TS::LOG, "compute() on empty images and empty keypoints collection must not generate exception (2).\n"); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); - } - } - - void regressionTest() - { - assert( !dextractor.empty() ); - - // Read the test image. - string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME; - - Mat img = imread( imgFilename ); - if( img.empty() ) - { - ts->printf( cvtest::TS::LOG, "Image %s can not be read.\n", imgFilename.c_str() ); - ts->set_failed_test_info( cvtest::TS::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 ); - - Mat calcDescriptors; - double t = (double)getTickCount(); - dextractor->compute( img, keypoints, calcDescriptors ); - t = getTickCount() - t; - ts->printf(cvtest::TS::LOG, "\nAverage time of computing one descriptor = %g ms.\n", t/((double)cvGetTickFrequency()*1000.)/calcDescriptors.rows); - - if( calcDescriptors.rows != (int)keypoints.size() ) - { - ts->printf( cvtest::TS::LOG, "Count of computed descriptors and keypoints count must be equal.\n" ); - ts->printf( cvtest::TS::LOG, "Count of keypoints is %d.\n", (int)keypoints.size() ); - ts->printf( cvtest::TS::LOG, "Count of computed descriptors is %d.\n", calcDescriptors.rows ); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); - return; - } - - if( calcDescriptors.cols != dextractor->descriptorSize() || calcDescriptors.type() != dextractor->descriptorType() ) - { - ts->printf( cvtest::TS::LOG, "Incorrect descriptor size or descriptor type.\n" ); - ts->printf( cvtest::TS::LOG, "Expected size is %d.\n", dextractor->descriptorSize() ); - ts->printf( cvtest::TS::LOG, "Calculated size is %d.\n", calcDescriptors.cols ); - ts->printf( cvtest::TS::LOG, "Expected type is %d.\n", dextractor->descriptorType() ); - ts->printf( cvtest::TS::LOG, "Calculated type is %d.\n", calcDescriptors.type() ); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); - return; - } - - // TODO read and write descriptor extractor parameters and check them - Mat validDescriptors = readDescriptors(); - if( !validDescriptors.empty() ) - compareDescriptors( validDescriptors, calcDescriptors ); - else - { - if( !writeDescriptors( calcDescriptors ) ) - { - ts->printf( cvtest::TS::LOG, "Descriptors can not be written.\n" ); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); - return; - } - } - } - else - { - ts->printf( cvtest::TS::LOG, "Compute and write keypoints.\n" ); - fs.open( string(ts->get_data_path()) + FEATURES2D_DIR + "/keypoints.xml.gz", FileStorage::WRITE ); - if( fs.isOpened() ) - { - ORB fd; - fd.detect(img, keypoints); - write( fs, "keypoints", keypoints ); - } - else - { - ts->printf(cvtest::TS::LOG, "File for writting keypoints can not be opened.\n"); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); - return; - } - } - } - - void run(int) - { - createDescriptorExtractor(); - if( dextractor.empty() ) - { - ts->printf(cvtest::TS::LOG, "Descriptor extractor is empty.\n"); - ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); - return; - } - - emptyDataTest(); - regressionTest(); - - ts->set_failed_test_info( cvtest::TS::OK ); - } - - 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; - } - - string name; - const DistanceType maxDist; - Ptr dextractor; - Distance distance; - -private: - CV_DescriptorExtractorTest& operator=(const CV_DescriptorExtractorTest&) { return *this; } -}; - -/****************************************************************************************\ * Algorithmic tests for descriptor matchers * \****************************************************************************************/ class CV_DescriptorMatcherTest : public cvtest::BaseTest @@ -974,95 +530,6 @@ void CV_DescriptorMatcherTest::run( int ) * Tests registrations * \****************************************************************************************/ -/* - * Detectors - */ - -TEST( Features2d_Detector_FAST, regression ) -{ - CV_FeatureDetectorTest test( "detector-fast", FeatureDetector::create("FAST") ); - test.safe_run(); -} - -TEST( Features2d_Detector_GFTT, regression ) -{ - CV_FeatureDetectorTest test( "detector-gftt", FeatureDetector::create("GFTT") ); - test.safe_run(); -} - -TEST( Features2d_Detector_Harris, regression ) -{ - CV_FeatureDetectorTest test( "detector-harris", FeatureDetector::create("HARRIS") ); - test.safe_run(); -} - -TEST( Features2d_Detector_MSER, DISABLED_regression ) -{ - CV_FeatureDetectorTest test( "detector-mser", FeatureDetector::create("MSER") ); - test.safe_run(); -} - -TEST( Features2d_Detector_STAR, regression ) -{ - CV_FeatureDetectorTest test( "detector-star", FeatureDetector::create("STAR") ); - test.safe_run(); -} - -TEST( Features2d_Detector_ORB, regression ) -{ - CV_FeatureDetectorTest test( "detector-orb", FeatureDetector::create("ORB") ); - test.safe_run(); -} - -TEST( Features2d_Detector_GridFAST, regression ) -{ - CV_FeatureDetectorTest test( "detector-grid-fast", FeatureDetector::create("GridFAST") ); - test.safe_run(); -} - -TEST( Features2d_Detector_PyramidFAST, regression ) -{ - CV_FeatureDetectorTest test( "detector-pyramid-fast", FeatureDetector::create("PyramidFAST") ); - test.safe_run(); -} - -/* - * Descriptors - */ - -TEST( Features2d_DescriptorExtractor_ORB, regression ) -{ - // TODO adjust the parameters below - CV_DescriptorExtractorTest test( "descriptor-orb", (CV_DescriptorExtractorTest::DistanceType)12.f, - DescriptorExtractor::create("ORB") ); - test.safe_run(); -} - -TEST( Features2d_DescriptorExtractor_FREAK, regression ) -{ - // TODO adjust the parameters below - CV_DescriptorExtractorTest test( "descriptor-freak", (CV_DescriptorExtractorTest::DistanceType)12.f, - DescriptorExtractor::create("FREAK") ); - test.safe_run(); -} - -TEST( Features2d_DescriptorExtractor_BRIEF, regression ) -{ - CV_DescriptorExtractorTest test( "descriptor-brief", 1, - DescriptorExtractor::create("BRIEF") ); - test.safe_run(); -} - -TEST( Features2d_DescriptorExtractor_OpponentBRIEF, regression ) -{ - CV_DescriptorExtractorTest test( "descriptor-opponent-brief", 1, - DescriptorExtractor::create("OpponentBRIEF") ); - test.safe_run(); -} - -/* - * Matchers - */ TEST( Features2d_DescriptorMatcher_BruteForce, regression ) { CV_DescriptorMatcherTest test( "descriptor-matcher-brute-force", new BFMatcher(NORM_L2), 0.01f ); @@ -1074,51 +541,3 @@ TEST( Features2d_DescriptorMatcher_FlannBased, regression ) CV_DescriptorMatcherTest test( "descriptor-matcher-flann-based", new FlannBasedMatcher, 0.04f ); test.safe_run(); } - - -TEST(Features2D_ORB, _1996) -{ - cv::Ptr fd = cv::FeatureDetector::create("ORB"); - cv::Ptr de = cv::DescriptorExtractor::create("ORB"); - - Mat image = cv::imread(string(cvtest::TS::ptr()->get_data_path()) + "shared/lena.jpg"); - ASSERT_FALSE(image.empty()); - - Mat roi(image.size(), CV_8UC1, Scalar(0)); - - Point poly[] = {Point(100, 20), Point(300, 50), Point(400, 200), Point(10, 500)}; - fillConvexPoly(roi, poly, int(sizeof(poly) / sizeof(poly[0])), Scalar(255)); - - std::vector keypoints; - fd->detect(image, keypoints, roi); - cv::Mat descriptors; - de->compute(image, keypoints, descriptors); - - //image.setTo(Scalar(255,255,255), roi); - - int roiViolations = 0; - for(std::vector::const_iterator kp = keypoints.begin(); kp != keypoints.end(); ++kp) - { - int x = cvRound(kp->pt.x); - int y = cvRound(kp->pt.y); - - ASSERT_LE(0, x); - ASSERT_LE(0, y); - ASSERT_GT(image.cols, x); - ASSERT_GT(image.rows, y); - - // if (!roi.at(y,x)) - // { - // roiViolations++; - // circle(image, kp->pt, 3, Scalar(0,0,255)); - // } - } - - // if(roiViolations) - // { - // imshow("img", image); - // waitKey(); - // } - - ASSERT_EQ(0, roiViolations); -} \ No newline at end of file diff --git a/modules/features2d/test/test_orb.cpp b/modules/features2d/test/test_orb.cpp new file mode 100644 index 0000000..34c23de --- /dev/null +++ b/modules/features2d/test/test_orb.cpp @@ -0,0 +1,92 @@ +/*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 "test_precomp.hpp" +#include "opencv2/highgui/highgui.hpp" + +using namespace cv; + +TEST(Features2D_ORB, _1996) +{ + Ptr fd = FeatureDetector::create("ORB"); + Ptr de = DescriptorExtractor::create("ORB"); + + Mat image = imread(string(cvtest::TS::ptr()->get_data_path()) + "shared/lena.jpg"); + ASSERT_FALSE(image.empty()); + + Mat roi(image.size(), CV_8UC1, Scalar(0)); + + Point poly[] = {Point(100, 20), Point(300, 50), Point(400, 200), Point(10, 500)}; + fillConvexPoly(roi, poly, int(sizeof(poly) / sizeof(poly[0])), Scalar(255)); + + std::vector keypoints; + fd->detect(image, keypoints, roi); + Mat descriptors; + de->compute(image, keypoints, descriptors); + + //image.setTo(Scalar(255,255,255), roi); + + int roiViolations = 0; + for(std::vector::const_iterator kp = keypoints.begin(); kp != keypoints.end(); ++kp) + { + int x = cvRound(kp->pt.x); + int y = cvRound(kp->pt.y); + + ASSERT_LE(0, x); + ASSERT_LE(0, y); + ASSERT_GT(image.cols, x); + ASSERT_GT(image.rows, y); + + // if (!roi.at(y,x)) + // { + // roiViolations++; + // circle(image, kp->pt, 3, Scalar(0,0,255)); + // } + } + + // if(roiViolations) + // { + // imshow("img", image); + // waitKey(); + // } + + ASSERT_EQ(0, roiViolations); +} -- 2.7.4