diff --git a/modules/core/include/opencv2/core.hpp b/modules/core/include/opencv2/core.hpp index a258e8c4d2..2fba9fc6bb 100644 --- a/modules/core/include/opencv2/core.hpp +++ b/modules/core/include/opencv2/core.hpp @@ -874,6 +874,9 @@ public: virtual ~Algorithm(); String name() const; + virtual void set(int, double); + virtual double get(int) const; + template typename ParamType<_Tp>::member_type get(const String& name) const; template typename ParamType<_Tp>::member_type get(const char* name) const; diff --git a/modules/core/src/algorithm.cpp b/modules/core/src/algorithm.cpp index 9f9493e8a0..bd28bdcdc3 100644 --- a/modules/core/src/algorithm.cpp +++ b/modules/core/src/algorithm.cpp @@ -179,6 +179,9 @@ String Algorithm::name() const return info()->name(); } +void Algorithm::set(int, double) {} +double Algorithm::get(int) const { return 0.; } + void Algorithm::set(const String& parameter, int value) { info()->set(this, parameter.c_str(), ParamType::type, &value); diff --git a/modules/features2d/include/opencv2/features2d.hpp b/modules/features2d/include/opencv2/features2d.hpp index ddf5d66aaa..243d755778 100644 --- a/modules/features2d/include/opencv2/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d.hpp @@ -213,9 +213,10 @@ CV_EXPORTS void FAST( InputArray image, CV_OUT std::vector& keypoints, class CV_EXPORTS_W FastFeatureDetector : public Feature2D { public: - enum Type + enum { - TYPE_5_8 = 0, TYPE_7_12 = 1, TYPE_9_16 = 2 + TYPE_5_8 = 0, TYPE_7_12 = 1, TYPE_9_16 = 2, + THRESHOLD = 10000, NONMAX_SUPPRESSION=10001, FAST_N=10002, }; CV_WRAP static Ptr create( int threshold=10, diff --git a/modules/features2d/perf/opencl/perf_fast.cpp b/modules/features2d/perf/opencl/perf_fast.cpp index c4a8e078c0..be310bacbf 100644 --- a/modules/features2d/perf/opencl/perf_fast.cpp +++ b/modules/features2d/perf/opencl/perf_fast.cpp @@ -32,11 +32,8 @@ OCL_PERF_TEST_P(FASTFixture, FastDetect, testing::Combine( mframe.copyTo(frame); declare.in(frame); - Ptr fd = Algorithm::create("Feature2D.FAST"); + Ptr fd = FastFeatureDetector::create(20, true, type); ASSERT_FALSE( fd.empty() ); - fd->set("threshold", 20); - fd->set("nonmaxSuppression", true); - fd->set("type", type); vector points; OCL_TEST_CYCLE() fd->detect(frame, points); diff --git a/modules/features2d/perf/perf_fast.cpp b/modules/features2d/perf/perf_fast.cpp index 25b82bd412..f706364004 100644 --- a/modules/features2d/perf/perf_fast.cpp +++ b/modules/features2d/perf/perf_fast.cpp @@ -30,11 +30,8 @@ PERF_TEST_P(fast, detect, testing::Combine( declare.in(frame); - Ptr fd = Algorithm::create("Feature2D.FAST"); + Ptr fd = FastFeatureDetector::create(20, true, type); ASSERT_FALSE( fd.empty() ); - fd->set("threshold", 20); - fd->set("nonmaxSuppression", true); - fd->set("type", type); vector points; TEST_CYCLE() fd->detect(frame, points); diff --git a/modules/features2d/src/brisk.cpp b/modules/features2d/src/brisk.cpp index d123a3a816..0a14148cf3 100644 --- a/modules/features2d/src/brisk.cpp +++ b/modules/features2d/src/brisk.cpp @@ -2099,7 +2099,7 @@ BriskLayer::BriskLayer(const BriskLayer& layer, int mode) void BriskLayer::getAgastPoints(int threshold, std::vector& keypoints) { - fast_9_16_ = FastFeatureDetector::create(threshold); + fast_9_16_->set(FastFeatureDetector::THRESHOLD, threshold); fast_9_16_->detect(img_, keypoints); // also write scores diff --git a/modules/features2d/src/fast.cpp b/modules/features2d/src/fast.cpp index bfb9246041..f3a44876af 100644 --- a/modules/features2d/src/fast.cpp +++ b/modules/features2d/src/fast.cpp @@ -383,6 +383,30 @@ public: KeyPointsFilter::runByPixelsMask( keypoints, mask ); } + void set(int prop, double value) + { + if(prop == THRESHOLD) + threshold = cvRound(value); + else if(prop == NONMAX_SUPPRESSION) + nonmaxSuppression = value != 0; + else if(prop == FAST_N) + type = cvRound(value); + else + CV_Error(Error::StsBadArg, ""); + } + + double get(int prop) const + { + if(prop == THRESHOLD) + return threshold; + if(prop == NONMAX_SUPPRESSION) + return nonmaxSuppression; + if(prop == FAST_N) + return type; + CV_Error(Error::StsBadArg, ""); + return 0; + } + int threshold; bool nonmaxSuppression; int type; diff --git a/modules/features2d/src/mser.cpp b/modules/features2d/src/mser.cpp index c9db56703e..49d173b9ba 100644 --- a/modules/features2d/src/mser.cpp +++ b/modules/features2d/src/mser.cpp @@ -800,9 +800,12 @@ extractMSER_8uC3( const Mat& src, Mat& labels, double emean = 0; Mat dx( src.rows, src.cols-1, CV_32FC1 ); Mat dy( src.rows, src.cols, CV_32FC1 ); + Ne = preprocessMSER_8UC3( map, edge, emean, src, dx, dy, Ne, params.edgeBlurSize ); emean = emean / (double)Ne; + std::sort(edge, edge + Ne, LessThanEdge()); + MSCREdge* edge_ub = edge+Ne; MSCREdge* edgeptr = edge; TempMSCR* mscrptr = mscr; diff --git a/modules/features2d/test/test_descriptors_regression.cpp b/modules/features2d/test/test_descriptors_regression.cpp index baffeeb3f8..c0956fda57 100644 --- a/modules/features2d/test/test_descriptors_regression.cpp +++ b/modules/features2d/test/test_descriptors_regression.cpp @@ -106,8 +106,6 @@ public: ~CV_DescriptorExtractorTest() { - if(!detector.empty()) - detector.release(); } protected: virtual void createDescriptorExtractor() {} @@ -333,7 +331,7 @@ TEST( Features2d_DescriptorExtractor_KAZE, regression ) { CV_DescriptorExtractorTest< L2 > test( "descriptor-kaze", 0.03f, KAZE::create(), - L2() ); + L2(), KAZE::create() ); test.safe_run(); } diff --git a/modules/features2d/test/test_keypoints.cpp b/modules/features2d/test/test_keypoints.cpp index 561c3cc6ee..c494f98828 100644 --- a/modules/features2d/test/test_keypoints.cpp +++ b/modules/features2d/test/test_keypoints.cpp @@ -41,6 +41,7 @@ #include "test_precomp.hpp" #include "opencv2/highgui.hpp" +#include "opencv2/core/core_c.h" using namespace std; using namespace cv; @@ -48,6 +49,683 @@ using namespace cv; const string FEATURES2D_DIR = "features2d"; const string IMAGE_FILENAME = "tsukuba.png"; +const int TABLE_SIZE = 400; + +static const float chitab3[]= +{ + 0.f, 0.0150057f, 0.0239478f, 0.0315227f, + 0.0383427f, 0.0446605f, 0.0506115f, 0.0562786f, + 0.0617174f, 0.0669672f, 0.0720573f, 0.0770099f, + 0.081843f, 0.0865705f, 0.0912043f, 0.0957541f, + 0.100228f, 0.104633f, 0.108976f, 0.113261f, + 0.117493f, 0.121676f, 0.125814f, 0.12991f, + 0.133967f, 0.137987f, 0.141974f, 0.145929f, + 0.149853f, 0.15375f, 0.15762f, 0.161466f, + 0.165287f, 0.169087f, 0.172866f, 0.176625f, + 0.180365f, 0.184088f, 0.187794f, 0.191483f, + 0.195158f, 0.198819f, 0.202466f, 0.2061f, + 0.209722f, 0.213332f, 0.216932f, 0.220521f, + 0.2241f, 0.22767f, 0.231231f, 0.234783f, + 0.238328f, 0.241865f, 0.245395f, 0.248918f, + 0.252435f, 0.255947f, 0.259452f, 0.262952f, + 0.266448f, 0.269939f, 0.273425f, 0.276908f, + 0.280386f, 0.283862f, 0.287334f, 0.290803f, + 0.29427f, 0.297734f, 0.301197f, 0.304657f, + 0.308115f, 0.311573f, 0.315028f, 0.318483f, + 0.321937f, 0.32539f, 0.328843f, 0.332296f, + 0.335749f, 0.339201f, 0.342654f, 0.346108f, + 0.349562f, 0.353017f, 0.356473f, 0.35993f, + 0.363389f, 0.366849f, 0.37031f, 0.373774f, + 0.377239f, 0.380706f, 0.384176f, 0.387648f, + 0.391123f, 0.3946f, 0.39808f, 0.401563f, + 0.405049f, 0.408539f, 0.412032f, 0.415528f, + 0.419028f, 0.422531f, 0.426039f, 0.429551f, + 0.433066f, 0.436586f, 0.440111f, 0.44364f, + 0.447173f, 0.450712f, 0.454255f, 0.457803f, + 0.461356f, 0.464915f, 0.468479f, 0.472049f, + 0.475624f, 0.479205f, 0.482792f, 0.486384f, + 0.489983f, 0.493588f, 0.4972f, 0.500818f, + 0.504442f, 0.508073f, 0.511711f, 0.515356f, + 0.519008f, 0.522667f, 0.526334f, 0.530008f, + 0.533689f, 0.537378f, 0.541075f, 0.54478f, + 0.548492f, 0.552213f, 0.555942f, 0.55968f, + 0.563425f, 0.56718f, 0.570943f, 0.574715f, + 0.578497f, 0.582287f, 0.586086f, 0.589895f, + 0.593713f, 0.597541f, 0.601379f, 0.605227f, + 0.609084f, 0.612952f, 0.61683f, 0.620718f, + 0.624617f, 0.628526f, 0.632447f, 0.636378f, + 0.64032f, 0.644274f, 0.648239f, 0.652215f, + 0.656203f, 0.660203f, 0.664215f, 0.668238f, + 0.672274f, 0.676323f, 0.680384f, 0.684457f, + 0.688543f, 0.692643f, 0.696755f, 0.700881f, + 0.70502f, 0.709172f, 0.713339f, 0.717519f, + 0.721714f, 0.725922f, 0.730145f, 0.734383f, + 0.738636f, 0.742903f, 0.747185f, 0.751483f, + 0.755796f, 0.760125f, 0.76447f, 0.768831f, + 0.773208f, 0.777601f, 0.782011f, 0.786438f, + 0.790882f, 0.795343f, 0.799821f, 0.804318f, + 0.808831f, 0.813363f, 0.817913f, 0.822482f, + 0.827069f, 0.831676f, 0.836301f, 0.840946f, + 0.84561f, 0.850295f, 0.854999f, 0.859724f, + 0.864469f, 0.869235f, 0.874022f, 0.878831f, + 0.883661f, 0.888513f, 0.893387f, 0.898284f, + 0.903204f, 0.908146f, 0.913112f, 0.918101f, + 0.923114f, 0.928152f, 0.933214f, 0.938301f, + 0.943413f, 0.94855f, 0.953713f, 0.958903f, + 0.964119f, 0.969361f, 0.974631f, 0.979929f, + 0.985254f, 0.990608f, 0.99599f, 1.0014f, + 1.00684f, 1.01231f, 1.01781f, 1.02335f, + 1.02891f, 1.0345f, 1.04013f, 1.04579f, + 1.05148f, 1.05721f, 1.06296f, 1.06876f, + 1.07459f, 1.08045f, 1.08635f, 1.09228f, + 1.09826f, 1.10427f, 1.11032f, 1.1164f, + 1.12253f, 1.1287f, 1.1349f, 1.14115f, + 1.14744f, 1.15377f, 1.16015f, 1.16656f, + 1.17303f, 1.17954f, 1.18609f, 1.19269f, + 1.19934f, 1.20603f, 1.21278f, 1.21958f, + 1.22642f, 1.23332f, 1.24027f, 1.24727f, + 1.25433f, 1.26144f, 1.26861f, 1.27584f, + 1.28312f, 1.29047f, 1.29787f, 1.30534f, + 1.31287f, 1.32046f, 1.32812f, 1.33585f, + 1.34364f, 1.3515f, 1.35943f, 1.36744f, + 1.37551f, 1.38367f, 1.39189f, 1.4002f, + 1.40859f, 1.41705f, 1.42561f, 1.43424f, + 1.44296f, 1.45177f, 1.46068f, 1.46967f, + 1.47876f, 1.48795f, 1.49723f, 1.50662f, + 1.51611f, 1.52571f, 1.53541f, 1.54523f, + 1.55517f, 1.56522f, 1.57539f, 1.58568f, + 1.59611f, 1.60666f, 1.61735f, 1.62817f, + 1.63914f, 1.65025f, 1.66152f, 1.67293f, + 1.68451f, 1.69625f, 1.70815f, 1.72023f, + 1.73249f, 1.74494f, 1.75757f, 1.77041f, + 1.78344f, 1.79669f, 1.81016f, 1.82385f, + 1.83777f, 1.85194f, 1.86635f, 1.88103f, + 1.89598f, 1.91121f, 1.92674f, 1.94257f, + 1.95871f, 1.97519f, 1.99201f, 2.0092f, + 2.02676f, 2.04471f, 2.06309f, 2.08189f, + 2.10115f, 2.12089f, 2.14114f, 2.16192f, + 2.18326f, 2.2052f, 2.22777f, 2.25101f, + 2.27496f, 2.29966f, 2.32518f, 2.35156f, + 2.37886f, 2.40717f, 2.43655f, 2.46709f, + 2.49889f, 2.53206f, 2.56673f, 2.60305f, + 2.64117f, 2.6813f, 2.72367f, 2.76854f, + 2.81623f, 2.86714f, 2.92173f, 2.98059f, + 3.04446f, 3.1143f, 3.19135f, 3.27731f, + 3.37455f, 3.48653f, 3.61862f, 3.77982f, + 3.98692f, 4.2776f, 4.77167f, 133.333f +}; + +struct MSCRNode; + +struct TempMSCR +{ + MSCRNode* head; + MSCRNode* tail; + double m; // the margin used to prune area later + int size; +}; + +struct MSCRNode +{ + MSCRNode* shortcut; + // to make the finding of root less painful + MSCRNode* prev; + MSCRNode* next; + // a point double-linked list + TempMSCR* tmsr; + // the temporary msr (set to NULL at every re-initialise) + TempMSCR* gmsr; + // the global msr (once set, never to NULL) + int index; + // the index of the node, at this point, it should be x at the first 16-bits, and y at the last 16-bits. + int rank; + int reinit; + int size, sizei; + double dt, di; + double s; +}; + +struct MSCREdge +{ + double chi; + MSCRNode* left; + MSCRNode* right; +}; + +static double ChiSquaredDistance( uchar* x, uchar* y ) +{ + return (double)((x[0]-y[0])*(x[0]-y[0]))/(double)(x[0]+y[0]+1e-10)+ + (double)((x[1]-y[1])*(x[1]-y[1]))/(double)(x[1]+y[1]+1e-10)+ + (double)((x[2]-y[2])*(x[2]-y[2]))/(double)(x[2]+y[2]+1e-10); +} + +static void initMSCRNode( MSCRNode* node ) +{ + node->gmsr = node->tmsr = NULL; + node->reinit = 0xffff; + node->rank = 0; + node->sizei = node->size = 1; + node->prev = node->next = node->shortcut = node; +} + +// the preprocess to get the edge list with proper gaussian blur +static int preprocessMSER_8UC3( MSCRNode* node, + MSCREdge* edge, + double* total, + CvMat* src, + CvMat* mask, + CvMat* dx, + CvMat* dy, + int Ne, + int edgeBlurSize ) +{ + int srccpt = src->step-src->cols*3; + uchar* srcptr = src->data.ptr; + uchar* lastptr = src->data.ptr+3; + double* dxptr = dx->data.db; + for ( int i = 0; i < src->rows; i++ ) + { + for ( int j = 0; j < src->cols-1; j++ ) + { + *dxptr = ChiSquaredDistance( srcptr, lastptr ); + dxptr++; + srcptr += 3; + lastptr += 3; + } + srcptr += srccpt+3; + lastptr += srccpt+3; + } + srcptr = src->data.ptr; + lastptr = src->data.ptr+src->step; + double* dyptr = dy->data.db; + for ( int i = 0; i < src->rows-1; i++ ) + { + for ( int j = 0; j < src->cols; j++ ) + { + *dyptr = ChiSquaredDistance( srcptr, lastptr ); + dyptr++; + srcptr += 3; + lastptr += 3; + } + srcptr += srccpt; + lastptr += srccpt; + } + // get dx and dy and blur it + if ( edgeBlurSize >= 1 ) + { + Mat _dx(dx->rows, dx->cols, dx->type, dx->data.ptr, dx->step); + Mat _dy(dy->rows, dy->cols, dy->type, dy->data.ptr, dy->step); + GaussianBlur( _dx, _dx, Size(edgeBlurSize, edgeBlurSize), 0 ); + GaussianBlur( _dy, _dy, Size(edgeBlurSize, edgeBlurSize), 0 ); + } + dxptr = dx->data.db; + dyptr = dy->data.db; + // assian dx, dy to proper edge list and initialize mscr node + // the nasty code here intended to avoid extra loops + if ( mask ) + { + Ne = 0; + int maskcpt = mask->step-mask->cols+1; + uchar* maskptr = mask->data.ptr; + MSCRNode* nodeptr = node; + initMSCRNode( nodeptr ); + nodeptr->index = 0; + *total += edge->chi = *dxptr; + if ( maskptr[0] && maskptr[1] ) + { + edge->left = nodeptr; + edge->right = nodeptr+1; + edge++; + Ne++; + } + dxptr++; + nodeptr++; + maskptr++; + for ( int i = 1; i < src->cols-1; i++ ) + { + initMSCRNode( nodeptr ); + nodeptr->index = i; + if ( maskptr[0] && maskptr[1] ) + { + *total += edge->chi = *dxptr; + edge->left = nodeptr; + edge->right = nodeptr+1; + edge++; + Ne++; + } + dxptr++; + nodeptr++; + maskptr++; + } + initMSCRNode( nodeptr ); + nodeptr->index = src->cols-1; + nodeptr++; + maskptr += maskcpt; + for ( int i = 1; i < src->rows-1; i++ ) + { + initMSCRNode( nodeptr ); + nodeptr->index = i<<16; + if ( maskptr[0] ) + { + if ( maskptr[-mask->step] ) + { + *total += edge->chi = *dyptr; + edge->left = nodeptr-src->cols; + edge->right = nodeptr; + edge++; + Ne++; + } + if ( maskptr[1] ) + { + *total += edge->chi = *dxptr; + edge->left = nodeptr; + edge->right = nodeptr+1; + edge++; + Ne++; + } + } + dyptr++; + dxptr++; + nodeptr++; + maskptr++; + for ( int j = 1; j < src->cols-1; j++ ) + { + initMSCRNode( nodeptr ); + nodeptr->index = (i<<16)|j; + if ( maskptr[0] ) + { + if ( maskptr[-mask->step] ) + { + *total += edge->chi = *dyptr; + edge->left = nodeptr-src->cols; + edge->right = nodeptr; + edge++; + Ne++; + } + if ( maskptr[1] ) + { + *total += edge->chi = *dxptr; + edge->left = nodeptr; + edge->right = nodeptr+1; + edge++; + Ne++; + } + } + dyptr++; + dxptr++; + nodeptr++; + maskptr++; + } + initMSCRNode( nodeptr ); + nodeptr->index = (i<<16)|(src->cols-1); + if ( maskptr[0] && maskptr[-mask->step] ) + { + *total += edge->chi = *dyptr; + edge->left = nodeptr-src->cols; + edge->right = nodeptr; + edge++; + Ne++; + } + dyptr++; + nodeptr++; + maskptr += maskcpt; + } + initMSCRNode( nodeptr ); + nodeptr->index = (src->rows-1)<<16; + if ( maskptr[0] ) + { + if ( maskptr[1] ) + { + *total += edge->chi = *dxptr; + edge->left = nodeptr; + edge->right = nodeptr+1; + edge++; + Ne++; + } + if ( maskptr[-mask->step] ) + { + *total += edge->chi = *dyptr; + edge->left = nodeptr-src->cols; + edge->right = nodeptr; + edge++; + Ne++; + } + } + dxptr++; + dyptr++; + nodeptr++; + maskptr++; + for ( int i = 1; i < src->cols-1; i++ ) + { + initMSCRNode( nodeptr ); + nodeptr->index = ((src->rows-1)<<16)|i; + if ( maskptr[0] ) + { + if ( maskptr[1] ) + { + *total += edge->chi = *dxptr; + edge->left = nodeptr; + edge->right = nodeptr+1; + edge++; + Ne++; + } + if ( maskptr[-mask->step] ) + { + *total += edge->chi = *dyptr; + edge->left = nodeptr-src->cols; + edge->right = nodeptr; + edge++; + Ne++; + } + } + dxptr++; + dyptr++; + nodeptr++; + maskptr++; + } + initMSCRNode( nodeptr ); + nodeptr->index = ((src->rows-1)<<16)|(src->cols-1); + if ( maskptr[0] && maskptr[-mask->step] ) + { + *total += edge->chi = *dyptr; + edge->left = nodeptr-src->cols; + edge->right = nodeptr; + Ne++; + } + } else { + MSCRNode* nodeptr = node; + initMSCRNode( nodeptr ); + nodeptr->index = 0; + *total += edge->chi = *dxptr; + dxptr++; + edge->left = nodeptr; + edge->right = nodeptr+1; + edge++; + nodeptr++; + for ( int i = 1; i < src->cols-1; i++ ) + { + initMSCRNode( nodeptr ); + nodeptr->index = i; + *total += edge->chi = *dxptr; + dxptr++; + edge->left = nodeptr; + edge->right = nodeptr+1; + edge++; + nodeptr++; + } + initMSCRNode( nodeptr ); + nodeptr->index = src->cols-1; + nodeptr++; + for ( int i = 1; i < src->rows-1; i++ ) + { + initMSCRNode( nodeptr ); + nodeptr->index = i<<16; + *total += edge->chi = *dyptr; + dyptr++; + edge->left = nodeptr-src->cols; + edge->right = nodeptr; + edge++; + *total += edge->chi = *dxptr; + dxptr++; + edge->left = nodeptr; + edge->right = nodeptr+1; + edge++; + nodeptr++; + for ( int j = 1; j < src->cols-1; j++ ) + { + initMSCRNode( nodeptr ); + nodeptr->index = (i<<16)|j; + *total += edge->chi = *dyptr; + dyptr++; + edge->left = nodeptr-src->cols; + edge->right = nodeptr; + edge++; + *total += edge->chi = *dxptr; + dxptr++; + edge->left = nodeptr; + edge->right = nodeptr+1; + edge++; + nodeptr++; + } + initMSCRNode( nodeptr ); + nodeptr->index = (i<<16)|(src->cols-1); + *total += edge->chi = *dyptr; + dyptr++; + edge->left = nodeptr-src->cols; + edge->right = nodeptr; + edge++; + nodeptr++; + } + initMSCRNode( nodeptr ); + nodeptr->index = (src->rows-1)<<16; + *total += edge->chi = *dxptr; + dxptr++; + edge->left = nodeptr; + edge->right = nodeptr+1; + edge++; + *total += edge->chi = *dyptr; + dyptr++; + edge->left = nodeptr-src->cols; + edge->right = nodeptr; + edge++; + nodeptr++; + for ( int i = 1; i < src->cols-1; i++ ) + { + initMSCRNode( nodeptr ); + nodeptr->index = ((src->rows-1)<<16)|i; + *total += edge->chi = *dxptr; + dxptr++; + edge->left = nodeptr; + edge->right = nodeptr+1; + edge++; + *total += edge->chi = *dyptr; + dyptr++; + edge->left = nodeptr-src->cols; + edge->right = nodeptr; + edge++; + nodeptr++; + } + initMSCRNode( nodeptr ); + nodeptr->index = ((src->rows-1)<<16)|(src->cols-1); + *total += edge->chi = *dyptr; + edge->left = nodeptr-src->cols; + edge->right = nodeptr; + } + return Ne; +} + +class LessThanEdge +{ +public: + bool operator()(const MSCREdge& a, const MSCREdge& b) const { return a.chi < b.chi; } +}; + +// to find the root of one region +static MSCRNode* findMSCR( MSCRNode* x ) +{ + MSCRNode* prev = x; + MSCRNode* next; + for ( ; ; ) + { + next = x->shortcut; + x->shortcut = prev; + if ( next == x ) break; + prev= x; + x = next; + } + MSCRNode* root = x; + for ( ; ; ) + { + prev = x->shortcut; + x->shortcut = root; + if ( prev == x ) break; + x = prev; + } + return root; +} + +struct MSERParams +{ + MSERParams( int _delta=5, int _min_area=60, int _max_area=14400, + double _max_variation=0.25, double _min_diversity=.2, + int _max_evolution=200, double _area_threshold=1.01, + double _min_margin=0.003, int _edge_blur_size=5 ) + { + delta = _delta; + minArea = _min_area; + maxArea = _max_area; + maxVariation = _max_variation; + minDiversity = _min_diversity; + maxEvolution = _max_evolution; + areaThreshold = _area_threshold; + minMargin = _min_margin; + edgeBlurSize = _edge_blur_size; + } + + int delta; + int minArea; + int maxArea; + double maxVariation; + double minDiversity; + int maxEvolution; + double areaThreshold; + double minMargin; + int edgeBlurSize; +}; + +// the stable mscr should be: +// bigger than minArea and smaller than maxArea +// differ from its ancestor more than minDiversity +static bool MSCRStableCheck( MSCRNode* x, MSERParams params ) +{ + if ( x->size <= params.minArea || x->size >= params.maxArea ) + return 0; + if ( x->gmsr == NULL ) + return 1; + double div = (double)(x->size-x->gmsr->size)/(double)x->size; + return div > params.minDiversity; +} + +static void +extractMSER_8UC3( CvMat* src, + CvMat* mask, + vector >& msers, + MSERParams params ) +{ + msers.clear(); + MSCRNode* map = (MSCRNode*)cvAlloc( src->cols*src->rows*sizeof(map[0]) ); + int Ne = src->cols*src->rows*2-src->cols-src->rows; + MSCREdge* edge = (MSCREdge*)cvAlloc( Ne*sizeof(edge[0]) ); + TempMSCR* mscr = (TempMSCR*)cvAlloc( src->cols*src->rows*sizeof(mscr[0]) ); + double emean = 0; + CvMat* dx = cvCreateMat( src->rows, src->cols-1, CV_64FC1 ); + CvMat* dy = cvCreateMat( src->rows-1, src->cols, CV_64FC1 ); + Ne = preprocessMSER_8UC3( map, edge, &emean, src, mask, dx, dy, Ne, params.edgeBlurSize ); + emean = emean / (double)Ne; + std::sort(edge, edge + Ne, LessThanEdge()); + MSCREdge* edge_ub = edge+Ne; + MSCREdge* edgeptr = edge; + TempMSCR* mscrptr = mscr; + // the evolution process + for ( int i = 0; i < params.maxEvolution; i++ ) + { + double k = (double)i/(double)params.maxEvolution*(TABLE_SIZE-1); + int ti = cvFloor(k); + double reminder = k-ti; + double thres = emean*(chitab3[ti]*(1-reminder)+chitab3[ti+1]*reminder); + // to process all the edges in the list that chi < thres + while ( edgeptr < edge_ub && edgeptr->chi < thres ) + { + MSCRNode* lr = findMSCR( edgeptr->left ); + MSCRNode* rr = findMSCR( edgeptr->right ); + // get the region root (who is responsible) + if ( lr != rr ) + { + // rank idea take from: N-tree Disjoint-Set Forests for Maximally Stable Extremal Regions + if ( rr->rank > lr->rank ) + { + MSCRNode* tmp; + CV_SWAP( lr, rr, tmp ); + } else if ( lr->rank == rr->rank ) { + // at the same rank, we will compare the size + if ( lr->size > rr->size ) + { + MSCRNode* tmp; + CV_SWAP( lr, rr, tmp ); + } + lr->rank++; + } + rr->shortcut = lr; + lr->size += rr->size; + // join rr to the end of list lr (lr is a endless double-linked list) + lr->prev->next = rr; + lr->prev = rr->prev; + rr->prev->next = lr; + rr->prev = lr; + // area threshold force to reinitialize + if ( lr->size > (lr->size-rr->size)*params.areaThreshold ) + { + lr->sizei = lr->size; + lr->reinit = i; + if ( lr->tmsr != NULL ) + { + lr->tmsr->m = lr->dt-lr->di; + lr->tmsr = NULL; + } + lr->di = edgeptr->chi; + lr->s = 1e10; + } + lr->dt = edgeptr->chi; + if ( i > lr->reinit ) + { + double s = (double)(lr->size-lr->sizei)/(lr->dt-lr->di); + if ( s < lr->s ) + { + // skip the first one and check stablity + if ( i > lr->reinit+1 && MSCRStableCheck( lr, params ) ) + { + if ( lr->tmsr == NULL ) + { + lr->gmsr = lr->tmsr = mscrptr; + mscrptr++; + } + lr->tmsr->size = lr->size; + lr->tmsr->head = lr; + lr->tmsr->tail = lr->prev; + lr->tmsr->m = 0; + } + lr->s = s; + } + } + } + edgeptr++; + } + if ( edgeptr >= edge_ub ) + break; + } + for ( TempMSCR* ptr = mscr; ptr < mscrptr; ptr++ ) + // to prune area with margin less than minMargin + if ( ptr->m > params.minMargin ) + { + vector mser; + MSCRNode* lpt = ptr->head; + for ( int i = 0; i < ptr->size; i++ ) + { + Point pt; + pt.x = (lpt->index)&0xffff; + pt.y = (lpt->index)>>16; + lpt = lpt->next; + mser.push_back(pt); + } + msers.push_back(mser); + } + cvReleaseMat( &dx ); + cvReleaseMat( &dy ); + cvFree( &mscr ); + cvFree( &edge ); + cvFree( &map ); +} + /****************************************************************************************\ * Test for KeyPoint * \****************************************************************************************/ @@ -74,6 +752,11 @@ protected: } vector keypoints; + vector > msers; + CvMat src = image; + + extractMSER_8UC3( &src, 0, msers, MSERParams()); + detector->detect(image, keypoints); if(keypoints.empty()) diff --git a/modules/features2d/test/test_matchers_algorithmic.cpp b/modules/features2d/test/test_matchers_algorithmic.cpp index 147ae55bb8..ec71e5a5ad 100644 --- a/modules/features2d/test/test_matchers_algorithmic.cpp +++ b/modules/features2d/test/test_matchers_algorithmic.cpp @@ -533,13 +533,13 @@ void CV_DescriptorMatcherTest::run( int ) TEST( Features2d_DescriptorMatcher_BruteForce, regression ) { CV_DescriptorMatcherTest test( "descriptor-matcher-brute-force", - DescriptorMatcher::create("BFMatcher"), 0.01f ); + DescriptorMatcher::create("BruteForce"), 0.01f ); test.safe_run(); } TEST( Features2d_DescriptorMatcher_FlannBased, regression ) { CV_DescriptorMatcherTest test( "descriptor-matcher-flann-based", - DescriptorMatcher::create("FlannBasedMatcher"), 0.04f ); + DescriptorMatcher::create("FlannBased"), 0.04f ); test.safe_run(); } diff --git a/modules/features2d/test/test_rotation_and_scale_invariance.cpp b/modules/features2d/test/test_rotation_and_scale_invariance.cpp index 90fc2f81ca..f03fa14477 100644 --- a/modules/features2d/test/test_rotation_and_scale_invariance.cpp +++ b/modules/features2d/test/test_rotation_and_scale_invariance.cpp @@ -595,7 +595,7 @@ protected: TEST(Features2d_RotationInvariance_Detector_BRISK, regression) { - DetectorRotationInvarianceTest test(Algorithm::create("Feature2D.BRISK"), + DetectorRotationInvarianceTest test(BRISK::create(), 0.32f, 0.76f); test.safe_run(); @@ -603,7 +603,7 @@ TEST(Features2d_RotationInvariance_Detector_BRISK, regression) TEST(Features2d_RotationInvariance_Detector_ORB, regression) { - DetectorRotationInvarianceTest test(Algorithm::create("Feature2D.ORB"), + DetectorRotationInvarianceTest test(ORB::create(), 0.47f, 0.76f); test.safe_run(); diff --git a/modules/stitching/src/matchers.cpp b/modules/stitching/src/matchers.cpp index 9279df70e1..ad3f6320fb 100644 --- a/modules/stitching/src/matchers.cpp +++ b/modules/stitching/src/matchers.cpp @@ -321,7 +321,7 @@ SurfFeaturesFinder::SurfFeaturesFinder(double hess_thresh, int num_octaves, int { if (num_octaves_descr == num_octaves && num_layers_descr == num_layers) { - surf = Algorithm::create("Feature2D.SURF"); + surf = xfeatures2d::SURF::create(); if( !surf ) CV_Error( Error::StsNotImplemented, "OpenCV was built without SURF support" ); surf->set("hessianThreshold", hess_thresh); @@ -330,8 +330,8 @@ SurfFeaturesFinder::SurfFeaturesFinder(double hess_thresh, int num_octaves, int } else { - detector_ = Algorithm::create("Feature2D.SURF"); - extractor_ = Algorithm::create("Feature2D.SURF"); + detector_ = xfeatures2d::SURF::create(); + extractor_ = xfeatures2d::SURF::create(); if( !detector_ || !extractor_ ) CV_Error( Error::StsNotImplemented, "OpenCV was built without SURF support" );