replaced detector/descriptor evaluation functions from test to features2d; modified...
authorMaria Dimashova <no@email>
Thu, 5 Aug 2010 12:19:26 +0000 (12:19 +0000)
committerMaria Dimashova <no@email>
Thu, 5 Aug 2010 12:19:26 +0000 (12:19 +0000)
modules/features2d/include/opencv2/features2d/features2d.hpp
modules/features2d/src/calonder.cpp
modules/features2d/src/evaluation.cpp [new file with mode: 0644]
tests/cv/src/adetectordescriptor_evaluation.cpp

index 1716d58..88aa105 100644 (file)
@@ -811,127 +811,6 @@ private:
   bool keep_floats_;
 };
 
-#if 0
-class CV_EXPORTS CalonderClassifier
-{
-public:
-    CalonderClassifier();
-    CalonderClassifier( const vector<vector<Point2f> >& points, const vector<Mat>& refimgs,
-                        const vector<vector<int> >& labels=vector<vector<int> >(), int _numClasses=0,
-                        int _pathSize=DEFAULT_PATCH_SIZE,
-                        int _numTrees=DEFAULT_NUM_TREES,
-                        int _treeDepth=DEFAULT_TREE_DEPTH,
-                        int _numViews=DEFAULT_NUM_VIEWS,
-                        int _compressedDim=DEFAULT_COMPRESSED_DIM,
-                        int _compressType=DEFAULT_COMPRESS_TYPE,
-                        int _numQuantBits=DEFAULT_NUM_QUANT_BITS,
-                        const PatchGenerator& patchGenerator=PatchGenerator() );
-
-    virtual ~CalonderClassifier();
-    virtual void clear();
-
-    void train( const vector<vector<Point2f> >& points, const vector<Mat>& refimgs,
-                const vector<vector<int> >& labels=vector<vector<int> >(), int _nclasses=0,
-                int _pathSize=DEFAULT_PATCH_SIZE,
-                int _numTrees=DEFAULT_NUM_TREES,
-                int _treeDepth=DEFAULT_TREE_DEPTH,
-                int _numViews=DEFAULT_NUM_VIEWS,
-                int _compressedDim=DEFAULT_COMPRESSED_DIM,
-                int _compressType=DEFAULT_COMPRESS_TYPE,
-                int _numQuantBits=DEFAULT_NUM_QUANT_BITS,
-                const PatchGenerator& patchGenerator=PatchGenerator() );
-
-    virtual void operator()(const Mat& img, Point2f pt, vector<float>& signature, float thresh=0.f) const;
-    virtual void operator()(const Mat& patch, vector<float>& signature, float thresh=-1.f) const;
-#define QUANTIZATION_AVAILABLE 1
-#if QUANTIZATION_AVAILABLE
-    void quantizePosteriors( int _numQuantBits, bool isClearFloatPosteriors=false );
-    void clearFloatPosteriors();
-    virtual void operator()(const Mat& img, Point2f pt, vector<uchar>& signature, uchar thresh=-1.f) const;
-    virtual void operator()(const Mat& patch, vector<uchar>& signature, uchar thresh=-1.f) const;
-#endif
-
-    void read( const FileNode& fn );
-    void read( std::istream& is );
-    void write( FileStorage& fs ) const;
-
-    bool empty() const;
-
-    void setVerbose( bool _verbose );
-
-    int getPatchSize() const;
-    int getNumTrees() const;
-    int getTreeDepth() const;
-    int getNumViews() const;
-    int getSignatureSize() const;
-    int getCompressType() const;
-    int getNumQuantBits() const;
-    int getOrigNumClasses() const;
-
-
-    enum
-    {
-        COMPRESS_NONE             = -1,
-        COMPRESS_DISTR_GAUSS      =  0,
-        COMPRESS_DISTR_BERNOULLI  =  1,
-        COMPRESS_DISTR_DBFRIENDLY =  2,
-    };
-
-    static float GET_LOWER_QUANT_PERC() { return .03f; }
-    static float GET_UPPER_QUANT_PERC() { return .92f; }
-
-    enum
-    {
-        MAX_NUM_QUANT_BITS = 8,
-        DEFAULT_PATCH_SIZE = 32,
-        DEFAULT_NUM_TREES = 48,
-        DEFAULT_TREE_DEPTH = 9,
-        DEFAULT_NUM_VIEWS = 500,
-        DEFAULT_COMPRESSED_DIM = 176,
-        DEFAULT_COMPRESS_TYPE = COMPRESS_DISTR_BERNOULLI,
-        DEFAULT_NUM_QUANT_BITS = -1,
-    };
-private:
-    void prepare( int _patchSize, int _signatureSize, int _numTrees, int _treeDepth, int _numViews );
-
-    int getLeafIdx( int treeIdx, const Mat& patch ) const;
-    void finalize( int _compressedDim, int _compressType, int _numQuantBits,
-                   const vector<int>& leafSampleCounters);
-
-    void compressLeaves( int _compressedDim, int _compressType );
-
-    bool verbose;
-
-    int patchSize;
-    int signatureSize;
-    int numTrees;
-    int treeDepth;
-    int numViews;
-
-    int origNumClasses;
-    int compressType;
-    int numQuantBits;
-
-    int numLeavesPerTree;
-    int numNodesPerTree;
-
-    struct Node
-    {
-        uchar x1, y1, x2, y2;
-        Node() : x1(0), y1(0), x2(0), y2(0) {}
-        Node( uchar _x1, uchar _y1, uchar _x2, uchar _y2 ) : x1(_x1), y1(_y1), x2(_x2), y2(_y2)
-        {}
-        int operator() (const Mat_<uchar>& patch) const
-        { return patch(y1,x1) > patch(y2, x2) ? 1 : 0; }
-    };
-    vector<Node> nodes;
-    vector<float> posteriors;
-#if QUANTIZATION_AVAILABLE
-    vector<uchar> quantizedPosteriors;
-#endif
-};
-#endif
-
 /****************************************************************************************\
 *                                     One-Way Descriptor                                 *
 \****************************************************************************************/
@@ -1515,6 +1394,10 @@ protected:
     virtual void detectImpl( const Mat& image, const Mat& mask, vector<KeyPoint>& keypoints ) const;
 };
 
+
+CV_EXPORTS Mat windowedMatchingMask( const vector<KeyPoint>& keypoints1, const vector<KeyPoint>& keypoints2,
+                                     float maxDeltaX, float maxDeltaY );
+
 /****************************************************************************************\
 *                                 DescriptorExtractor                                    *
 \****************************************************************************************/
@@ -2274,8 +2157,8 @@ class CV_EXPORTS VectorDescriptorMatch : public GenericDescriptorMatch
 public:
     using GenericDescriptorMatch::add;
 
-    VectorDescriptorMatch( DescriptorExtractor *_extractor = 0, DescriptorMatcher * _matcher = 0 ) :
-                           extractor( _extractor ), matcher( _matcher ) {}
+    VectorDescriptorMatch( const Ptr<DescriptorExtractor>& _extractor, const Ptr<DescriptorMatcher>& _matcher )
+                        : extractor( _extractor ), matcher( _matcher ) {}
 
     ~VectorDescriptorMatch() {}
 
@@ -2303,10 +2186,9 @@ protected:
     //vector<int> classIds;
 };
 
-CV_EXPORTS Mat windowedMatchingMask( const vector<KeyPoint>& keypoints1, const vector<KeyPoint>& keypoints2,
-                                     float maxDeltaX, float maxDeltaY );
-
-
+/****************************************************************************************\
+*                                   Drawing functions                                    *
+\****************************************************************************************/
 struct CV_EXPORTS DrawMatchesFlags
 {
     enum{ DEFAULT = 0, // Output image matrix will be created (Mat::create),
@@ -2318,7 +2200,7 @@ struct CV_EXPORTS DrawMatchesFlags
                                 // Matches will be drawn on existing content of output image.
           NOT_DRAW_SINGLE_POINTS = 2, // Single keypoints will not be drawn.
           DRAW_RICH_KEYPOINTS = 4 // For each keypoint the circle around keypoint with keypoint size and
-                                 // orientation will be drawn.
+                                  // orientation will be drawn.
         };
 };
 
@@ -2345,7 +2227,28 @@ CV_EXPORTS void drawMatches( const Mat& img1, const vector<KeyPoint>& keypoints1
                              const Scalar& matchColor=Scalar::all(-1), const Scalar& singlePointColor=Scalar::all(-1),
                              const vector<vector<char> >& matchesMask=vector<vector<char> >(), int flags=DrawMatchesFlags::DEFAULT );
 
-}
+/****************************************************************************************\
+*                                   Evaluation functions                                 *
+\****************************************************************************************/
+
+CV_EXPORTS void evaluateFeatureDetector( const Mat& img1, const Mat& img2, const Mat& H1to2,
+                                         vector<KeyPoint>* keypoints1, vector<KeyPoint>* keypoints2,
+                                         float& repeatability, int& correspCount,
+                                         const Ptr<FeatureDetector>& fdetector=Ptr<FeatureDetector>() );
+
+CV_EXPORTS void computeRecallPrecisionCurve( const vector<vector<DMatch> >& matches1to2,
+                                             const vector<vector<uchar> >& correctMatches1to2Mask,
+                                             vector<Point2f>& recallPrecisionCurve );
+CV_EXPORTS float getRecall( const vector<Point2f>& recallPrecisionCurve, float l_precision );
+
+CV_EXPORTS void evaluateDescriptorMatch( const Mat& img1, const Mat& img2, const Mat& H1to2,
+                                         vector<KeyPoint>& keypoints1, vector<KeyPoint>& keypoints2,
+                                         vector<vector<DMatch> >* matches1to2, vector<vector<uchar> >* correctMatches1to2Mask,
+                                         vector<Point2f>& recallPrecisionCurve,
+                                         const Ptr<GenericDescriptorMatch>& dmatch=Ptr<GenericDescriptorMatch>() );
+
+
+} /* namespace cv */
 
 #endif /* __cplusplus */
 
index 6958016..fa409a9 100644 (file)
@@ -991,596 +991,4 @@ void RTreeClassifier::discardFloatPosteriors()
    printf("[OK] RTC: discarded float posteriors of all trees\n");
 }
 
