diff --git a/modules/features2d/doc/common_interfaces_of_descriptor_matchers.rst b/modules/features2d/doc/common_interfaces_of_descriptor_matchers.rst index bc5de994b6..8ec9c4bc18 100644 --- a/modules/features2d/doc/common_interfaces_of_descriptor_matchers.rst +++ b/modules/features2d/doc/common_interfaces_of_descriptor_matchers.rst @@ -300,6 +300,20 @@ For efficiency, ``BruteForceMatcher`` is used as a template parameterized with t ResultType operator()( const T* a, const T* b, int size ) const; }; + + /* + * Squared Euclidean distance functor + */ + template + struct SL2 + { + typedef T ValueType; + typedef typename Accumulator::Type ResultType; + + ResultType operator()( const T* a, const T* b, int size ) const; + }; + // Note: in case of SL2 distance a parameter maxDistance in the method DescriptorMatcher::radiusMatch + // is a squared maximum distance in L2. /* * Manhattan distance (city block distance) functor @@ -311,7 +325,6 @@ For efficiency, ``BruteForceMatcher`` is used as a template parameterized with t typedef typename Accumulator::Type ResultType; ResultType operator()( const T* a, const T* b, int size ) const; - ... }; /* @@ -334,7 +347,6 @@ For efficiency, ``BruteForceMatcher`` is used as a template parameterized with t ResultType operator()( const unsigned char* a, const unsigned char* b, int size ) const; - ... }; diff --git a/modules/features2d/include/opencv2/features2d/features2d.hpp b/modules/features2d/include/opencv2/features2d/features2d.hpp index 232f3b53af..8f116bd7d2 100644 --- a/modules/features2d/include/opencv2/features2d/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d/features2d.hpp @@ -2083,6 +2083,27 @@ template<> struct Accumulator { typedef float Type; }; template<> struct Accumulator { typedef float Type; }; template<> struct Accumulator { typedef float Type; }; +/* + * Squeared Euclidean distance functor + */ +template +struct CV_EXPORTS SL2 +{ + typedef T ValueType; + typedef typename Accumulator::Type ResultType; + + ResultType operator()( const T* a, const T* b, int size ) const + { + ResultType result = ResultType(); + for( int i = 0; i < size; i++ ) + { + ResultType diff = (ResultType)(a[i] - b[i]); + result += diff*diff; + } + return result; + } +}; + /* * Euclidean distance functor */ @@ -2395,77 +2416,77 @@ template inline void BruteForceMatcher::commonKnnMatchImpl( BruteForceMatcher& matcher, const Mat& queryDescriptors, vector >& matches, int knn, const vector& masks, bool compactResult ) - { - typedef typename Distance::ValueType ValueType; - typedef typename Distance::ResultType DistanceType; - CV_DbgAssert( !queryDescriptors.empty() ); - CV_Assert( DataType::type == queryDescriptors.type() ); +{ + typedef typename Distance::ValueType ValueType; + typedef typename Distance::ResultType DistanceType; + CV_DbgAssert( !queryDescriptors.empty() ); + CV_Assert( DataType::type == queryDescriptors.type() ); - int dimension = queryDescriptors.cols; - matches.reserve(queryDescriptors.rows); + int dimension = queryDescriptors.cols; + matches.reserve(queryDescriptors.rows); - size_t imgCount = matcher.trainDescCollection.size(); - vector allDists( imgCount ); // distances between one query descriptor and all train descriptors - for( size_t i = 0; i < imgCount; i++ ) + size_t imgCount = matcher.trainDescCollection.size(); + vector allDists( imgCount ); // distances between one query descriptor and all train descriptors + for( size_t i = 0; i < imgCount; i++ ) allDists[i] = Mat( 1, matcher.trainDescCollection[i].rows, DataType::type ); - for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ ) - { - if( matcher.isMaskedOut( masks, qIdx ) ) - { - if( !compactResult ) // push empty vector - matches.push_back( vector() ); - } - else - { - // 1. compute distances between i-th query descriptor and all train descriptors - for( size_t iIdx = 0; iIdx < imgCount; iIdx++ ) - { - CV_Assert( DataType::type == matcher.trainDescCollection[iIdx].type() || matcher.trainDescCollection[iIdx].empty() ); - CV_Assert( queryDescriptors.cols == matcher.trainDescCollection[iIdx].cols || - matcher.trainDescCollection[iIdx].empty() ); + for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ ) + { + if( matcher.isMaskedOut( masks, qIdx ) ) + { + if( !compactResult ) // push empty vector + matches.push_back( vector() ); + } + else + { + // 1. compute distances between i-th query descriptor and all train descriptors + for( size_t iIdx = 0; iIdx < imgCount; iIdx++ ) + { + CV_Assert( DataType::type == matcher.trainDescCollection[iIdx].type() || matcher.trainDescCollection[iIdx].empty() ); + CV_Assert( queryDescriptors.cols == matcher.trainDescCollection[iIdx].cols || + matcher.trainDescCollection[iIdx].empty() ); - const ValueType* d1 = (const ValueType*)(queryDescriptors.data + queryDescriptors.step*qIdx); - allDists[iIdx].setTo( Scalar::all(std::numeric_limits::max()) ); - for( int tIdx = 0; tIdx < matcher.trainDescCollection[iIdx].rows; tIdx++ ) - { - if( masks.empty() || matcher.isPossibleMatch(masks[iIdx], qIdx, tIdx) ) - { - const ValueType* d2 = (const ValueType*)(matcher.trainDescCollection[iIdx].data + - matcher.trainDescCollection[iIdx].step*tIdx); - allDists[iIdx].at(0, tIdx) = matcher.distance(d1, d2, dimension); - } - } - } + const ValueType* d1 = (const ValueType*)(queryDescriptors.data + queryDescriptors.step*qIdx); + allDists[iIdx].setTo( Scalar::all(std::numeric_limits::max()) ); + for( int tIdx = 0; tIdx < matcher.trainDescCollection[iIdx].rows; tIdx++ ) + { + if( masks.empty() || matcher.isPossibleMatch(masks[iIdx], qIdx, tIdx) ) + { + const ValueType* d2 = (const ValueType*)(matcher.trainDescCollection[iIdx].data + + matcher.trainDescCollection[iIdx].step*tIdx); + allDists[iIdx].at(0, tIdx) = matcher.distance(d1, d2, dimension); + } + } + } - // 2. choose k nearest matches for query[i] - matches.push_back( vector() ); - vector >::reverse_iterator curMatches = matches.rbegin(); - for( int k = 0; k < knn; k++ ) - { - DMatch bestMatch; - bestMatch.distance = std::numeric_limits::max(); - for( size_t iIdx = 0; iIdx < imgCount; iIdx++ ) - { - if( !allDists[iIdx].empty() ) - { - double minVal; - Point minLoc; - minMaxLoc( allDists[iIdx], &minVal, 0, &minLoc, 0 ); - if( minVal < bestMatch.distance ) - bestMatch = DMatch( qIdx, minLoc.x, (int)iIdx, (float)minVal ); - } - } - if( bestMatch.trainIdx == -1 ) - break; + // 2. choose k nearest matches for query[i] + matches.push_back( vector() ); + vector >::reverse_iterator curMatches = matches.rbegin(); + for( int k = 0; k < knn; k++ ) + { + DMatch bestMatch; + bestMatch.distance = std::numeric_limits::max(); + for( size_t iIdx = 0; iIdx < imgCount; iIdx++ ) + { + if( !allDists[iIdx].empty() ) + { + double minVal; + Point minLoc; + minMaxLoc( allDists[iIdx], &minVal, 0, &minLoc, 0 ); + if( minVal < bestMatch.distance ) + bestMatch = DMatch( qIdx, minLoc.x, (int)iIdx, (float)minVal ); + } + } + if( bestMatch.trainIdx == -1 ) + break; - allDists[bestMatch.imgIdx].at(0, bestMatch.trainIdx) = std::numeric_limits::max(); - curMatches->push_back( bestMatch ); - } - //TODO should already be sorted at this point? - std::sort( curMatches->begin(), curMatches->end() ); - } - } + allDists[bestMatch.imgIdx].at(0, bestMatch.trainIdx) = std::numeric_limits::max(); + curMatches->push_back( bestMatch ); + } + //TODO should already be sorted at this point? + std::sort( curMatches->begin(), curMatches->end() ); + } + } } template diff --git a/modules/features2d/src/matchers.cpp b/modules/features2d/src/matchers.cpp index 37371eed06..795cc1afeb 100755 --- a/modules/features2d/src/matchers.cpp +++ b/modules/features2d/src/matchers.cpp @@ -328,6 +328,10 @@ Ptr DescriptorMatcher::create( const string& descriptorMatche { dm = new BruteForceMatcher >(); } + else if( !descriptorMatcherType.compare( "BruteForce-SL2" ) ) // Squared L2 + { + dm = new BruteForceMatcher >(); + } else if( !descriptorMatcherType.compare( "BruteForce-L1" ) ) { dm = new BruteForceMatcher >(); @@ -345,10 +349,10 @@ Ptr DescriptorMatcher::create( const string& descriptorMatche } /* - * BruteForce L2 specialization + * BruteForce SL2 and L2 specialization */ template<> -void BruteForceMatcher >::knnMatchImpl( const Mat& queryDescriptors, vector >& matches, int knn, +void BruteForceMatcher >::knnMatchImpl( const Mat& queryDescriptors, vector >& matches, int knn, const vector& masks, bool compactResult ) { #ifndef HAVE_EIGEN @@ -427,7 +431,7 @@ void BruteForceMatcher >::knnMatchImpl( const Mat& queryDescriptors, v break; e_allDists[bestImgIdx](bestTrainIdx) = -std::numeric_limits::max(); - curMatches->push_back( DMatch(qIdx, bestTrainIdx, bestImgIdx, sqrt((-2)*totalMaxCoeff + queryNorm2)) ); + curMatches->push_back( DMatch(qIdx, bestTrainIdx, bestImgIdx, (-2)*totalMaxCoeff + queryNorm2) ); } std::sort( curMatches->begin(), curMatches->end() ); } @@ -436,7 +440,7 @@ void BruteForceMatcher >::knnMatchImpl( const Mat& queryDescriptors, v } template<> -void BruteForceMatcher >::radiusMatchImpl( const Mat& queryDescriptors, vector >& matches, float maxDistance, +void BruteForceMatcher >::radiusMatchImpl( const Mat& queryDescriptors, vector >& matches, float maxDistance, const vector& masks, bool compactResult ) { #ifndef HAVE_EIGEN @@ -492,7 +496,7 @@ void BruteForceMatcher >::radiusMatchImpl( const Mat& queryDescriptors { if( masks.empty() || isPossibleMatch(masks[iIdx], qIdx, tIdx) ) { - float d = sqrt((-2)*e_allDists[iIdx](tIdx) + queryNorm2); + float d = (-2)*e_allDists[iIdx](tIdx) + queryNorm2; if( d < maxDistance ) curMatches->push_back( DMatch( qIdx, tIdx, iIdx, d ) ); } @@ -504,6 +508,40 @@ void BruteForceMatcher >::radiusMatchImpl( const Mat& queryDescriptors #endif } +inline void sqrtDistance( vector >& matches ) +{ + for( size_t imgIdx = 0; imgIdx < matches.size(); imgIdx++ ) + { + for( size_t matchIdx = 0; matchIdx < matches[imgIdx].size(); matchIdx++ ) + { + matches[imgIdx][matchIdx].distance = std::sqrt( matches[imgIdx][matchIdx].distance ); + } + } +} + +template<> +void BruteForceMatcher >::knnMatchImpl( const Mat& queryDescriptors, vector >& matches, int knn, + const vector& masks, bool compactResult ) +{ + BruteForceMatcher > matcherSL2; + matcherSL2.add( getTrainDescriptors() ); + matcherSL2.knnMatch( queryDescriptors, matches, knn, masks, compactResult ); + + sqrtDistance( matches ); +} + +template<> +void BruteForceMatcher >::radiusMatchImpl( const Mat& queryDescriptors, vector >& matches, float maxDistance, + const vector& masks, bool compactResult ) +{ + const float maxDistance2 = maxDistance * maxDistance; + BruteForceMatcher > matcherSL2; + matcherSL2.add( getTrainDescriptors() ); + matcherSL2.radiusMatch( queryDescriptors, matches, maxDistance2, masks, compactResult ); + + sqrtDistance( matches ); +} + /* * Flann based matcher */