From 276f3b88625590fd9e9ed319e189dd1d1ccddbce Mon Sep 17 00:00:00 2001 From: Ilya Lysenkov Date: Wed, 16 Jun 2010 08:56:53 +0000 Subject: [PATCH] Added distance threshold-based matching --- .../include/opencv2/features2d/features2d.hpp | 107 +++++++++++++++++- modules/features2d/src/descriptors.cpp | 52 +++++++-- .../cv/src/adetectordescriptor_evaluation.cpp | 15 ++- 3 files changed, 154 insertions(+), 20 deletions(-) diff --git a/modules/features2d/include/opencv2/features2d/features2d.hpp b/modules/features2d/include/opencv2/features2d/features2d.hpp index 175613c3d4..a9ad43c920 100644 --- a/modules/features2d/include/opencv2/features2d/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d/features2d.hpp @@ -1573,9 +1573,9 @@ public: * Find the best match for each descriptor from a query set * * query The query set of descriptors - * matchings Matchings of the closest matches from the training set + * matches DMatches of the closest matches from the training set */ - void match( const Mat& query, vector& matchings ) const; + void match( const Mat& query, vector& matches ) const; /* * Find the best matches between two descriptor sets, with constraints @@ -1586,10 +1586,36 @@ public: * * query The query set of descriptors * mask Mask specifying permissible matches. - * matchings Matchings of the closest matches from the training set + * matches DMatches of the closest matches from the training set */ void match( const Mat& query, const Mat& mask, - vector& matchings ) const; + vector& matches ) const; + + /* + * Find many matches for each descriptor from a query set + * + * query The query set of descriptors + * matches DMatches of the closest matches from the training set + * threshold Distance threshold for descriptors matching + */ + void match( const Mat& query, vector >& matches, float threshold ) const; + + /* + * Find many matches for each descriptor from a query set, with constraints + * on which pairs of descriptors can be matched. + * + * The mask describes which descriptors can be matched. descriptors_1[i] + * can be matched with descriptors_2[j] only if mask.at(i,j) is non-zero. + * + * query The query set of descriptors + * mask Mask specifying permissible matches. + * matches DMatches of the closest matches from the training set + * threshold Distance threshold for descriptors matching + */ + void match( const Mat& query, const Mat& mask, + vector >& matches, float threshold ) const; + + /* * Find the best keypoint matches for small view changes. @@ -1627,6 +1653,10 @@ protected: virtual void matchImpl( const Mat& descriptors_1, const Mat& descriptors_2, const Mat& mask, vector& matches ) const = 0; + virtual void matchImpl( const Mat& descriptors_1, const Mat& descriptors_2, + const Mat& mask, vector >& matches, float threshold ) const = 0; + + static bool possibleMatch( const Mat& mask, int index_1, int index_2 ) { return mask.empty() || mask.at(index_1, index_2); @@ -1674,6 +1704,18 @@ inline void DescriptorMatcher::match( const Mat& query, const Mat& mask, matchImpl( query, train, mask, matches ); } +inline void DescriptorMatcher::match( const Mat& query, vector >& matches, float threshold ) const +{ + matchImpl( query, train, Mat(), matches, threshold ); +} + +inline void DescriptorMatcher::match( const Mat& query, const Mat& mask, + vector >& matches, float threshold ) const +{ + matchImpl( query, train, mask, matches, threshold ); +} + + inline void DescriptorMatcher::clear() { train.release(); @@ -1701,6 +1743,9 @@ protected: virtual void matchImpl( const Mat& descriptors_1, const Mat& descriptors_2, const Mat& mask, vector& matches ) const; + virtual void matchImpl( const Mat& descriptors_1, const Mat& descriptors_2, + const Mat& mask, vector >& matches, float threshold ) const; + Distance distance; }; @@ -1764,6 +1809,46 @@ void BruteForceMatcher::matchImpl( const Mat& descriptors_1, const Mat } } +template +void BruteForceMatcher::matchImpl( const Mat& descriptors_1, const Mat& descriptors_2, + const Mat& mask, vector >& matches, float threshold ) const +{ + typedef typename Distance::ValueType ValueType; + typedef typename Distance::ResultType DistanceType; + + assert( mask.empty() || (mask.rows == descriptors_1.rows && mask.cols == descriptors_2.rows) ); + + assert( descriptors_1.cols == descriptors_2.cols || descriptors_1.empty() || descriptors_2.empty() ); + assert( DataType::type == descriptors_1.type() || descriptors_1.empty() ); + assert( DataType::type == descriptors_2.type() || descriptors_2.empty() ); + + int dimension = descriptors_1.cols; + matches.resize( descriptors_1.rows ); + + for( int i = 0; i < descriptors_1.rows; i++ ) + { + const ValueType* d1 = descriptors_1.ptr(i); + + for( int j = 0; j < descriptors_2.rows; j++ ) + { + if( possibleMatch(mask, i, j) ) + { + const ValueType* d2 = descriptors_2.ptr(j); + DistanceType curDistance = distance(d1, d2, dimension); + if( curDistance < threshold ) + { + DMatch match; + match.distance = curDistance; + match.indexQuery = i; + match.indexTrain = j; + matches[i].push_back( match ); + } + } + } + } +} + + DescriptorMatcher* createDescriptorMatcher( const string& descriptorMatcherType ); /****************************************************************************************\ @@ -1835,6 +1920,8 @@ public: // matches A vector to be filled with keypoint matches virtual void match( const Mat& image, vector& points, vector& matches ) {}; + virtual void match( const Mat& image, vector& points, vector >& matches, float threshold ) {}; + // Clears keypoints storing in collection virtual void clear(); @@ -2039,7 +2126,9 @@ public: virtual void match( const Mat& image, vector& keypoints, vector& indices ); - virtual void match( const Mat& image, vector& points, vector& matches ); + virtual void match( const Mat& image, vector& points, vector& matches); + + virtual void match( const Mat& image, vector& points, vector >& matches, float threshold); virtual void classify( const Mat& image, vector& keypoints ); @@ -2105,6 +2194,14 @@ public: matcher.match( descriptors, matches ); } + virtual void match( const Mat& image, vector& points, vector >& matches, float threshold ) + { + Mat descriptors; + extractor.compute( image, points, descriptors ); + + matcher.match( descriptors, matches, threshold ); + } + virtual void clear() { GenericDescriptorMatch::clear(); diff --git a/modules/features2d/src/descriptors.cpp b/modules/features2d/src/descriptors.cpp index 4f3f66ceec..e66af9c75b 100644 --- a/modules/features2d/src/descriptors.cpp +++ b/modules/features2d/src/descriptors.cpp @@ -41,6 +41,8 @@ #include "precomp.hpp" +//#define _KDTREE + using namespace std; namespace cv { @@ -439,21 +441,22 @@ void OneWayDescriptorMatch::match( const Mat& image, vector& points, v match( image, points, matchings ); for( size_t i = 0; i < points.size(); i++ ) - indices[i] = matchings[i].index; + indices[i] = matchings[i].indexTrain; } -void OneWayDescriptorMatch::match( const Mat& image, vector& points, vector& matchings ) +void OneWayDescriptorMatch::match( const Mat& image, vector& points, vector& matches ) { - matchings.resize( points.size() ); + matches.resize( points.size() ); IplImage _image = image; for( size_t i = 0; i < points.size(); i++ ) { int poseIdx = -1; - DMatch matching; - matching.index = -1; - base->FindDescriptor( &_image, points[i].pt, matching.index, poseIdx, matching.distance ); - matchings[i] = matching; + DMatch match; + match.indexQuery = i; + match.indexTrain = -1; + base->FindDescriptor( &_image, points[i].pt, match.indexTrain, poseIdx, match.distance ); + matches[i] = match; } } @@ -744,18 +747,45 @@ void FernDescriptorMatch::match( const Mat& image, vector& keypoints, } } -void FernDescriptorMatch::match( const Mat& image, vector& keypoints, vector& matchings ) +void FernDescriptorMatch::match( const Mat& image, vector& keypoints, vector& matches ) { trainFernClassifier(); - matchings.resize( keypoints.size() ); + matches.resize( keypoints.size() ); vector signature( (size_t)classifier->getClassCount() ); for( size_t pi = 0; pi < keypoints.size(); pi++ ) { - calcBestProbAndMatchIdx( image, keypoints[pi].pt, matchings[pi].distance, matchings[pi].index, signature ); + matches[pi].indexQuery = pi; + calcBestProbAndMatchIdx( image, keypoints[pi].pt, matches[pi].distance, matches[pi].indexTrain, signature ); //matching[pi].distance is log of probability so we need to transform it - matchings[pi].distance = -matchings[pi].distance; + matches[pi].distance = -matches[pi].distance; + } +} + +void FernDescriptorMatch::match( const Mat& image, vector& keypoints, vector >& matches, float threshold ) +{ + trainFernClassifier(); + + matches.resize( keypoints.size() ); + vector signature( (size_t)classifier->getClassCount() ); + + for( size_t pi = 0; pi < keypoints.size(); pi++ ) + { + (*classifier)( image, keypoints[pi].pt, signature); + + DMatch match; + match.indexQuery = pi; + + for( size_t ci = 0; ci < (size_t)classifier->getClassCount(); ci++ ) + { + if( -signature[ci] < threshold ) + { + match.distance = -signature[ci]; + match.indexTrain = ci; + matches[pi].push_back( match ); + } + } } } diff --git a/tests/cv/src/adetectordescriptor_evaluation.cpp b/tests/cv/src/adetectordescriptor_evaluation.cpp index 7a24ec69b8..1e359134fa 100644 --- a/tests/cv/src/adetectordescriptor_evaluation.cpp +++ b/tests/cv/src/adetectordescriptor_evaluation.cpp @@ -1447,12 +1447,19 @@ void DescriptorQualityTest::runDatasetTest (const vector &imgs, const vecto readKeypoints( keypontsFS, keypoints2, ci+1 ); transformToEllipticKeyPoints( keypoints2, ekeypoints2 ); descMatch->add( imgs[ci+1], keypoints2 ); - vector matches1to2; - descMatch->match( imgs[0], keypoints1, matches1to2 ); - vector matches ( matches1to2.size() ); + vector > matches1to2; + descMatch->match( imgs[0], keypoints1, matches1to2, std::numeric_limits::max() ); + vector matches; for( size_t i=0;i