-#if 0
-const int progressBarSize = 50;
-
-CalonderClassifier::CalonderClassifier()
-{
-    verbose = false;
-    clear();
-}
-
-CalonderClassifier::~CalonderClassifier()
-{}
-
-CalonderClassifier::CalonderClassifier( const vector<vector<Point2f> >& points, const vector<Mat>& refimgs,
-                                        const vector<vector<int> >& labels, int _numClasses,
-                                        int _pathSize, int _numTrees, int _treeDepth,
-                                        int _numViews, int _compressedDim, int _compressType, int _numQuantBits,
-                                        const PatchGenerator &patchGenerator )
-{
-    verbose = false;
-    train( points, refimgs, labels, _numClasses, _pathSize, _numTrees, _treeDepth, _numViews,
-           _compressedDim, _compressType, _numQuantBits, patchGenerator );
-}
-
-int CalonderClassifier::getPatchSize() const
-{ return patchSize; }
-
-int CalonderClassifier::getNumTrees() const
-{ return numTrees; }
-
-int CalonderClassifier::getTreeDepth() const
-{ return treeDepth; }
-
-int CalonderClassifier::getNumViews() const
-{ return numViews; }
-
-int CalonderClassifier::getSignatureSize() const
-{ return signatureSize; }
-
-int CalonderClassifier::getCompressType() const
-{ return compressType; }
-
-int CalonderClassifier::getNumQuantBits() const
-{ return numQuantBits; }
-
-int CalonderClassifier::getOrigNumClasses() const
-{ return origNumClasses; }
-
-void CalonderClassifier::setVerbose( bool _verbose )
-{
-    verbose = _verbose;
-}
-
-void CalonderClassifier::clear()
-{
-    patchSize = numTrees = origNumClasses = signatureSize = treeDepth = numViews = numQuantBits = 0;
-    compressType = COMPRESS_NONE;
-
-    nodes.clear();
-    posteriors.clear();
-#if QUANTIZATION_AVAILABLE
-    quantizedPosteriors.clear();
-#endif
-}
-
-bool CalonderClassifier::empty() const
-{
-    return posteriors.empty() && quantizedPosteriors.empty();
-}
-
-void CalonderClassifier::prepare( int _patchSize, int _signatureSize, int _numTrees, int _treeDepth, int _numViews )
-{
-    clear();
-
-    patchSize = _patchSize;
-    signatureSize = _signatureSize;
-    numTrees = _numTrees;
-    treeDepth = _treeDepth;
-    numViews = _numViews;
-
-    numLeavesPerTree = 1 << treeDepth;      // 2^d
-    numNodesPerTree = numLeavesPerTree - 1; // 2^d - 1
-
-    nodes = vector<Node>( numTrees*numNodesPerTree );
-    posteriors = vector<float>( numTrees*numLeavesPerTree*signatureSize, 0.f );
-}
-
-static int calcNumPoints( const vector<vector<Point2f> >& points )
-{
-    int count = 0;
-    for( size_t i = 0; i < points.size(); i++ )
-        count += points[i].size();
-    return count;
-}
-
-void CalonderClassifier::train( const vector<vector<Point2f> >& points, const vector<Mat>& refimgs,
-                                const vector<vector<int> >& labels, int _numClasses,
-                                int _patchSize, int _numTrees, int _treeDepth, int _numViews,
-                                int _compressedDim, int _compressType, int _numQuantBits,
-                                const PatchGenerator &patchGenerator )
-{
-    if( points.empty() || refimgs.size() != points.size() )
-        CV_Error( CV_StsBadSize, "points vector must be no empty and refimgs must have the same size as points" );
-    if( _patchSize < 5 || _patchSize >= 256 )
-        CV_Error( CV_StsBadArg, "patchSize must be in [5, 255]");
-    if( _numTrees <= 0 || _treeDepth <= 0 )
-        CV_Error( CV_StsBadArg, "numTrees, treeDepth, numViews must be positive");
-    int numPoints = calcNumPoints( points );
-    if( !labels.empty() && ( labels.size() != points.size() || _numClasses <=0 || _numClasses > numPoints ) )
-        CV_Error( CV_StsBadArg, "labels has incorrect size or _numClasses is not in [1, numPoints]");
-    _numViews = std::max( 1, _numViews );
-
-    int _origNumClasses = labels.empty() ? numPoints : _numClasses;
-
-    if( verbose )
-    {
-        cout << "Using train parameters:" << endl;
-        cout << "   patchSize=" << _patchSize << endl;
-        cout << "   numTrees=" << _numTrees << endl;
-        cout << "   treeDepth=" << _treeDepth << endl;
-        cout << "   numViews=" << _numViews << endl;
-        cout << "   compressedDim=" << _compressedDim << endl;
-        cout << "   compressType=" << _compressType << endl;
-        cout << "   numQuantBits=" << _numQuantBits << endl;
-        cout << endl
-             << "   numPoints=" << numPoints << endl;
-        cout << "   origNumClasses=" << _origNumClasses << endl;
-    }
-
-    prepare( _patchSize, _origNumClasses, _numTrees, _treeDepth, _numViews );
-
-    origNumClasses = _origNumClasses;
-    vector<int> leafSampleCounters = vector<int>( numTrees*numLeavesPerTree, 0 );
-    // generate nodes
-    RNG rng = theRNG();
-    for( int i = 0; i < numTrees*numNodesPerTree; i++ )
-    {
-        uchar x1 = rng(_patchSize);
-        uchar y1 = rng(_patchSize);
-        uchar x2 = rng(_patchSize);
-        uchar y2 = rng(_patchSize);
-        nodes[i] = Node(x1, y1, x2, y2);
-    }
-
-    Size size( patchSize, patchSize );
-    Mat patch;
-    if( verbose ) cout << "START training..." << endl;
-    for( size_t treeIdx = 0; treeIdx < (size_t)numTrees; treeIdx++ )
-    {
-        if( verbose ) cout << "< tree " << treeIdx << endl;
-        int globalPointIdx = 0;
-        int* treeLeafSampleCounters = &leafSampleCounters[treeIdx*numLeavesPerTree];
-        float* treePosteriors = &posteriors[treeIdx*numLeavesPerTree*signatureSize];
-        for( size_t imgIdx = 0; imgIdx < points.size(); imgIdx++ )
-        {
-            const Point2f* imgPoints = &points[imgIdx][0];
-            const int* imgLabels = labels.empty() ? 0 : &labels[imgIdx][0];
-            int last = -1, cur;
-            for( size_t pointIdx = 0; pointIdx < points[imgIdx].size(); pointIdx++, globalPointIdx++ )
-            {
-                int classID = imgLabels==0 ? globalPointIdx : imgLabels[pointIdx];
-                Point2f pt = imgPoints[pointIdx];
-                const Mat& src = refimgs[imgIdx];
-
-                if( verbose && (cur = (int)((float)globalPointIdx/numPoints*progressBarSize)) != last )
-                {
-                    last = cur;
-                    cout << ".";
-                    cout.flush();
-                }
-
-                CV_Assert( classID >= 0 && classID < signatureSize );
-                for( int v = 0; v < numViews; v++ )
-                {
-                    patchGenerator( src, pt, patch, size, rng );
-                    // add sample
-                    int leafIdx = getLeafIdx( treeIdx, patch );
-                    treeLeafSampleCounters[leafIdx]++;
-                    treePosteriors[leafIdx*signatureSize + classID]++;
-                }
-            }
-        }
-
-        if( verbose ) cout << endl << ">" << endl;
-    }
-
-    _compressedDim = std::max( 0, std::min(signatureSize, _compressedDim) );
-    _numQuantBits = std::max( 0, std::min((int)MAX_NUM_QUANT_BITS, _numQuantBits) );
-    finalize( _compressedDim, _compressType, _numQuantBits, leafSampleCounters );
-
-    if( verbose ) cout << "END training." << endl;
-}
-
-int CalonderClassifier::getLeafIdx( int treeIdx, const Mat& patch ) const
-{
-    const Node* treeNodes = &nodes[treeIdx*numNodesPerTree];
-    int idx = 0;
-    for( int d = 0; d < treeDepth-1; d++ )
-    {
-        int offset = treeNodes[idx](patch);
-        idx = 2*idx + 1 + offset;
-    }
-    return idx;
-}
-
-void CalonderClassifier::finalize( int _compressedDim, int _compressType, int _numQuantBits,
-                                   const vector<int>& leafSampleCounters )
-{
-    for( int ti = 0; ti < numTrees; ti++ )
-    {
-        const int* treeLeafSampleCounters = &leafSampleCounters[ti*numLeavesPerTree];
-        float* treePosteriors = &posteriors[ti*numLeavesPerTree*signatureSize];
-        // Normalize by number of patches to reach each leaf
-        for( int li = 0; li < numLeavesPerTree; li++ )
-        {
-            int sampleCount = treeLeafSampleCounters[li];
-            if( sampleCount != 0 )
-            {
-                float normalizer = 1.0f / sampleCount;
-                int leafPosteriorIdx = li*signatureSize;
-                for( int ci = 0; ci < signatureSize; ci++ )
-                   treePosteriors[leafPosteriorIdx + ci] *= normalizer;
-             }
-        }
-    }
-
-    // apply compressive sensing
-    if( _compressedDim > 0 && _compressedDim < signatureSize )
-        compressLeaves( _compressedDim, _compressType );
-    else
-    {
-        if( verbose )
-            cout << endl << "[WARNING] NO compression to leaves applied, because _compressedDim=" << _compressedDim << endl;
-    }
-
-    // convert float-posteriors to uchar-posteriors (quantization step)
-#if QUANTIZATION_AVAILABLE
-    if( _numQuantBits > 0 )
-        quantizePosteriors( _numQuantBits );
-    else
-    {
-        if( verbose )
-            cout << endl << "[WARNING] NO quantization to posteriors, because _numQuantBits=" << _numQuantBits << endl;
-    }
-#endif
-}
-
-Mat createCompressionMatrix( int rows, int cols, int distrType )
-{
-    Mat mtr( rows, cols, CV_32FC1 );
-    assert( rows <= cols );
-
-    RNG rng(23);
-
-    if( distrType == CalonderClassifier::COMPRESS_DISTR_GAUSS )
-    {
-        float sigma = 1./rows;
-        for( int y = 0; y < rows; y++ )
-            for( int x = 0; x < cols; x++ )
-                mtr.at<float>(y,x) = rng.gaussian( sigma );
-    }
-    else if( distrType == CalonderClassifier::COMPRESS_DISTR_BERNOULLI )
-    {
-        float par = (float)(1./sqrt((float)rows));
-        for( int y = 0; y < rows; y++ )
-            for( int x = 0; x < cols; x++ )
-                mtr.at<float>(y,x) = rng(2)==0 ? par : -par;
-    }
-    else if( distrType == CalonderClassifier::COMPRESS_DISTR_DBFRIENDLY )
-    {
-        float par = (float)sqrt(3./rows);
-        for( int y = 0; y < rows; y++ )
-            for( int x = 0; x < cols; x++ )
-            {
-                int rng6 = rng(6);
-                mtr.at<float>(y,x) = rng6==0 ? par : (rng6==1 ? -par : 0.f);
-            }
-    }
-    else
-        CV_Assert( 0 );
-
-    return mtr;
-}
-
-void CalonderClassifier::compressLeaves( int _compressedDim, int _compressType )
-{
-    if( verbose )
-        cout << endl << "[OK] compressing leaves with matrix " << _compressedDim << " x " << signatureSize << endl;
-
-    Mat compressionMtrT = (createCompressionMatrix( _compressedDim, signatureSize, _compressType )).t();
-
-    vector<float> comprPosteriors( numTrees*numLeavesPerTree*_compressedDim, 0);
-    Mat( numTrees*numLeavesPerTree, _compressedDim, CV_32FC1, &comprPosteriors[0] ) =
-            Mat( numTrees*numLeavesPerTree, signatureSize, CV_32FC1, &posteriors[0]) * compressionMtrT;
-
-    posteriors.resize( comprPosteriors.size() );
-    copy( comprPosteriors.begin(), comprPosteriors.end(), posteriors.begin() );
-
-    signatureSize = _compressedDim;
-    compressType = _compressType;
-}
-
-#if QUANTIZATION_AVAILABLE
-static float percentile( const float* data, int n, float p )
-{
-   assert( n>0 );
-   assert( p>=0 && p<=1 );
-
-   vector<float> vec( data, data+n );
-   std::sort(vec.begin(), vec.end());
-   int ix = (int)(p*(n-1));
-   return vec[ix];
-}
-
-void quantizeVector( const float* src, int dim, float fbounds[2], uchar ubounds[2], uchar* dst )
-{
-    assert( fbounds[0] < fbounds[1] );
-    assert( ubounds[0] < ubounds[1] );
-
-    float normFactor = 1.f/(fbounds[1] - fbounds[0]);
-    for( int i = 0; i < dim; i++ )
-    {
-        float part = (src[i] - fbounds[0]) * normFactor;
-        assert( 0 <= part && part <= 1 ) ;
-        uchar val = ubounds[0] +  (uchar)( part*ubounds[1] );
-        dst[i] = std::max( 0, (int)std::min(ubounds[1], val) );
-    }
-}
-
-void CalonderClassifier::quantizePosteriors( int _numQuantBits, bool isClearFloatPosteriors )
-{
-    uchar ubounds[] = { 0, (uchar)((1<<_numQuantBits)-1) };
-    float fbounds[] = { 0.f, 0.f };
-
-    int totalLeavesCount = numTrees*numLeavesPerTree;
-    for( int li = 0; li < totalLeavesCount; li++ ) // TODO for some random choosen leaves !
-    {
-        fbounds[0] += percentile( &posteriors[li*signatureSize], signatureSize, GET_LOWER_QUANT_PERC() );
-        fbounds[1] += percentile( &posteriors[li*signatureSize], signatureSize, GET_UPPER_QUANT_PERC() );
-    }
-    fbounds[0] /= totalLeavesCount;
-    fbounds[1] /= totalLeavesCount;
-
-    quantizedPosteriors.resize( posteriors.size() );
-    quantizeVector( &posteriors[0], posteriors.size(), fbounds, ubounds, &quantizedPosteriors[0] );
-
-    if( isClearFloatPosteriors )
-        clearFloatPosteriors();
-}
-
-void CalonderClassifier::clearFloatPosteriors()
-{
-    quantizedPosteriors.clear();
-}
-
-#endif
-
-void CalonderClassifier::operator()( const Mat& img, Point2f pt, vector<float>& signature, float thresh ) const
-{
-    if( img.empty() || img.type() != CV_8UC1 )
-        return;
-
-    Mat patch;
-    getRectSubPix(img, Size(patchSize,patchSize), pt, patch, img.type());
-    (*this)( patch, signature, thresh );
-}
-
-void CalonderClassifier::operator()( const Mat& patch, vector<float>& signature, float thresh ) const
-{
-    if( posteriors.empty() || patch.empty() || patch.type() != CV_8UC1 || patch.cols < patchSize || patch.rows < patchSize )
-        return;
-
-    int treePostSize = numLeavesPerTree*signatureSize;
-
-    signature.resize( signatureSize, 0.f );
-    float* sig = &signature[0];
-    for( int ti = 0; ti < numTrees; ti++ )
-    {
-        int leafIdx = getLeafIdx( ti, patch );
-        const float* post = &posteriors[ti*treePostSize + leafIdx*signatureSize];
-        for( int ci = 0; ci < signatureSize; ci++ )
-            sig[ci] += post[ci];
-    }
-    float coef = 1.f/numTrees;
-    for( int ci = 0; ci < signatureSize; ci++ )
-    {
-        sig[ci] *= coef;
-        if( sig[ci] < thresh )
-            sig[ci] = 0;
-    }
-}
-
-#if QUANTIZATION_AVAILABLE
-void CalonderClassifier::operator()( const Mat& img, Point2f pt, vector<uchar>& signature, uchar thresh ) const
-{
-    if( img.empty() || img.type() != CV_8UC1 )
-        return;
-
-    Mat patch;
-    getRectSubPix(img, Size(patchSize,patchSize), pt, patch, img.type());
-    (*this)(patch, signature, thresh );
-}
-
-void CalonderClassifier::operator()( const Mat& patch, vector<uchar>& signature, uchar thresh ) const
-{
-    if( quantizedPosteriors.empty() || patch.empty() || patch.type() != CV_8UC1 || patch.cols > patchSize || patch.rows > patchSize )
-        return;
-
-    int treePostSize = numLeavesPerTree*signatureSize;
-
-    vector<float> sum( signatureSize, 0.f );
-    for( int ti = 0; ti < numTrees; ti++ )
-    {
-        int leafIdx = getLeafIdx( ti, patch );
-        const uchar* post = &quantizedPosteriors[ti*treePostSize + leafIdx*signatureSize];
-        for( int ci = 0; ci < signatureSize; ci++ )
-            sum[ci] += post[ci];
-    }
-    float coef = 1.f/numTrees;
-    signature.resize( signatureSize );
-    uchar* sig = &signature[0];
-    for( int ci = 0; ci < signatureSize; ci++ )
-    {
-        sig[ci] = (uchar)(sum[ci]*coef);
-        if( sig[ci] < thresh )
-            sig[ci] = 0;
-    }
-}
-#endif
-
-void CalonderClassifier::read( const FileNode& fn )
-{
-    prepare( fn["patchSize"], fn["signatureSize"], fn["numTrees"], fn["treeDepth"], fn["numViews"] );
-    origNumClasses = fn["origNumClasses"];
-    compressType = fn["compressType"];
-    int _numQuantBits = fn["numQuantBits"];
-
-    for( int ti = 0; ti < numTrees; ti++ )
-    {
-        stringstream treeName;
-        treeName << "tree" << ti;
-        FileNode treeFN = fn["trees"][treeName.str()];
-
-        Node* treeNodes = &nodes[ti*numNodesPerTree];
-        FileNodeIterator nodesFNIter = treeFN["nodes"].begin();
-        for( int ni = 0; ni < numNodesPerTree; ni++ )
-        {
-            Node* node = treeNodes + ni;
-            nodesFNIter >> node->x1 >> node->y1 >> node->x2 >> node->y2;
-        }
-
-        FileNode posteriorsFN = treeFN["posteriors"];
-        for( int li = 0; li < numLeavesPerTree; li++ )
-        {
-            stringstream leafName;
-            leafName << "leaf" << li;
-            float* post = &posteriors[ti*numLeavesPerTree*signatureSize + li*signatureSize];
-            FileNodeIterator leafFNIter = posteriorsFN[leafName.str()].begin();
-            for( int ci = 0; ci < signatureSize; ci++ )
-                leafFNIter >> post[ci];
-        }
-    }
-#if QUANTIZATION_AVAILABLE
-    if( _numQuantBits )
-        quantizePosteriors(_numQuantBits);
-#endif
-}
-
-void CalonderClassifier::write( FileStorage& fs ) const
-{
-    if( !fs.isOpened() )
-        return;
-    fs << "patchSize" << patchSize;
-    fs << "numTrees" << numTrees;
-    fs << "treeDepth" << treeDepth;
-    fs << "numViews" << numViews;
-    fs << "origNumClasses" << origNumClasses;
-    fs << "signatureSize" << signatureSize;
-    fs << "compressType" << compressType;
-    fs << "numQuantBits" << numQuantBits;
-
-    fs << "trees" << "{";
-    for( int ti = 0; ti < numTrees; ti++ )
-    {
-        stringstream treeName;
-        treeName << "tree" << ti;
-        fs << treeName.str() << "{";
-
-        fs << "nodes" << "[:";
-        const Node* treeNodes = &nodes[ti*numNodesPerTree];
-        for( int ni = 0; ni < numNodesPerTree; ni++ )
-        {
-            const Node* node = treeNodes + ni;
-            fs << node->x1 << node->y1 << node->x2 << node->y2;
-        }
-        fs << "]"; // nodes
-
-        fs << "posteriors" << "{";
-        for( int li = 0; li < numLeavesPerTree; li++ )
-        {
-            stringstream leafName;
-            leafName << "leaf" << li;
-            fs << leafName.str() << "[:";
-            const float* post = &posteriors[ti*numLeavesPerTree*signatureSize + li*signatureSize];
-            for( int ci = 0; ci < signatureSize; ci++ )
-            {
-                fs << post[ci];
-            }
-            fs << "]"; // leaf
-        }
-        fs << "}"; // posteriors
-        fs << "}"; // tree
-    }
-    fs << "}"; // trees
-}
-
-struct RTreeNode
-{
-    short offset1, offset2;
-};
-
-void CalonderClassifier::read( istream &is )
-{
-    int _patchSize, _numTrees, _treeDepth, _numViews, _signatureSize, _origNumClasses, _numQuantBits, _compressType;
-
-    _patchSize = 32;
-    _numViews = 0;
-    _compressType = COMPRESS_DISTR_BERNOULLI;
-    is.read((char*)(&_numTrees), sizeof(_numTrees));
-    is.read((char*)(&_signatureSize), sizeof(_signatureSize));
-    is.read((char*)(&_origNumClasses), sizeof(_origNumClasses));
-    is.read((char*)(&_numQuantBits), sizeof(_numQuantBits));
-
-    // 1st tree
-    int _classes;
-    is.read((char*)(&_classes), sizeof(_classes));
-    CV_Assert( _signatureSize == _classes );
-    is.read((char*)(&_treeDepth), sizeof(_treeDepth));
-
-    prepare( _patchSize, _signatureSize, _numTrees, _treeDepth, _numViews );
-
-    origNumClasses = _origNumClasses;
-    compressType = _compressType;
-
-    if( _numQuantBits>8 )
-    {
-        if( verbose )
-            cout << "[WARNING] suspicious value numQuantBits=" << numQuantBits << " found; setting to " << DEFAULT_NUM_QUANT_BITS;
-        _numQuantBits = DEFAULT_NUM_QUANT_BITS;
-    }
-
-    // 1st tree
-    vector<RTreeNode> rtreeNodes(numNodesPerTree);
-    is.read((char*)(&rtreeNodes[0]), numNodesPerTree * sizeof(rtreeNodes[0]));
-    for( int ni = 0; ni < numNodesPerTree; ni ++ )
-    {
-        short offset1 = rtreeNodes[ni].offset1,
-              offset2 = rtreeNodes[ni].offset2;
-        nodes[ni] = Node(offset1 % _patchSize, offset1 / _patchSize, offset2 % _patchSize, offset2 / _patchSize );
-    }
-    for( int li = 0; li < numLeavesPerTree; li++ )
-        is.read((char*)&posteriors[li*signatureSize], signatureSize * sizeof(float));
-
-    // other trees
-    for( int treeIdx = 1; treeIdx < numTrees; treeIdx++ )
-    {
-        is.read((char*)(&_classes), sizeof(_classes));
-        CV_Assert( _classes == signatureSize );
-        is.read((char*)(&_treeDepth), sizeof(_treeDepth));
-        CV_Assert( _treeDepth == treeDepth );
-
-        is.read((char*)(&rtreeNodes[0]), numNodesPerTree * sizeof(rtreeNodes[0]));
-
-        Node* treeNodes = &nodes[treeIdx*numNodesPerTree];
-        for( int ni = 0; ni < numNodesPerTree; ni ++ )
-        {
-            short offset1 = rtreeNodes[ni].offset1,
-                  offset2 = rtreeNodes[ni].offset2;
-            treeNodes[ni] = Node(offset1 % _patchSize, offset1 / _patchSize, offset2 % _patchSize, offset2 / _patchSize );
-        }
-        float* treePosteriors = &posteriors[treeIdx*numLeavesPerTree*signatureSize];
-        for( int li = 0; li < numLeavesPerTree; li++ )
-            is.read((char*)&treePosteriors[li*signatureSize], signatureSize * sizeof(float));
-
-    }
-
-#if QUANTIZATION_AVAILABLE
-    if( _numQuantBits )
-        quantizePosteriors(_numQuantBits);
-#endif
-}
-#endif
-
 }
