Path: blob/master/modules/imgproc/test/test_goodfeaturetotrack.cpp
16339 views
/*M///////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// Intel License Agreement10// For Open Source Computer Vision Library11//12// Copyright (C) 2000, Intel Corporation, all rights reserved.13// Third party copyrights are property of their respective owners.14//15// Redistribution and use in source and binary forms, with or without modification,16// are permitted provided that the following conditions are met:17//18// * Redistribution's of source code must retain the above copyright notice,19// this list of conditions and the following disclaimer.20//21// * Redistribution's in binary form must reproduce the above copyright notice,22// this list of conditions and the following disclaimer in the documentation23// and/or other materials provided with the distribution.24//25// * The name of Intel Corporation may not be used to endorse or promote products26// derived from this software without specific prior written permission.27//28// This software is provided by the copyright holders and contributors "as is" and29// any express or implied warranties, including, but not limited to, the implied30// warranties of merchantability and fitness for a particular purpose are disclaimed.31// In no event shall the Intel Corporation or contributors be liable for any direct,32// indirect, incidental, special, exemplary, or consequential damages33// (including, but not limited to, procurement of substitute goods or services;34// loss of use, data, or profits; or business interruption) however caused35// and on any theory of liability, whether in contract, strict liability,36// or tort (including negligence or otherwise) arising in any way out of37// the use of this software, even if advised of the possibility of such damage.38//39//M*/4041#include "test_precomp.hpp"4243namespace opencv_test { namespace {4445enum { MINEIGENVAL=0, HARRIS=1, EIGENVALSVECS=2 };464748#if 0 //set 1 to switch ON debug message49#define TEST_MESSAGE( message ) std::cout << message;50#define TEST_MESSAGEL( message, val) std::cout << message << val << std::endl;51#else52#define TEST_MESSAGE( message )53#define TEST_MESSAGEL( message, val)54#endif5556/////////////////////ref//////////////////////5758struct greaterThanPtr59{60bool operator () (const float * a, const float * b) const61{ return *a > *b; }62};6364static void65test_cornerEigenValsVecs( const Mat& src, Mat& eigenv, int block_size,66int _aperture_size, double k, int mode, int borderType, const Scalar& _borderValue )67{68int i, j;69Scalar borderValue = _borderValue;70int aperture_size = _aperture_size < 0 ? 3 : _aperture_size;71Point anchor( aperture_size/2, aperture_size/2 );7273CV_Assert( src.type() == CV_8UC1 || src.type() == CV_32FC1 );74CV_Assert( eigenv.type() == CV_32FC1 );75CV_Assert( ( src.rows == eigenv.rows ) &&76(((mode == MINEIGENVAL)||(mode == HARRIS)) && (src.cols == eigenv.cols)) );7778int type = src.type();79int ftype = CV_32FC1;80double kernel_scale = 1;8182Mat dx2, dy2, dxdy(src.size(), CV_32F), kernel;8384kernel = cvtest::calcSobelKernel2D( 1, 0, _aperture_size );85cvtest::filter2D( src, dx2, ftype, kernel*kernel_scale, anchor, 0, borderType, borderValue );86kernel = cvtest::calcSobelKernel2D( 0, 1, _aperture_size );87cvtest::filter2D( src, dy2, ftype, kernel*kernel_scale, anchor, 0, borderType,borderValue );8889double denom = (1 << (aperture_size-1))*block_size;90denom = denom * denom;9192if( _aperture_size < 0 )93denom *= 4;94if(type != ftype )95denom *= 255.;9697denom = 1./denom;9899for( i = 0; i < src.rows; i++ )100{101float* dxdyp = dxdy.ptr<float>(i);102float* dx2p = dx2.ptr<float>(i);103float* dy2p = dy2.ptr<float>(i);104105for( j = 0; j < src.cols; j++ )106{107double xval = dx2p[j], yval = dy2p[j];108dxdyp[j] = (float)(xval*yval*denom);109dx2p[j] = (float)(xval*xval*denom);110dy2p[j] = (float)(yval*yval*denom);111}112}113114kernel = Mat::ones(block_size, block_size, CV_32F);115anchor = Point(block_size/2, block_size/2);116117cvtest::filter2D( dx2, dx2, ftype, kernel, anchor, 0, borderType, borderValue );118cvtest::filter2D( dy2, dy2, ftype, kernel, anchor, 0, borderType, borderValue );119cvtest::filter2D( dxdy, dxdy, ftype, kernel, anchor, 0, borderType, borderValue );120121if( mode == MINEIGENVAL )122{123for( i = 0; i < src.rows; i++ )124{125float* eigenvp = eigenv.ptr<float>(i);126const float* dxdyp = dxdy.ptr<float>(i);127const float* dx2p = dx2.ptr<float>(i);128const float* dy2p = dy2.ptr<float>(i);129130for( j = 0; j < src.cols; j++ )131{132double a = dx2p[j], b = dxdyp[j], c = dy2p[j];133double d = sqrt( ( a - c )*( a - c ) + 4*b*b );134eigenvp[j] = (float)( 0.5*(a + c - d));135}136}137}138else if( mode == HARRIS )139{140141for( i = 0; i < src.rows; i++ )142{143float* eigenvp = eigenv.ptr<float>(i);144const float* dxdyp = dxdy.ptr<float>(i);145const float* dx2p = dx2.ptr<float>(i);146const float* dy2p = dy2.ptr<float>(i);147148for( j = 0; j < src.cols; j++ )149{150double a = dx2p[j], b = dxdyp[j], c = dy2p[j];151eigenvp[j] = (float)(a*c - b*b - k*(a + c)*(a + c));152}153}154}155}156157158static void159test_goodFeaturesToTrack( InputArray _image, OutputArray _corners,160int maxCorners, double qualityLevel, double minDistance,161InputArray _mask, int blockSize, int gradientSize,162bool useHarrisDetector, double harrisK )163{164165CV_Assert( qualityLevel > 0 && minDistance >= 0 && maxCorners >= 0 );166CV_Assert( _mask.empty() || (_mask.type() == CV_8UC1 && _mask.sameSize(_image)) );167168169Mat image = _image.getMat(), mask = _mask.getMat();170int aperture_size = gradientSize;171int borderType = BORDER_DEFAULT;172173Mat eig, tmp, tt;174175eig.create( image.size(), CV_32F );176177if( useHarrisDetector )178test_cornerEigenValsVecs( image, eig, blockSize, aperture_size, harrisK, HARRIS, borderType, 0 );179else180test_cornerEigenValsVecs( image, eig, blockSize, aperture_size, 0, MINEIGENVAL, borderType, 0 );181182double maxVal = 0;183184cvtest::minMaxIdx( eig, 0, &maxVal, 0, 0, mask );185cvtest::threshold( eig, eig, (float)(maxVal*qualityLevel), 0.f,THRESH_TOZERO );186cvtest::dilate( eig, tmp, Mat(),Point(-1,-1),borderType,0);187188Size imgsize = image.size();189190vector<const float*> tmpCorners;191192// collect list of pointers to features - put them into temporary image193for( int y = 1; y < imgsize.height - 1; y++ )194{195const float* eig_data = (const float*)eig.ptr(y);196const float* tmp_data = (const float*)tmp.ptr(y);197const uchar* mask_data = mask.data ? mask.ptr(y) : 0;198199for( int x = 1; x < imgsize.width - 1; x++ )200{201float val = eig_data[x];202if( val != 0 && val == tmp_data[x] && (!mask_data || mask_data[x]) )203{204tmpCorners.push_back(eig_data + x);205}206}207}208209vector<Point2f> corners;210size_t i, j, total = tmpCorners.size(), ncorners = 0;211212std::sort( tmpCorners.begin(), tmpCorners.end(), greaterThanPtr() );213214if(minDistance >= 1)215{216// Partition the image into larger grids217int w = image.cols;218int h = image.rows;219220const int cell_size = cvRound(minDistance);221const int grid_width = (w + cell_size - 1) / cell_size;222const int grid_height = (h + cell_size - 1) / cell_size;223224std::vector<std::vector<Point2f> > grid(grid_width*grid_height);225226minDistance *= minDistance;227228for( i = 0; i < total; i++ )229{230int ofs = (int)((const uchar*)tmpCorners[i] - eig.data);231int y = (int)(ofs / eig.step);232int x = (int)((ofs - y*eig.step)/sizeof(float));233234bool good = true;235236int x_cell = x / cell_size;237int y_cell = y / cell_size;238239int x1 = x_cell - 1;240int y1 = y_cell - 1;241int x2 = x_cell + 1;242int y2 = y_cell + 1;243244// boundary check245x1 = std::max(0, x1);246y1 = std::max(0, y1);247x2 = std::min(grid_width-1, x2);248y2 = std::min(grid_height-1, y2);249250for( int yy = y1; yy <= y2; yy++ )251{252for( int xx = x1; xx <= x2; xx++ )253{254vector <Point2f> &m = grid[yy*grid_width + xx];255256if( m.size() )257{258for(j = 0; j < m.size(); j++)259{260float dx = x - m[j].x;261float dy = y - m[j].y;262263if( dx*dx + dy*dy < minDistance )264{265good = false;266goto break_out;267}268}269}270}271}272273break_out:274275if(good)276{277grid[y_cell*grid_width + x_cell].push_back(Point2f((float)x, (float)y));278279corners.push_back(Point2f((float)x, (float)y));280++ncorners;281282if( maxCorners > 0 && (int)ncorners == maxCorners )283break;284}285}286}287else288{289for( i = 0; i < total; i++ )290{291int ofs = (int)((const uchar*)tmpCorners[i] - eig.data);292int y = (int)(ofs / eig.step);293int x = (int)((ofs - y*eig.step)/sizeof(float));294295corners.push_back(Point2f((float)x, (float)y));296++ncorners;297if( maxCorners > 0 && (int)ncorners == maxCorners )298break;299}300}301302Mat(corners).convertTo(_corners, _corners.fixedType() ? _corners.type() : CV_32F);303304}305306/////////////////end of ref code//////////////////////////307308309310class CV_GoodFeatureToTTest : public cvtest::ArrayTest311{312public:313CV_GoodFeatureToTTest();314315protected:316int prepare_test_case( int test_case_idx );317void run_func();318int validate_test_results( int test_case_idx );319320Mat src, src_gray;321Mat src_gray32f, src_gray8U;322Mat mask;323324int maxCorners;325vector<Point2f> corners;326vector<Point2f> Refcorners;327double qualityLevel;328double minDistance;329int blockSize;330int gradientSize;331bool useHarrisDetector;332double k;333int SrcType;334};335336337CV_GoodFeatureToTTest::CV_GoodFeatureToTTest()338{339RNG& rng = ts->get_rng();340maxCorners = rng.uniform( 50, 100 );341qualityLevel = 0.01;342minDistance = 10;343blockSize = 3;344gradientSize = 3;345useHarrisDetector = false;346k = 0.04;347mask = Mat();348test_case_count = 4;349SrcType = 0;350}351352int CV_GoodFeatureToTTest::prepare_test_case( int test_case_idx )353{354const static int types[] = { CV_32FC1, CV_8UC1 };355356cvtest::TS& tst = *cvtest::TS::ptr();357src = imread(string(tst.get_data_path()) + "shared/fruits.png", IMREAD_COLOR);358359CV_Assert(src.data != NULL);360361cvtColor( src, src_gray, CV_BGR2GRAY );362SrcType = types[test_case_idx & 0x1];363useHarrisDetector = test_case_idx & 2 ? true : false;364return 1;365}366367368void CV_GoodFeatureToTTest::run_func()369{370int cn = src_gray.channels();371372CV_Assert( cn == 1 );373CV_Assert( ( CV_MAT_DEPTH(SrcType) == CV_32FC1 ) || ( CV_MAT_DEPTH(SrcType) == CV_8UC1 ));374375TEST_MESSAGEL (" maxCorners = ", maxCorners)376if (useHarrisDetector)377{378TEST_MESSAGE (" useHarrisDetector = true\n");379}380else381{382TEST_MESSAGE (" useHarrisDetector = false\n");383}384385if( CV_MAT_DEPTH(SrcType) == CV_32FC1)386{387if (src_gray.depth() != CV_32FC1 ) src_gray.convertTo(src_gray32f, CV_32FC1);388else src_gray32f = src_gray.clone();389390TEST_MESSAGE ("goodFeaturesToTrack 32f\n")391392goodFeaturesToTrack( src_gray32f,393corners,394maxCorners,395qualityLevel,396minDistance,397Mat(),398blockSize,399gradientSize,400useHarrisDetector,401k );402}403else404{405if (src_gray.depth() != CV_8UC1 ) src_gray.convertTo(src_gray8U, CV_8UC1);406else src_gray8U = src_gray.clone();407408TEST_MESSAGE ("goodFeaturesToTrack 8U\n")409410goodFeaturesToTrack( src_gray8U,411corners,412maxCorners,413qualityLevel,414minDistance,415Mat(),416blockSize,417gradientSize,418useHarrisDetector,419k );420}421}422423424int CV_GoodFeatureToTTest::validate_test_results( int test_case_idx )425{426static const double eps = 2e-6;427428if( CV_MAT_DEPTH(SrcType) == CV_32FC1 )429{430if (src_gray.depth() != CV_32FC1 ) src_gray.convertTo(src_gray32f, CV_32FC1);431else src_gray32f = src_gray.clone();432433TEST_MESSAGE ("test_goodFeaturesToTrack 32f\n")434435test_goodFeaturesToTrack( src_gray32f,436Refcorners,437maxCorners,438qualityLevel,439minDistance,440Mat(),441blockSize,442gradientSize,443useHarrisDetector,444k );445}446else447{448if (src_gray.depth() != CV_8UC1 ) src_gray.convertTo(src_gray8U, CV_8UC1);449else src_gray8U = src_gray.clone();450451TEST_MESSAGE ("test_goodFeaturesToTrack 8U\n")452453test_goodFeaturesToTrack( src_gray8U,454Refcorners,455maxCorners,456qualityLevel,457minDistance,458Mat(),459blockSize,460gradientSize,461useHarrisDetector,462k );463}464465double e = cv::norm(corners, Refcorners); // TODO cvtest466467if (e > eps)468{469TEST_MESSAGEL ("Number of features: Refcorners = ", Refcorners.size())470TEST_MESSAGEL (" TestCorners = ", corners.size())471TEST_MESSAGE ("\n")472473ts->printf(cvtest::TS::CONSOLE, "actual error: %g, expected: %g", e, eps);474ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);475476for(int i = 0; i < (int)std::min((unsigned int)(corners.size()), (unsigned int)(Refcorners.size())); i++){477if ( (corners[i].x != Refcorners[i].x) || (corners[i].y != Refcorners[i].y))478printf("i = %i X %2.2f Xref %2.2f Y %2.2f Yref %2.2f\n",i,corners[i].x,Refcorners[i].x,corners[i].y,Refcorners[i].y);479}480}481else482{483TEST_MESSAGEL (" Refcorners = ", Refcorners.size())484TEST_MESSAGEL (" TestCorners = ", corners.size())485TEST_MESSAGE ("\n")486487ts->set_failed_test_info(cvtest::TS::OK);488}489490return BaseTest::validate_test_results(test_case_idx);491492}493494TEST(Imgproc_GoodFeatureToT, accuracy) { CV_GoodFeatureToTTest test; test.safe_run(); }495496497}} // namespace498/* End of file. */499500501