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 *
\****************************************************************************************/
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 *
\****************************************************************************************/
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() {}
//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),
// 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.
};
};
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 */
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
-
}
--- /dev/null
+//*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 );
+}
/****************************************************************************************\
* 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 )
{
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 );
}
}
-/*
- * 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 *
\****************************************************************************************/
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 );
}
}
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
{
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 );
}
}
-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;
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 )
calcDatasetQuality[di].push_back( quality );
- if( !isResultCalculated && quality.precision < resultPrecision)
+ if( !isResultCalculated && quality.precision < resultPrecision )
{
for(int ci=0;ci<TEST_CASE_COUNT;ci++)
{
quality.precision = precision( correctMatchCount, falseMatchCount );
calcDatasetQuality[di].push_back( quality );
+#endif
}
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
}
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 )