diff --git a/modules/features2d/src/evaluation.cpp b/modules/features2d/src/evaluation.cpp
new file mode 100644 (file)
index 0000000..d30ab61
--- /dev/null
@@ -0,0 +1,502 @@
+//*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.
+//
+//
+//                           License Agreement
+//                For Open Source Computer Vision Library
+//
+// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
+// Copyright (C) 2009, Willow Garage Inc., 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 the copyright holders 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 "precomp.hpp"
+#include <limits>
+
+using namespace cv;
+using namespace std;
+
+inline Point2f applyHomography( const Mat_<double>& H, const Point2f& pt )
+{
+    double z = H(2,0)*pt.x + H(2,1)*pt.y + H(2,2);
+    if( z )
+    {
+        double w = 1./z;
+        return Point2f( (H(0,0)*pt.x + H(0,1)*pt.y + H(0,2))*w, (H(1,0)*pt.x + H(1,1)*pt.y + H(1,2))*w );
+    }
+    return Point2f( numeric_limits<double>::max(), numeric_limits<double>::max() );
+}
+
+inline void linearizeHomographyAt( const Mat_<double>& H, const Point2f& pt, Mat_<double>& A )
+{
+    A.create(2,2);
+    double p1 = H(0,0)*pt.x + H(0,1)*pt.y + H(0,2),
+           p2 = H(1,0)*pt.x + H(1,1)*pt.y + H(1,2),
+           p3 = H(2,0)*pt.x + H(2,1)*pt.y + H(2,2),
+           p3_2 = p3*p3;
+    if( p3 )
+    {
+        A(0,0) = H(0,0)/p3 - p1*H(2,0)/p3_2; // fxdx
+        A(0,1) = H(0,1)/p3 - p1*H(2,1)/p3_2; // fxdy
+
+        A(1,0) = H(1,0)/p3 - p2*H(2,0)/p3_2; // fydx
+        A(1,1) = H(1,1)/p3 - p2*H(2,1)/p3_2; // fydx
+    }
+    else
+        A.setTo(Scalar::all(numeric_limits<double>::max()));
+}
+
+class EllipticKeyPoint
+{
+public:
+    EllipticKeyPoint();
+    EllipticKeyPoint( const Point2f& _center, const Scalar& _ellipse );
+
+    static void convert( const vector<KeyPoint>& src, vector<EllipticKeyPoint>& dst );
+    static void convert( const vector<EllipticKeyPoint>& src, vector<KeyPoint>& dst );
+
+    static Mat_<double> getSecondMomentsMatrix( const Scalar& _ellipse );
+    Mat_<double> getSecondMomentsMatrix() const;
+
+    void calcProjection( const Mat_<double>& H, EllipticKeyPoint& projection ) const;
+    static void calcProjection( const vector<EllipticKeyPoint>& src, const Mat_<double>& H, vector<EllipticKeyPoint>& dst );
+
+    Point2f center;
+    Scalar ellipse; // 3 elements a, b, c: ax^2+2bxy+cy^2=1
+    Size_<float> axes; // half lenght of elipse axes
+    Size_<float> boundingBox; // half sizes of bounding box which sides are parallel to the coordinate axes
+};
+
+EllipticKeyPoint::EllipticKeyPoint()
+{
+    *this = EllipticKeyPoint(Point2f(0,0), Scalar(1, 0, 1) );
+}
+
+EllipticKeyPoint::EllipticKeyPoint( const Point2f& _center, const Scalar& _ellipse )
+{
+    center = _center;
+    ellipse = _ellipse;
+
+    Mat_<double> M = getSecondMomentsMatrix(_ellipse), eval;
+    eigen( M, eval );
+    assert( eval.rows == 2 && eval.cols == 1 );
+    axes.width = 1.f / sqrt(eval(0,0));
+    axes.height = 1.f / sqrt(eval(1,0));
+
+    float ac_b2 = ellipse[0]*ellipse[2] - ellipse[1]*ellipse[1];
+    boundingBox.width = sqrt(ellipse[2]/ac_b2);
+    boundingBox.height = sqrt(ellipse[0]/ac_b2);
+}
+
+Mat_<double> EllipticKeyPoint::getSecondMomentsMatrix( const Scalar& _ellipse )
+{
+    Mat_<double> M(2, 2);
+    M(0,0) = _ellipse[0];
+    M(1,0) = M(0,1) = _ellipse[1];
+    M(1,1) = _ellipse[2];
+    return M;
+}
+
+Mat_<double> EllipticKeyPoint::getSecondMomentsMatrix() const
+{
+    return getSecondMomentsMatrix(ellipse);
+}
+
+void EllipticKeyPoint::calcProjection( const Mat_<double>& H, EllipticKeyPoint& projection ) const
+{
+    Point2f dstCenter = applyHomography(H, center);
+
+    Mat_<double> invM; invert(getSecondMomentsMatrix(), invM);
+    Mat_<double> Aff; linearizeHomographyAt(H, center, Aff);
+    Mat_<double> dstM; invert(Aff*invM*Aff.t(), dstM);
+
+    projection = EllipticKeyPoint( dstCenter, Scalar(dstM(0,0), dstM(0,1), dstM(1,1)) );
+}
+
+void EllipticKeyPoint::convert( const vector<KeyPoint>& src, vector<EllipticKeyPoint>& dst )
+{
+    if( !src.empty() )
+    {
+        dst.resize(src.size());
+        for( size_t i = 0; i < src.size(); i++ )
+        {
+            float rad = src[i].size/2;
+            assert( rad );
+            float fac = 1.f/(rad*rad);
+            dst[i] = EllipticKeyPoint( src[i].pt, Scalar(fac, 0, fac) );
+        }
+    }
+}
+
+void EllipticKeyPoint::convert( const vector<EllipticKeyPoint>& src, vector<KeyPoint>& dst )
+{
+    if( !src.empty() )
+    {
+        dst.resize(src.size());
+        for( size_t i = 0; i < src.size(); i++ )
+        {
+            Size_<float> axes = src[i].axes;
+            float rad = sqrt(axes.height*axes.width);
+            dst[i] = KeyPoint(src[i].center, 2*rad );
+        }
+    }
+}
+
+void EllipticKeyPoint::calcProjection( const vector<EllipticKeyPoint>& src, const Mat_<double>& H, vector<EllipticKeyPoint>& dst )
+{
+    if( !src.empty() )
+    {
+        assert( !H.empty() && H.cols == 3 && H.rows == 3);
+        dst.resize(src.size());
+        vector<EllipticKeyPoint>::const_iterator srcIt = src.begin();
+        vector<EllipticKeyPoint>::iterator       dstIt = dst.begin();
+        for( ; srcIt != src.end(); ++srcIt, ++dstIt )
+            srcIt->calcProjection(H, *dstIt);
+    }
+}
+
+static void filterEllipticKeyPointsByImageSize( vector<EllipticKeyPoint>& keypoints, const Size& imgSize )
+{
+    if( !keypoints.empty() )
+    {
+        vector<EllipticKeyPoint> filtered;
+        filtered.reserve(keypoints.size());
+        vector<EllipticKeyPoint>::const_iterator it = keypoints.begin();
+        for( int i = 0; it != keypoints.end(); ++it, i++ )
+        {
+            if( it->center.x + it->boundingBox.width < imgSize.width &&
+                it->center.x - it->boundingBox.width > 0 &&
+                it->center.y + it->boundingBox.height < imgSize.height &&
+                it->center.y - it->boundingBox.height > 0 )
+                filtered.push_back(*it);
+        }
+        keypoints.assign(filtered.begin(), filtered.end());
+    }
+}
+
+static void overlap( const vector<EllipticKeyPoint>& keypoints1, const vector<EllipticKeyPoint>& keypoints2t, bool commonPart,
+                     SparseMat_<float>& overlaps )
+{
+    overlaps.clear();
+    if( keypoints1.empty() || keypoints2t.empty() )
+        return;
+
+    int size[] = { keypoints1.size(), keypoints2t.size() };
+    overlaps.create( 2, size );
+
+    for( size_t i1 = 0; i1 < keypoints1.size(); i1++ )
+    {
+        EllipticKeyPoint kp1 = keypoints1[i1];
+        float maxDist = sqrt(kp1.axes.width*kp1.axes.height),
+              fac = 30.f/maxDist;
+        if( !commonPart )
+            fac=3;
+
+        maxDist = maxDist*4;
+        fac = 1.0/(fac*fac);
+
+        EllipticKeyPoint keypoint1a = EllipticKeyPoint( kp1.center, Scalar(fac*kp1.ellipse[0], fac*kp1.ellipse[1], fac*kp1.ellipse[2]) );
+
+        for( size_t i2 = 0; i2 < keypoints2t.size(); i2++ )
+        {
+            EllipticKeyPoint kp2 = keypoints2t[i2];
+            Point2f diff = kp2.center - kp1.center;
+
+            if( norm(diff) < maxDist )
+            {
+                EllipticKeyPoint keypoint2a = EllipticKeyPoint( kp2.center, Scalar(fac*kp2.ellipse[0], fac*kp2.ellipse[1], fac*kp2.ellipse[2]) );
+                //find the largest eigenvalue
+                float maxx =  ceil(( keypoint1a.boundingBox.width > (diff.x+keypoint2a.boundingBox.width)) ?
+                                     keypoint1a.boundingBox.width : (diff.x+keypoint2a.boundingBox.width));
+                float minx = floor((-keypoint1a.boundingBox.width < (diff.x-keypoint2a.boundingBox.width)) ?
+                                    -keypoint1a.boundingBox.width : (diff.x-keypoint2a.boundingBox.width));
+
+                float maxy =  ceil(( keypoint1a.boundingBox.height > (diff.y+keypoint2a.boundingBox.height)) ?
+                                     keypoint1a.boundingBox.height : (diff.y+keypoint2a.boundingBox.height));
+                float miny = floor((-keypoint1a.boundingBox.height < (diff.y-keypoint2a.boundingBox.height)) ?
+                                    -keypoint1a.boundingBox.height : (diff.y-keypoint2a.boundingBox.height));
+                float mina = (maxx-minx) < (maxy-miny) ? (maxx-minx) : (maxy-miny) ;
+                float dr = mina/50.0;
+                float bua = 0, bna = 0;
+                //compute the area
+                for( float rx1 = minx; rx1 <= maxx; rx1+=dr )
+                {
+                    float rx2 = rx1-diff.x;
+                    for( float ry1=miny; ry1<=maxy; ry1+=dr )
+                    {
+                        float ry2=ry1-diff.y;
+                        //compute the distance from the ellipse center
+                        float e1 = keypoint1a.ellipse[0]*rx1*rx1+2*keypoint1a.ellipse[1]*rx1*ry1+keypoint1a.ellipse[2]*ry1*ry1;
+                        float e2 = keypoint2a.ellipse[0]*rx2*rx2+2*keypoint2a.ellipse[1]*rx2*ry2+keypoint2a.ellipse[2]*ry2*ry2;
+                        //compute the area
+                        if( e1<1 && e2<1 ) bna++;
+                        if( e1<1 || e2<1 ) bua++;
+                    }
+                }
+                if( bna > 0)
+                    overlaps.ref(i1,i2) = bna/bua;
+            }
+        }
+    }
+}
+
+static void calculateRepeatability( const Mat& img1, const Mat& img2, const Mat& H1to2,
+                                    const vector<KeyPoint>& _keypoints1, const vector<KeyPoint>& _keypoints2,
+                                    float& repeatability, int& correspondencesCount,
+                                    SparseMat_<uchar>* thresholdedOverlapMask=0 )
+{
+    vector<EllipticKeyPoint> keypoints1, keypoints2, keypoints1t, keypoints2t;
+    EllipticKeyPoint::convert( _keypoints1, keypoints1 );
+    EllipticKeyPoint::convert( _keypoints2, keypoints2 );
+
+    // calculate projections of key points
+    EllipticKeyPoint::calcProjection( keypoints1, H1to2, keypoints1t );
+    Mat H2to1; invert(H1to2, H2to1);
+    EllipticKeyPoint::calcProjection( keypoints2, H2to1, keypoints2t );
+
+    bool ifEvaluateDetectors = !thresholdedOverlapMask; // == commonPart
+    float overlapThreshold;
+    if( ifEvaluateDetectors )
+    {
+        overlapThreshold = 1.f - 0.4f;
+
+        // remove key points from outside of the common image part
+        Size sz1 = img1.size(), sz2 = img2.size();
+        filterEllipticKeyPointsByImageSize( keypoints1, sz1 );
+        filterEllipticKeyPointsByImageSize( keypoints1t, sz2 );
+        filterEllipticKeyPointsByImageSize( keypoints2, sz2 );
+        filterEllipticKeyPointsByImageSize( keypoints2t, sz1 );
+    }
+    else
+    {
+        overlapThreshold = 1.f - 0.5f;
+    }
+    int minCount = min( keypoints1.size(), keypoints2t.size() );
+
+    // calculate overlap errors
+    SparseMat_<float> overlaps;
+    overlap( keypoints1, keypoints2t, ifEvaluateDetectors, overlaps );
+
+    correspondencesCount = -1;
+    repeatability = -1.f;
+    const int* size = overlaps.size();
+    if( !size || overlaps.nzcount() == 0 )
+        return;
+
+    if( ifEvaluateDetectors )
+    {
+        // threshold the overlaps
+        for( int y = 0; y < size[0]; y++ )
+        {
+            for( int x = 0; x < size[1]; x++ )
+            {
+                if ( overlaps(y,x) < overlapThreshold )
+                    overlaps.erase(y,x);
+            }
+        }
+
+        // regions one-to-one matching
+        correspondencesCount = 0;
+        while( overlaps.nzcount() > 0 )
+        {
+            double maxOverlap = 0;
+            int maxIdx[2];
+            minMaxLoc( overlaps, 0, &maxOverlap, 0, maxIdx );
+            for( size_t i1 = 0; i1 < keypoints1.size(); i1++ )
+                overlaps.erase(i1, maxIdx[1]);
+            for( size_t i2 = 0; i2 < keypoints2t.size(); i2++ )
+                overlaps.erase(maxIdx[0], i2);
+            correspondencesCount++;
+        }
+        repeatability = minCount ? (float)correspondencesCount/minCount : -1;
+    }
+    else
+    {
+        thresholdedOverlapMask->create( 2, size );
+        for( int y = 0; y < size[0]; y++ )
+        {
+            for( int x = 0; x < size[1]; x++ )
+            {
+                float val = overlaps(y,x);
+                if ( val >= overlapThreshold )
+                    thresholdedOverlapMask->ref(y,x) = 1;
+            }
+        }
+    }
+}
+
+void cv::evaluateFeatureDetector( const Mat& img1, const Mat& img2, const Mat& H1to2,
+                              vector<KeyPoint>* _keypoints1, vector<KeyPoint>* _keypoints2,
+                              float& repeatability, int& correspCount,
+                              const Ptr<FeatureDetector>& _fdetector )
+{
+    Ptr<FeatureDetector> fdetector(_fdetector);
+    vector<KeyPoint> *keypoints1, *keypoints2, buf1, buf2;
+    keypoints1 = _keypoints1 != 0 ? _keypoints1 : &buf1;
+    keypoints2 = _keypoints2 != 0 ? _keypoints2 : &buf2;
+
+    if( (keypoints1->empty() || keypoints2->empty()) && fdetector.empty() )
+        CV_Error( CV_StsBadArg, "fdetector must be no empty when keypoints1 or keypoints2 is empty" );
+
+    if( keypoints1->empty() )
+        fdetector->detect( img1, *keypoints1 );
+    if( keypoints2->empty() )
+        fdetector->detect( img1, *keypoints2 );
+
+    calculateRepeatability( img1, img2, H1to2, *keypoints1, *keypoints2, repeatability, correspCount );
+}
+
+struct DMatchForEvaluation : public DMatch
+{
+    uchar isCorrect;
+    DMatchForEvaluation( const DMatch &dm ) : DMatch( dm ) {}
+};
+
+static inline float recall( int correctMatchCount, int correspondenceCount )
+{
+    return correspondenceCount ? (float)correctMatchCount / (float)correspondenceCount : -1;
+}
+
+static inline float precision( int correctMatchCount, int falseMatchCount )
+{
+    return correctMatchCount + falseMatchCount ? (float)correctMatchCount / (float)(correctMatchCount + falseMatchCount) : -1;
+}
+
+void cv::computeRecallPrecisionCurve( const vector<vector<DMatch> >& matches1to2,
+                                      const vector<vector<uchar> >& correctMatches1to2Mask,
+                                      vector<Point2f>& recallPrecisionCurve )
+{
+    CV_Assert( matches1to2.size() == correctMatches1to2Mask.size() );
+
+    vector<DMatchForEvaluation> allMatches;
+    int correspondenceCount = 0;
+    for( size_t i = 0; i < matches1to2.size(); i++ )
+    {
+        for( size_t j = 0; j < matches1to2[i].size(); j++ )
+        {
+            DMatchForEvaluation match = matches1to2[i][j];
+            match.isCorrect = correctMatches1to2Mask[i][j] ;
+            allMatches.push_back( match );
+            correspondenceCount += match.isCorrect != 0 ? 1 : 0;
+        }
+    }
+
+    std::sort( allMatches.begin(), allMatches.end() );
+
+    int correctMatchCount = 0, falseMatchCount = 0;
+    recallPrecisionCurve.resize( allMatches.size() );
+    for( size_t i = 0; i < allMatches.size(); i++ )
+    {
+        if( allMatches[i].isCorrect )
+            correctMatchCount++;
+        else
+            falseMatchCount++;
+
+        float r = recall( correctMatchCount, correspondenceCount );
+        float p =  precision( correctMatchCount, falseMatchCount );
+        recallPrecisionCurve[i] = Point2f(1-p, r);
+    }
+}
+
+float cv::getRecall( const vector<Point2f>& recallPrecisionCurve, float l_precision )
+{
+    float recall = -1;
+
+    if( l_precision >= 0 && l_precision <= 1 )
+    {
+        int bestIdx = -1;
+        float minDiff = FLT_MAX;
+        for( size_t i = 0; i < recallPrecisionCurve.size(); i++ )
+        {
+            float curDiff = std::fabs(l_precision - recallPrecisionCurve[i].x);
+            if( curDiff <= minDiff )
+            {
+                bestIdx = i;
+                minDiff = curDiff;
+            }
+        }
+
+        recall = recallPrecisionCurve[bestIdx].y;
+    }
+
+    return recall;
+}
+
+void cv::evaluateDescriptorMatch( const Mat& img1, const Mat& img2, const Mat& H1to2,
+                                  vector<KeyPoint>& keypoints1, vector<KeyPoint>& keypoints2,
+                                  vector<vector<DMatch> >* _matches1to2, vector<vector<uchar> >* _correctMatches1to2Mask,
+                                  vector<Point2f>& recallPrecisionCurve,
+                                  const Ptr<GenericDescriptorMatch>& _dmatch )
+{
+    Ptr<GenericDescriptorMatch> dmatch = _dmatch;
+    dmatch->clear();
+
+    vector<vector<DMatch> > *matches1to2, buf1;
+    vector<vector<uchar> > *correctMatches1to2Mask, buf2;
+    matches1to2 = _matches1to2 != 0 ? _matches1to2 : &buf1;
+    correctMatches1to2Mask = _correctMatches1to2Mask != 0 ? _correctMatches1to2Mask : &buf2;
+
+    if( keypoints1.empty() || keypoints2.empty() )
+        CV_Error( CV_StsBadArg, "keypoints1 and keypoints2 must be no empty" );
+    if( matches1to2->empty() && dmatch.empty() )
+        CV_Error( CV_StsBadArg, "dmatch must be no empty when matches1to2 is empty" );
+    if( matches1to2->empty() )
+    {
+        dmatch->add( img2, keypoints2 );
+        //TODO: use more sophisticated strategy to choose threshold
+        dmatch->match( img1, keypoints1, *matches1to2, std::numeric_limits<float>::max() );
+    }
+    float repeatability;
+    int correspCount;
+    SparseMat_<uchar> thresholdedOverlapMask; // thresholded allOverlapErrors
+    calculateRepeatability( img1, img2, H1to2,
+                            keypoints1, keypoints2,
+                            repeatability, correspCount,
+                            &thresholdedOverlapMask );
+
+    correctMatches1to2Mask->resize(matches1to2->size());
+    int ddd = 0;
+    for( size_t i = 0; i < matches1to2->size(); i++ )
+    {
+        (*correctMatches1to2Mask)[i].resize((*matches1to2)[i].size());
+        for( size_t j = 0;j < (*matches1to2)[i].size(); j++ )
+        {
+            int indexQuery = (*matches1to2)[i][j].indexQuery;
+            int indexTrain = (*matches1to2)[i][j].indexTrain;
+            (*correctMatches1to2Mask)[i][j] = thresholdedOverlapMask( indexQuery, indexTrain );
+            ddd += thresholdedOverlapMask( indexQuery, indexTrain ) != 0 ? 1 : 0;
+        }
+    }
+
+    computeRecallPrecisionCurve( *matches1to2, *correctMatches1to2Mask, recallPrecisionCurve );
+}
index 42dfbe6..f00388d 100644 (file)
@@ -51,143 +51,9 @@ using namespace cv;
 /****************************************************************************************\
 *           Functions to evaluate affine covariant detectors and descriptors.            *
 \****************************************************************************************/
