Path: blob/master/modules/features2d/test/test_descriptors_regression.impl.hpp
16354 views
// This file is part of OpenCV project.1// It is subject to the license terms in the LICENSE file found in the top-level directory2// of this distribution and at http://opencv.org/license.html34namespace opencv_test { namespace {56/****************************************************************************************\7* Regression tests for descriptor extractors. *8\****************************************************************************************/9static void writeMatInBin( const Mat& mat, const string& filename )10{11FILE* f = fopen( filename.c_str(), "wb");12if( f )13{14CV_Assert(4 == sizeof(int));15int type = mat.type();16fwrite( (void*)&mat.rows, sizeof(int), 1, f );17fwrite( (void*)&mat.cols, sizeof(int), 1, f );18fwrite( (void*)&type, sizeof(int), 1, f );19int dataSize = (int)(mat.step * mat.rows);20fwrite( (void*)&dataSize, sizeof(int), 1, f );21fwrite( (void*)mat.ptr(), 1, dataSize, f );22fclose(f);23}24}2526static Mat readMatFromBin( const string& filename )27{28FILE* f = fopen( filename.c_str(), "rb" );29if( f )30{31CV_Assert(4 == sizeof(int));32int rows, cols, type, dataSize;33size_t elements_read1 = fread( (void*)&rows, sizeof(int), 1, f );34size_t elements_read2 = fread( (void*)&cols, sizeof(int), 1, f );35size_t elements_read3 = fread( (void*)&type, sizeof(int), 1, f );36size_t elements_read4 = fread( (void*)&dataSize, sizeof(int), 1, f );37CV_Assert(elements_read1 == 1 && elements_read2 == 1 && elements_read3 == 1 && elements_read4 == 1);3839int step = dataSize / rows / CV_ELEM_SIZE(type);40CV_Assert(step >= cols);4142Mat returnMat = Mat(rows, step, type).colRange(0, cols);4344size_t elements_read = fread( returnMat.ptr(), 1, dataSize, f );45CV_Assert(elements_read == (size_t)(dataSize));4647fclose(f);4849return returnMat;50}51return Mat();52}5354template<class Distance>55class CV_DescriptorExtractorTest : public cvtest::BaseTest56{57public:58typedef typename Distance::ValueType ValueType;59typedef typename Distance::ResultType DistanceType;6061CV_DescriptorExtractorTest( const string _name, DistanceType _maxDist, const Ptr<DescriptorExtractor>& _dextractor,62Distance d = Distance(), Ptr<FeatureDetector> _detector = Ptr<FeatureDetector>()):63name(_name), maxDist(_maxDist), dextractor(_dextractor), distance(d) , detector(_detector) {}6465~CV_DescriptorExtractorTest()66{67}68protected:69virtual void createDescriptorExtractor() {}7071void compareDescriptors( const Mat& validDescriptors, const Mat& calcDescriptors )72{73if( validDescriptors.size != calcDescriptors.size || validDescriptors.type() != calcDescriptors.type() )74{75ts->printf(cvtest::TS::LOG, "Valid and computed descriptors matrices must have the same size and type.\n");76ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );77return;78}7980CV_Assert( DataType<ValueType>::type == validDescriptors.type() );8182int dimension = validDescriptors.cols;83DistanceType curMaxDist = 0;84size_t exact_count = 0, failed_count = 0;85for( int y = 0; y < validDescriptors.rows; y++ )86{87DistanceType dist = distance( validDescriptors.ptr<ValueType>(y), calcDescriptors.ptr<ValueType>(y), dimension );88if (dist == 0)89exact_count++;90if( dist > curMaxDist )91{92if (dist > maxDist)93failed_count++;94curMaxDist = dist;95}96#if 097if (dist > 0)98{99std::cout << "i=" << y << " fail_count=" << failed_count << " dist=" << dist << std::endl;100std::cout << "valid: " << validDescriptors.row(y) << std::endl;101std::cout << " calc: " << calcDescriptors.row(y) << std::endl;102}103#endif104}105106float exact_percents = (100 * (float)exact_count / validDescriptors.rows);107float failed_percents = (100 * (float)failed_count / validDescriptors.rows);108std::stringstream ss;109ss << "Exact count (dist == 0): " << exact_count << " (" << (int)exact_percents << "%)" << std::endl110<< "Failed count (dist > " << maxDist << "): " << failed_count << " (" << (int)failed_percents << "%)" << std::endl111<< "Max distance between valid and computed descriptors (" << validDescriptors.size() << "): " << curMaxDist;112EXPECT_LE(failed_percents, 20.0f);113std::cout << ss.str() << std::endl;114}115116void emptyDataTest()117{118assert( dextractor );119120// One image.121Mat image;122vector<KeyPoint> keypoints;123Mat descriptors;124125try126{127dextractor->compute( image, keypoints, descriptors );128}129catch(...)130{131ts->printf( cvtest::TS::LOG, "compute() on empty image and empty keypoints must not generate exception (1).\n");132ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );133}134135RNG rng;136image = cvtest::randomMat(rng, Size(50, 50), CV_8UC3, 0, 255, false);137try138{139dextractor->compute( image, keypoints, descriptors );140}141catch(...)142{143ts->printf( cvtest::TS::LOG, "compute() on nonempty image and empty keypoints must not generate exception (1).\n");144ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );145}146147// Several images.148vector<Mat> images;149vector<vector<KeyPoint> > keypointsCollection;150vector<Mat> descriptorsCollection;151try152{153dextractor->compute( images, keypointsCollection, descriptorsCollection );154}155catch(...)156{157ts->printf( cvtest::TS::LOG, "compute() on empty images and empty keypoints collection must not generate exception (2).\n");158ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );159}160}161162void regressionTest()163{164assert( dextractor );165166// Read the test image.167string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME;168Mat img = imread( imgFilename );169if( img.empty() )170{171ts->printf( cvtest::TS::LOG, "Image %s can not be read.\n", imgFilename.c_str() );172ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );173return;174}175const std::string keypoints_filename = string(ts->get_data_path()) +176(detector.empty()177? (FEATURES2D_DIR + "/" + std::string("keypoints.xml.gz"))178: (DESCRIPTOR_DIR + "/" + name + "_keypoints.xml.gz"));179FileStorage fs(keypoints_filename, FileStorage::READ);180181vector<KeyPoint> keypoints;182EXPECT_TRUE(fs.isOpened()) << "Keypoint testdata is missing. Re-computing and re-writing keypoints testdata...";183if (!fs.isOpened())184{185fs.open(keypoints_filename, FileStorage::WRITE);186ASSERT_TRUE(fs.isOpened()) << "File for writing keypoints can not be opened.";187if (detector.empty())188{189Ptr<ORB> fd = ORB::create();190fd->detect(img, keypoints);191}192else193{194detector->detect(img, keypoints);195}196write(fs, "keypoints", keypoints);197fs.release();198}199else200{201read(fs.getFirstTopLevelNode(), keypoints);202fs.release();203}204205if(!detector.empty())206{207vector<KeyPoint> calcKeypoints;208detector->detect(img, calcKeypoints);209// TODO validate received keypoints210int diff = abs((int)calcKeypoints.size() - (int)keypoints.size());211if (diff > 0)212{213std::cout << "Keypoints difference: " << diff << std::endl;214EXPECT_LE(diff, (int)(keypoints.size() * 0.03f));215}216}217ASSERT_FALSE(keypoints.empty());218{219Mat calcDescriptors;220double t = (double)getTickCount();221dextractor->compute(img, keypoints, calcDescriptors);222t = getTickCount() - t;223ts->printf(cvtest::TS::LOG, "\nAverage time of computing one descriptor = %g ms.\n", t/((double)getTickFrequency()*1000.)/calcDescriptors.rows);224225if (calcDescriptors.rows != (int)keypoints.size())226{227ts->printf( cvtest::TS::LOG, "Count of computed descriptors and keypoints count must be equal.\n" );228ts->printf( cvtest::TS::LOG, "Count of keypoints is %d.\n", (int)keypoints.size() );229ts->printf( cvtest::TS::LOG, "Count of computed descriptors is %d.\n", calcDescriptors.rows );230ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );231return;232}233234if (calcDescriptors.cols != dextractor->descriptorSize() || calcDescriptors.type() != dextractor->descriptorType())235{236ts->printf( cvtest::TS::LOG, "Incorrect descriptor size or descriptor type.\n" );237ts->printf( cvtest::TS::LOG, "Expected size is %d.\n", dextractor->descriptorSize() );238ts->printf( cvtest::TS::LOG, "Calculated size is %d.\n", calcDescriptors.cols );239ts->printf( cvtest::TS::LOG, "Expected type is %d.\n", dextractor->descriptorType() );240ts->printf( cvtest::TS::LOG, "Calculated type is %d.\n", calcDescriptors.type() );241ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );242return;243}244245// TODO read and write descriptor extractor parameters and check them246Mat validDescriptors = readDescriptors();247EXPECT_FALSE(validDescriptors.empty()) << "Descriptors testdata is missing. Re-writing descriptors testdata...";248if (!validDescriptors.empty())249{250compareDescriptors(validDescriptors, calcDescriptors);251}252else253{254ASSERT_TRUE(writeDescriptors(calcDescriptors)) << "Descriptors can not be written.";255}256}257}258259void run(int)260{261createDescriptorExtractor();262if( !dextractor )263{264ts->printf(cvtest::TS::LOG, "Descriptor extractor is empty.\n");265ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );266return;267}268269emptyDataTest();270regressionTest();271272ts->set_failed_test_info( cvtest::TS::OK );273}274275virtual Mat readDescriptors()276{277Mat res = readMatFromBin( string(ts->get_data_path()) + DESCRIPTOR_DIR + "/" + string(name) );278return res;279}280281virtual bool writeDescriptors( Mat& descs )282{283writeMatInBin( descs, string(ts->get_data_path()) + DESCRIPTOR_DIR + "/" + string(name) );284return true;285}286287string name;288const DistanceType maxDist;289Ptr<DescriptorExtractor> dextractor;290Distance distance;291Ptr<FeatureDetector> detector;292293private:294CV_DescriptorExtractorTest& operator=(const CV_DescriptorExtractorTest&) { return *this; }295};296297}} // namespace298299300