-inline Point2f applyHomography( const Mat_<double>& H, const Point2f& pt )
-{
-    double z = H(2,0)*pt.x + H(2,1)*pt.y + H(2,2);
-    if( z )
-    {
-        double w = 1./z;
-        return Point2f( (H(0,0)*pt.x + H(0,1)*pt.y + H(0,2))*w, (H(1,0)*pt.x + H(1,1)*pt.y + H(1,2))*w );
-    }
-    return Point2f( numeric_limits<double>::max(), numeric_limits<double>::max() );
-}
-
-inline void linearizeHomographyAt( const Mat_<double>& H, const Point2f& pt, Mat_<double>& A )
-{
-    A.create(2,2);
-    double p1 = H(0,0)*pt.x + H(0,1)*pt.y + H(0,2),
-           p2 = H(1,0)*pt.x + H(1,1)*pt.y + H(1,2),
-           p3 = H(2,0)*pt.x + H(2,1)*pt.y + H(2,2),
-           p3_2 = p3*p3;
-    if( p3 )
-    {
-        A(0,0) = H(0,0)/p3 - p1*H(2,0)/p3_2; // fxdx
-        A(0,1) = H(0,1)/p3 - p1*H(2,1)/p3_2; // fxdy
-
-        A(1,0) = H(1,0)/p3 - p2*H(2,0)/p3_2; // fydx
-        A(1,1) = H(1,1)/p3 - p2*H(2,1)/p3_2; // fydx
-    }
-    else
-        A.setTo(Scalar::all(numeric_limits<double>::max()));
-}
-
-class EllipticKeyPoint
-{
-public:
-    EllipticKeyPoint();
-    EllipticKeyPoint( const Point2f& _center, const Scalar& _ellipse );
-
-    static void convert( const vector<KeyPoint>& src, vector<EllipticKeyPoint>& dst );
-    static void convert( const vector<EllipticKeyPoint>& src, vector<KeyPoint>& dst );
 
-    static Mat_<double> getSecondMomentsMatrix( const Scalar& _ellipse );
-    Mat_<double> getSecondMomentsMatrix() const;
-
-    void calcProjection( const Mat_<double>& H, EllipticKeyPoint& projection ) const;
-
-    Point2f center;
-    Scalar ellipse; // 3 elements a, b, c: ax^2+2bxy+cy^2=1
-    Size_<float> axes; // half lenght of elipse axes
-    Size_<float> boundingBox; // half sizes of bounding box which sides are parallel to the coordinate axes
-};
-
-EllipticKeyPoint::EllipticKeyPoint()
-{
-    *this = EllipticKeyPoint(Point2f(0,0), Scalar(1, 0, 1) );
-}
-
-EllipticKeyPoint::EllipticKeyPoint( const Point2f& _center, const Scalar& _ellipse )
-{
-    center = _center;
-    ellipse = _ellipse;
-
-    Mat_<double> M = getSecondMomentsMatrix(_ellipse), eval;
-    eigen( M, eval );
-    assert( eval.rows == 2 && eval.cols == 1 );
-    axes.width = 1.f / sqrt(eval(0,0));
-    axes.height = 1.f / sqrt(eval(1,0));
-
-    float ac_b2 = ellipse[0]*ellipse[2] - ellipse[1]*ellipse[1];
-    boundingBox.width = sqrt(ellipse[2]/ac_b2);
-    boundingBox.height = sqrt(ellipse[0]/ac_b2);
-}
-
-Mat_<double> EllipticKeyPoint::getSecondMomentsMatrix( const Scalar& _ellipse )
-{
-    Mat_<double> M(2, 2);
-    M(0,0) = _ellipse[0];
-    M(1,0) = M(0,1) = _ellipse[1];
-    M(1,1) = _ellipse[2];
-    return M;
-}
-
-Mat_<double> EllipticKeyPoint::getSecondMomentsMatrix() const
-{
-    return getSecondMomentsMatrix(ellipse);
-}
-
-void EllipticKeyPoint::calcProjection( const Mat_<double>& H, EllipticKeyPoint& projection ) const
-{
-    Point2f dstCenter = applyHomography(H, center);
-
-    Mat_<double> invM; invert(getSecondMomentsMatrix(), invM);
-    Mat_<double> Aff; linearizeHomographyAt(H, center, Aff);
-    Mat_<double> dstM; invert(Aff*invM*Aff.t(), dstM);
-
-    projection = EllipticKeyPoint( dstCenter, Scalar(dstM(0,0), dstM(0,1), dstM(1,1)) );
-}
-
-void EllipticKeyPoint::convert( const vector<KeyPoint>& src, vector<EllipticKeyPoint>& dst )
-{
-    if( !src.empty() )
-    {
-        dst.resize(src.size());
-        for( size_t i = 0; i < src.size(); i++ )
-        {
-            float rad = src[i].size/2;
-            assert( rad );
-            float fac = 1.f/(rad*rad);
-            dst[i] = EllipticKeyPoint( src[i].pt, Scalar(fac, 0, fac) );
-        }
-    }
-}
-
-void EllipticKeyPoint::convert( const vector<EllipticKeyPoint>& src, vector<KeyPoint>& dst )
-{
-    if( !src.empty() )
-    {
-        dst.resize(src.size());
-        for( size_t i = 0; i < src.size(); i++ )
-        {
-            Size_<float> axes = src[i].axes;
-            float rad = sqrt(axes.height*axes.width);
-            dst[i] = KeyPoint(src[i].center, 2*rad );
-        }
-    }
-}
-
-void calcEllipticKeyPointProjections( const vector<EllipticKeyPoint>& src, const Mat_<double>& H, vector<EllipticKeyPoint>& dst )
-{
-    if( !src.empty() )
-    {
-        assert( !H.empty() && H.cols == 3 && H.rows == 3);
-        dst.resize(src.size());
-        vector<EllipticKeyPoint>::const_iterator srcIt = src.begin();
-        vector<EllipticKeyPoint>::iterator       dstIt = dst.begin();
-        for( ; srcIt != src.end(); ++srcIt, ++dstIt )
-            srcIt->calcProjection(H, *dstIt);
-    }
-}
+Point2f applyHomography( const Mat_<double>& H, const Point2f& pt );
+void linearizeHomographyAt( const Mat_<double>& H, const Point2f& pt, Mat_<double>& A );
 
 void calcKeyPointProjections( const vector<KeyPoint>& src, const Mat_<double>& H, vector<KeyPoint>& dst )
 {
@@ -202,7 +68,10 @@ void calcKeyPointProjections( const vector<KeyPoint>& src, const Mat_<double>& H
             Point2f dstPt = applyHomography(H, srcIt->pt);
 
             float srcSize2 = srcIt->size * srcIt->size;
-            Mat_<double> invM; invert(EllipticKeyPoint::getSecondMomentsMatrix( Scalar(1./srcSize2, 0., 1./srcSize2)), invM);
+            Mat_<double> M(2, 2);
+            M(0,0) = M(1,1) = 1./srcSize2;
+            M(1,0) = M(0,1) = 0;
+            Mat_<double> invM; invert(M, invM);
             Mat_<double> Aff; linearizeHomographyAt(H, srcIt->pt, Aff);
             Mat_<double> dstM; invert(Aff*invM*Aff.t(), dstM);
             Mat_<double> eval; eigen( dstM, eval );
@@ -236,261 +105,6 @@ void filterKeyPointsByImageSize( vector<KeyPoint>& keypoints, const Size& imgSiz
     }
 }
 
-/*
- * calulate ovelap errors
- */
-void overlap( const vector<EllipticKeyPoint>& keypoints1, const vector<EllipticKeyPoint>& keypoints2t, bool commonPart,
-              SparseMat_<float>& overlaps )
-{
-    overlaps.clear();
-    if( keypoints1.empty() || keypoints2t.empty() )
-        return;
-
-    int size[] = { keypoints1.size(), keypoints2t.size() };
-    overlaps.create( 2, size );
-
-    for( size_t i1 = 0; i1 < keypoints1.size(); i1++ )
-    {
-        EllipticKeyPoint kp1 = keypoints1[i1];
-        float maxDist = sqrt(kp1.axes.width*kp1.axes.height),
-              fac = 30.f/maxDist;
-        if( !commonPart )
-            fac=3;
-
-        maxDist = maxDist*4;
-        fac = 1.0/(fac*fac);
-
-        EllipticKeyPoint keypoint1a = EllipticKeyPoint( kp1.center, Scalar(fac*kp1.ellipse[0], fac*kp1.ellipse[1], fac*kp1.ellipse[2]) );
-
-        for( size_t i2 = 0; i2 < keypoints2t.size(); i2++ )
-        {
-            EllipticKeyPoint kp2 = keypoints2t[i2];
-            Point2f diff = kp2.center - kp1.center;
-
-            if( norm(diff) < maxDist )
-            {
-                EllipticKeyPoint keypoint2a = EllipticKeyPoint( kp2.center, Scalar(fac*kp2.ellipse[0], fac*kp2.ellipse[1], fac*kp2.ellipse[2]) );
-                //find the largest eigenvalue
-                float maxx =  ceil(( keypoint1a.boundingBox.width > (diff.x+keypoint2a.boundingBox.width)) ?
-                                     keypoint1a.boundingBox.width : (diff.x+keypoint2a.boundingBox.width));
-                float minx = floor((-keypoint1a.boundingBox.width < (diff.x-keypoint2a.boundingBox.width)) ?
-                                    -keypoint1a.boundingBox.width : (diff.x-keypoint2a.boundingBox.width));
-
-                float maxy =  ceil(( keypoint1a.boundingBox.height > (diff.y+keypoint2a.boundingBox.height)) ?
-                                     keypoint1a.boundingBox.height : (diff.y+keypoint2a.boundingBox.height));
-                float miny = floor((-keypoint1a.boundingBox.height < (diff.y-keypoint2a.boundingBox.height)) ?
-                                    -keypoint1a.boundingBox.height : (diff.y-keypoint2a.boundingBox.height));
-                float mina = (maxx-minx) < (maxy-miny) ? (maxx-minx) : (maxy-miny) ;
-                float dr = mina/50.0;
-                float bua = 0, bna = 0;
-                //compute the area
-                for( float rx1 = minx; rx1 <= maxx; rx1+=dr )
-                {
-                    float rx2 = rx1-diff.x;
-                    for( float ry1=miny; ry1<=maxy; ry1+=dr )
-                    {
-                        float ry2=ry1-diff.y;
-                        //compute the distance from the ellipse center
-                        float e1 = keypoint1a.ellipse[0]*rx1*rx1+2*keypoint1a.ellipse[1]*rx1*ry1+keypoint1a.ellipse[2]*ry1*ry1;
-                        float e2 = keypoint2a.ellipse[0]*rx2*rx2+2*keypoint2a.ellipse[1]*rx2*ry2+keypoint2a.ellipse[2]*ry2*ry2;
-                        //compute the area
-                        if( e1<1 && e2<1 ) bna++;
-                        if( e1<1 || e2<1 ) bua++;
-                    }
-                }
-                if( bna > 0)
-                    overlaps.ref(i1,i2) = 100.0*bna/bua;
-            }
-        }
-    }
-}
-
-void filterEllipticKeyPointsByImageSize( vector<EllipticKeyPoint>& keypoints, const Size& imgSize )
-{
-    if( !keypoints.empty() )
-    {
-        vector<EllipticKeyPoint> filtered;
-        filtered.reserve(keypoints.size());
-        vector<EllipticKeyPoint>::const_iterator it = keypoints.begin();
-        for( int i = 0; it != keypoints.end(); ++it, i++ )
-        {
-            if( it->center.x + it->boundingBox.width < imgSize.width &&
-                it->center.x - it->boundingBox.width > 0 &&
-                it->center.y + it->boundingBox.height < imgSize.height &&
-                it->center.y - it->boundingBox.height > 0 )
-                filtered.push_back(*it);
-        }
-        keypoints.assign(filtered.begin(), filtered.end());
-    }
-}
-
-void getEllipticKeyPointsInCommonPart( vector<EllipticKeyPoint>& keypoints1, vector<EllipticKeyPoint>& keypoints2,
-                              vector<EllipticKeyPoint>& keypoints1t, vector<EllipticKeyPoint>& keypoints2t,
-                              Size& imgSize1, const Size& imgSize2 )
-{
-    filterEllipticKeyPointsByImageSize( keypoints1, imgSize1 );
-    filterEllipticKeyPointsByImageSize( keypoints1t, imgSize2 );
-    filterEllipticKeyPointsByImageSize( keypoints2, imgSize2 );
-    filterEllipticKeyPointsByImageSize( keypoints2t, imgSize1 );
-}
-
-void calculateRepeatability( const vector<EllipticKeyPoint>& _keypoints1, const vector<EllipticKeyPoint>& _keypoints2,
-                             const Mat& img1, const Mat& img2, const Mat& H1to2,
-                             float& repeatability, int& correspondencesCount,
-                             SparseMat_<uchar>* thresholdedOverlapMask=0 )
-{
-    vector<EllipticKeyPoint> keypoints1( _keypoints1.begin(), _keypoints1.end() ),
-                             keypoints2( _keypoints2.begin(), _keypoints2.end() ),
-                             keypoints1t( keypoints1.size() ),
-                             keypoints2t( keypoints2.size() );
-
-    // calculate projections of key points
-    calcEllipticKeyPointProjections( keypoints1, H1to2, keypoints1t );
-    Mat H2to1; invert(H1to2, H2to1);
-    calcEllipticKeyPointProjections( keypoints2, H2to1, keypoints2t );
-
-    bool ifEvaluateDetectors = !thresholdedOverlapMask; // == commonPart
-    float overlapThreshold;
-    if( ifEvaluateDetectors )
-    {
-        overlapThreshold = 100.f - 40.f;
-
-        // remove key points from outside of the common image part
-        Size sz1 = img1.size(), sz2 = img2.size();
-        getEllipticKeyPointsInCommonPart( keypoints1, keypoints2, keypoints1t, keypoints2t, sz1, sz2 );
-    }
-    else
-    {
-        overlapThreshold = 100.f - 50.f;
-    }
-    int minCount = min( keypoints1.size(), keypoints2t.size() );
-
-    // calculate overlap errors
-    SparseMat_<float> overlaps;
-    overlap( keypoints1, keypoints2t, ifEvaluateDetectors, overlaps );
-
-    correspondencesCount = -1;
-    repeatability = -1.f;
-    const int* size = overlaps.size();
-    if( !size || overlaps.nzcount() == 0 )
-        return;
-
-    if( ifEvaluateDetectors )
-    {
-        // threshold the overlaps
-        for( int y = 0; y < size[0]; y++ )
-        {
-            for( int x = 0; x < size[1]; x++ )
-            {
-                if ( overlaps(y,x) < overlapThreshold )
-                    overlaps.erase(y,x);
-            }
-        }
-    
-        // regions one-to-one matching
-        correspondencesCount = 0;
-        while( overlaps.nzcount() > 0 )
-        {
-            double maxOverlap = 0;
-            int maxIdx[2];
-            minMaxLoc( overlaps, 0, &maxOverlap, 0, maxIdx );
-            for( size_t i1 = 0; i1 < keypoints1.size(); i1++ )
-                overlaps.erase(i1, maxIdx[1]);
-            for( size_t i2 = 0; i2 < keypoints2t.size(); i2++ )
-                overlaps.erase(maxIdx[0], i2);
-            correspondencesCount++;
-        }
-        repeatability = minCount ? (float)(correspondencesCount*100)/minCount : -1;
-    }
-    else
-    {
-        thresholdedOverlapMask->create( 2, size );
-        for( int y = 0; y < size[0]; y++ )
-        {
-            for( int x = 0; x < size[1]; x++ )
-            {
-                float val = overlaps(y,x);
-                if ( val >= overlapThreshold )
-                    thresholdedOverlapMask->ref(y,x) = val;
-            }
-        }
-    }
-}
-
-
-void evaluateDetectors( const vector<EllipticKeyPoint>& keypoints1, const vector<EllipticKeyPoint>& keypoints2,
-                        const Mat& img1, const Mat& img2, const Mat& H1to2,
-                        float& repeatability, int& correspCount )
-{
-    calculateRepeatability( keypoints1, keypoints2,
-                            img1, img2, H1to2,
-                            repeatability, correspCount );
-}
-
-inline float recall( int correctMatchCount, int correspondenceCount )
-{
-    return correspondenceCount ? (float)correctMatchCount / (float)correspondenceCount : -1;
-}
-
-inline float precision( int correctMatchCount, int falseMatchCount )
-{
-    return correctMatchCount + falseMatchCount ? (float)correctMatchCount / (float)(correctMatchCount + falseMatchCount) : -1;
-}
-
-
-struct DMatchForEvaluation : public DMatch
-{
-    int isCorrect;
-
-    DMatchForEvaluation( const DMatch &dm )
-    : DMatch( dm )
-    {
-    }
-};
-
-
-void evaluateDescriptors( const vector<EllipticKeyPoint>& keypoints1, const vector<EllipticKeyPoint>& keypoints2,
-                          const vector<vector<DMatch> >& matches1to2, vector<DMatchForEvaluation> &allMatches,
-                          const Mat& img1, const Mat& img2, const Mat& H1to2,
-                          int &correctMatchCount, int &falseMatchCount, int& correspondenceCount )
-{
-    assert( !keypoints1.empty() && !keypoints2.empty() && !matches1to2.empty() );
-    assert( keypoints1.size() == matches1to2.size() );
-
-    float repeatability;
-    int correspCount;
-    SparseMat_<uchar> thresholdedOverlapMask; // thresholded allOverlapErrors
-    calculateRepeatability( keypoints1, keypoints2,
-                            img1, img2, H1to2,
-                            repeatability, correspCount,
-                            &thresholdedOverlapMask );
-    correspondenceCount = thresholdedOverlapMask.nzcount();
-
-    correctMatchCount = 0;
-    falseMatchCount = 0;
-
-    for( size_t i = 0; i < matches1to2.size(); i++ )
-    {
-        for( size_t j = 0;j < matches1to2[i].size(); j++ )
-        {
-        //if( matches1to2[i].match.indexTrain > 0 )
-        //{
-            DMatchForEvaluation match = matches1to2[i][j];
-            match.isCorrect = thresholdedOverlapMask( match.indexQuery, match.indexTrain);
-            if( match.isCorrect )
-                correctMatchCount++;
-            else
-                falseMatchCount++;
-            allMatches.push_back( match );
-        //}
-        //else
-        //{
-        //    matches1to2[i].isCorrect = -1;
-        //}
-        }
-    }
-}
-
 /****************************************************************************************\
 *                                  Detectors evaluation                                 *
 \****************************************************************************************/
@@ -1063,22 +677,18 @@ void DetectorQualityTest::runDatasetTest (const vector<Mat> &imgs, const vector<
 
     calcQuality[di].resize(TEST_CASE_COUNT);
 
-    vector<KeyPoint> keypoints1; vector<EllipticKeyPoint> ekeypoints1;
-
+    vector<KeyPoint> keypoints1;
     detector->detect( imgs[0], keypoints1 );
     writeKeypoints( keypontsFS, keypoints1, 0);
-    EllipticKeyPoint::convert( keypoints1, ekeypoints1 );
     int progressCount = DATASETS_COUNT*TEST_CASE_COUNT;
     for( int ci = 0; ci < TEST_CASE_COUNT; ci++ )
     {
         progress = update_progress( progress, di*TEST_CASE_COUNT + ci, progressCount, 0 );
         vector<KeyPoint> keypoints2;
-        detector->detect( imgs[ci+1], keypoints2 );
+        evaluateFeatureDetector( imgs[0], imgs[ci+1], Hs[ci], &keypoints1, &keypoints2,
+                                 calcQuality[di][ci].repeatability, calcQuality[di][ci].correspondenceCount,
+                                 detector );
         writeKeypoints( keypontsFS, keypoints2, ci+1);
-        vector<EllipticKeyPoint> ekeypoints2;
-        EllipticKeyPoint::convert( keypoints2, ekeypoints2 );
-        evaluateDetectors( ekeypoints1, ekeypoints2, imgs[0], imgs[ci], Hs[ci],
-                           calcQuality[di][ci].repeatability, calcQuality[di][ci].correspondenceCount );
     }
 }
 
@@ -1185,7 +795,7 @@ protected:
     virtual int processResults( int datasetIdx, int caseIdx );
 
     virtual void writePlotData( int di ) const;
-    void calculatePlotData( vector<DMatchForEvaluation> &allMatches, int allCorrespCount, int di );
+    void calculatePlotData( vector<vector<DMatch> > &allMatches, vector<vector<uchar> > &allCorrectMatchesMask, int di );
 
     struct Quality
     {
@@ -1333,8 +943,8 @@ void DescriptorQualityTest::readAlgorithm( )
 
     if( defaultDescMatch == 0 )
     {
-        DescriptorExtractor *extractor = createDescriptorExtractor( algName );
-        DescriptorMatcher *matcher = createDescriptorMatcher( matcherName );
+        Ptr<DescriptorExtractor> extractor = createDescriptorExtractor( algName );
+        Ptr<DescriptorMatcher> matcher = createDescriptorMatcher( matcherName );
         defaultDescMatch = new VectorDescriptorMatch( extractor, matcher );
         specificDescMatch = new VectorDescriptorMatch( extractor, matcher );
 
@@ -1346,10 +956,15 @@ void DescriptorQualityTest::readAlgorithm( )
     }
 }
 
-void DescriptorQualityTest::calculatePlotData( vector<DMatchForEvaluation> &allMatches, int allCorrespCount, int di )
+void DescriptorQualityTest::calculatePlotData( vector<vector<DMatch> > &allMatches, vector<vector<uchar> > &allCorrectMatchesMask, int di )
 {
-    std::sort( allMatches.begin(), allMatches.end() );
+    vector<Point2f> recallPrecisionCurve;
+    computeRecallPrecisionCurve( allMatches, allCorrectMatchesMask, recallPrecisionCurve );
+    // you have recallPrecisionCurve for all images from dataset
+    // size of recallPrecisionCurve == total matches count
+#if 0
 
+    std::sort( allMatches.begin(), allMatches.end() );
     //calcDatasetQuality[di].resize( allMatches.size() );
     calcDatasetQuality[di].clear();
     int correctMatchCount = 0, falseMatchCount = 0;
@@ -1358,6 +973,7 @@ void DescriptorQualityTest::calculatePlotData( vector<DMatchForEvaluation> &allM
     int step = 1 + allMatches.size() / npoints;
     const float resultPrecision = 0.5;
     bool isResultCalculated = false;
+
     for( size_t i=0;i<allMatches.size();i++)
     {
         if( allMatches[i].isCorrect )
@@ -1373,7 +989,7 @@ void DescriptorQualityTest::calculatePlotData( vector<DMatchForEvaluation> &allM
 
             calcDatasetQuality[di].push_back( quality );
 
-            if( !isResultCalculated && quality.precision < resultPrecision)
+            if( !isResultCalculated && quality.precision < resultPrecision )
             {
                 for(int ci=0;ci<TEST_CASE_COUNT;ci++)
                 {
@@ -1390,6 +1006,7 @@ void DescriptorQualityTest::calculatePlotData( vector<DMatchForEvaluation> &allM
     quality.precision = precision( correctMatchCount, falseMatchCount );
 
     calcDatasetQuality[di].push_back( quality );
+#endif
 
 }
 
@@ -1407,20 +1024,18 @@ void DescriptorQualityTest::runDatasetTest (const vector<Mat> &imgs, const vecto
     Ptr<GenericDescriptorMatch> descMatch = commRunParams[di].isActiveParams ? specificDescMatch : defaultDescMatch;
     calcQuality[di].resize(TEST_CASE_COUNT);
 
-    vector<KeyPoint> keypoints1; vector<EllipticKeyPoint> ekeypoints1;
+    vector<KeyPoint> keypoints1;
     readKeypoints( keypontsFS, keypoints1, 0);
-    EllipticKeyPoint::convert( keypoints1, ekeypoints1 );
 
     int progressCount = DATASETS_COUNT*TEST_CASE_COUNT;
-    vector<DMatchForEvaluation> allMatches;
 
-    int allCorrespCount = 0;
+    vector<vector<DMatch> > allMatches1to2;
+    vector<vector<uchar> > allCorrectMatchesMask;
     for( int ci = 0; ci < TEST_CASE_COUNT; ci++ )
     {
         progress = update_progress( progress, di*TEST_CASE_COUNT + ci, progressCount, 0 );
 
         vector<KeyPoint> keypoints2;
-        vector<EllipticKeyPoint> ekeypoints2;
         if( commRunParams[di].projectKeypointsFrom1Image )
         {
             // TODO need to test function calcKeyPointProjections
@@ -1429,24 +1044,20 @@ void DescriptorQualityTest::runDatasetTest (const vector<Mat> &imgs, const vecto
         }
         else
             readKeypoints( keypontsFS, keypoints2, ci+1 );
-        EllipticKeyPoint::convert( keypoints2, ekeypoints2 );
-        descMatch->add( imgs[ci+1], keypoints2 );
-        vector<vector<DMatch> > matches1to2;
-        //TODO: use more sophisticated strategy to choose threshold
-        descMatch->match( imgs[0], keypoints1, matches1to2, std::numeric_limits<float>::max() );
-
         // TODO if( commRunParams[di].matchFilter )
-        int correspCount;
-        int correctMatchCount = 0, falseMatchCount = 0;
-        evaluateDescriptors( ekeypoints1, ekeypoints2, matches1to2, allMatches, imgs[0], imgs[ci+1], Hs[ci],
-                             correctMatchCount, falseMatchCount, correspCount );
 
-        allCorrespCount += correspCount;
-
-        descMatch->clear ();
+        vector<vector<DMatch> > matches1to2;
+        vector<vector<uchar> > correctMatchesMask;
+        vector<Point2f> recallPrecisionCurve; // not used because we need recallPrecisionCurve for
+                                              // all images in dataset
+        evaluateDescriptorMatch( imgs[0], imgs[ci+1], Hs[ci], keypoints1, keypoints2,
+                                 &matches1to2, &correctMatchesMask, recallPrecisionCurve,
+                                 descMatch );
+        allMatches1to2.insert( allMatches1to2.end(), matches1to2.begin(), matches1to2.end() );
+        allCorrectMatchesMask.insert( allCorrectMatchesMask.end(), correctMatchesMask.begin(), correctMatchesMask.end() );
     }
 
-    calculatePlotData( allMatches, allCorrespCount, di );
+    calculatePlotData( allMatches1to2, allCorrectMatchesMask, di );
 }
 
 int DescriptorQualityTest::processResults( int datasetIdx, int caseIdx )