added SL2 (squared L2 distance) and implemented the descriptors matching in L2 using SL2
This commit is contained in:
parent
a9fdc1bdff
commit
fcd999ae6e
@ -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;
|
ResultType operator()( const T* a, const T* b, int size ) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Squared Euclidean distance functor
|
||||||
|
*/
|
||||||
|
template<class T>
|
||||||
|
struct SL2
|
||||||
|
{
|
||||||
|
typedef T ValueType;
|
||||||
|
typedef typename Accumulator<T>::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
|
* Manhattan distance (city block distance) functor
|
||||||
@ -311,7 +325,6 @@ For efficiency, ``BruteForceMatcher`` is used as a template parameterized with t
|
|||||||
typedef typename Accumulator<T>::Type ResultType;
|
typedef typename Accumulator<T>::Type ResultType;
|
||||||
|
|
||||||
ResultType operator()( const T* a, const T* b, int size ) const;
|
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,
|
ResultType operator()( const unsigned char* a, const unsigned char* b,
|
||||||
int size ) const;
|
int size ) const;
|
||||||
...
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -2083,6 +2083,27 @@ template<> struct Accumulator<unsigned short> { typedef float Type; };
|
|||||||
template<> struct Accumulator<char> { typedef float Type; };
|
template<> struct Accumulator<char> { typedef float Type; };
|
||||||
template<> struct Accumulator<short> { typedef float Type; };
|
template<> struct Accumulator<short> { typedef float Type; };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Squeared Euclidean distance functor
|
||||||
|
*/
|
||||||
|
template<class T>
|
||||||
|
struct CV_EXPORTS SL2
|
||||||
|
{
|
||||||
|
typedef T ValueType;
|
||||||
|
typedef typename Accumulator<T>::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
|
* Euclidean distance functor
|
||||||
*/
|
*/
|
||||||
@ -2395,77 +2416,77 @@ template<class Distance>
|
|||||||
inline void BruteForceMatcher<Distance>::commonKnnMatchImpl( BruteForceMatcher<Distance>& matcher,
|
inline void BruteForceMatcher<Distance>::commonKnnMatchImpl( BruteForceMatcher<Distance>& matcher,
|
||||||
const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int knn,
|
const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int knn,
|
||||||
const vector<Mat>& masks, bool compactResult )
|
const vector<Mat>& masks, bool compactResult )
|
||||||
{
|
{
|
||||||
typedef typename Distance::ValueType ValueType;
|
typedef typename Distance::ValueType ValueType;
|
||||||
typedef typename Distance::ResultType DistanceType;
|
typedef typename Distance::ResultType DistanceType;
|
||||||
CV_DbgAssert( !queryDescriptors.empty() );
|
CV_DbgAssert( !queryDescriptors.empty() );
|
||||||
CV_Assert( DataType<ValueType>::type == queryDescriptors.type() );
|
CV_Assert( DataType<ValueType>::type == queryDescriptors.type() );
|
||||||
|
|
||||||
int dimension = queryDescriptors.cols;
|
int dimension = queryDescriptors.cols;
|
||||||
matches.reserve(queryDescriptors.rows);
|
matches.reserve(queryDescriptors.rows);
|
||||||
|
|
||||||
size_t imgCount = matcher.trainDescCollection.size();
|
size_t imgCount = matcher.trainDescCollection.size();
|
||||||
vector<Mat> allDists( imgCount ); // distances between one query descriptor and all train descriptors
|
vector<Mat> allDists( imgCount ); // distances between one query descriptor and all train descriptors
|
||||||
for( size_t i = 0; i < imgCount; i++ )
|
for( size_t i = 0; i < imgCount; i++ )
|
||||||
allDists[i] = Mat( 1, matcher.trainDescCollection[i].rows, DataType<DistanceType>::type );
|
allDists[i] = Mat( 1, matcher.trainDescCollection[i].rows, DataType<DistanceType>::type );
|
||||||
|
|
||||||
for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ )
|
for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ )
|
||||||
{
|
{
|
||||||
if( matcher.isMaskedOut( masks, qIdx ) )
|
if( matcher.isMaskedOut( masks, qIdx ) )
|
||||||
{
|
{
|
||||||
if( !compactResult ) // push empty vector
|
if( !compactResult ) // push empty vector
|
||||||
matches.push_back( vector<DMatch>() );
|
matches.push_back( vector<DMatch>() );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 1. compute distances between i-th query descriptor and all train descriptors
|
// 1. compute distances between i-th query descriptor and all train descriptors
|
||||||
for( size_t iIdx = 0; iIdx < imgCount; iIdx++ )
|
for( size_t iIdx = 0; iIdx < imgCount; iIdx++ )
|
||||||
{
|
{
|
||||||
CV_Assert( DataType<ValueType>::type == matcher.trainDescCollection[iIdx].type() || matcher.trainDescCollection[iIdx].empty() );
|
CV_Assert( DataType<ValueType>::type == matcher.trainDescCollection[iIdx].type() || matcher.trainDescCollection[iIdx].empty() );
|
||||||
CV_Assert( queryDescriptors.cols == matcher.trainDescCollection[iIdx].cols ||
|
CV_Assert( queryDescriptors.cols == matcher.trainDescCollection[iIdx].cols ||
|
||||||
matcher.trainDescCollection[iIdx].empty() );
|
matcher.trainDescCollection[iIdx].empty() );
|
||||||
|
|
||||||
const ValueType* d1 = (const ValueType*)(queryDescriptors.data + queryDescriptors.step*qIdx);
|
const ValueType* d1 = (const ValueType*)(queryDescriptors.data + queryDescriptors.step*qIdx);
|
||||||
allDists[iIdx].setTo( Scalar::all(std::numeric_limits<DistanceType>::max()) );
|
allDists[iIdx].setTo( Scalar::all(std::numeric_limits<DistanceType>::max()) );
|
||||||
for( int tIdx = 0; tIdx < matcher.trainDescCollection[iIdx].rows; tIdx++ )
|
for( int tIdx = 0; tIdx < matcher.trainDescCollection[iIdx].rows; tIdx++ )
|
||||||
{
|
{
|
||||||
if( masks.empty() || matcher.isPossibleMatch(masks[iIdx], qIdx, tIdx) )
|
if( masks.empty() || matcher.isPossibleMatch(masks[iIdx], qIdx, tIdx) )
|
||||||
{
|
{
|
||||||
const ValueType* d2 = (const ValueType*)(matcher.trainDescCollection[iIdx].data +
|
const ValueType* d2 = (const ValueType*)(matcher.trainDescCollection[iIdx].data +
|
||||||
matcher.trainDescCollection[iIdx].step*tIdx);
|
matcher.trainDescCollection[iIdx].step*tIdx);
|
||||||
allDists[iIdx].at<DistanceType>(0, tIdx) = matcher.distance(d1, d2, dimension);
|
allDists[iIdx].at<DistanceType>(0, tIdx) = matcher.distance(d1, d2, dimension);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. choose k nearest matches for query[i]
|
// 2. choose k nearest matches for query[i]
|
||||||
matches.push_back( vector<DMatch>() );
|
matches.push_back( vector<DMatch>() );
|
||||||
vector<vector<DMatch> >::reverse_iterator curMatches = matches.rbegin();
|
vector<vector<DMatch> >::reverse_iterator curMatches = matches.rbegin();
|
||||||
for( int k = 0; k < knn; k++ )
|
for( int k = 0; k < knn; k++ )
|
||||||
{
|
{
|
||||||
DMatch bestMatch;
|
DMatch bestMatch;
|
||||||
bestMatch.distance = std::numeric_limits<float>::max();
|
bestMatch.distance = std::numeric_limits<float>::max();
|
||||||
for( size_t iIdx = 0; iIdx < imgCount; iIdx++ )
|
for( size_t iIdx = 0; iIdx < imgCount; iIdx++ )
|
||||||
{
|
{
|
||||||
if( !allDists[iIdx].empty() )
|
if( !allDists[iIdx].empty() )
|
||||||
{
|
{
|
||||||
double minVal;
|
double minVal;
|
||||||
Point minLoc;
|
Point minLoc;
|
||||||
minMaxLoc( allDists[iIdx], &minVal, 0, &minLoc, 0 );
|
minMaxLoc( allDists[iIdx], &minVal, 0, &minLoc, 0 );
|
||||||
if( minVal < bestMatch.distance )
|
if( minVal < bestMatch.distance )
|
||||||
bestMatch = DMatch( qIdx, minLoc.x, (int)iIdx, (float)minVal );
|
bestMatch = DMatch( qIdx, minLoc.x, (int)iIdx, (float)minVal );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if( bestMatch.trainIdx == -1 )
|
if( bestMatch.trainIdx == -1 )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
allDists[bestMatch.imgIdx].at<DistanceType>(0, bestMatch.trainIdx) = std::numeric_limits<DistanceType>::max();
|
allDists[bestMatch.imgIdx].at<DistanceType>(0, bestMatch.trainIdx) = std::numeric_limits<DistanceType>::max();
|
||||||
curMatches->push_back( bestMatch );
|
curMatches->push_back( bestMatch );
|
||||||
}
|
}
|
||||||
//TODO should already be sorted at this point?
|
//TODO should already be sorted at this point?
|
||||||
std::sort( curMatches->begin(), curMatches->end() );
|
std::sort( curMatches->begin(), curMatches->end() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Distance>
|
template<class Distance>
|
||||||
|
@ -328,6 +328,10 @@ Ptr<DescriptorMatcher> DescriptorMatcher::create( const string& descriptorMatche
|
|||||||
{
|
{
|
||||||
dm = new BruteForceMatcher<L2<float> >();
|
dm = new BruteForceMatcher<L2<float> >();
|
||||||
}
|
}
|
||||||
|
else if( !descriptorMatcherType.compare( "BruteForce-SL2" ) ) // Squared L2
|
||||||
|
{
|
||||||
|
dm = new BruteForceMatcher<SL2<float> >();
|
||||||
|
}
|
||||||
else if( !descriptorMatcherType.compare( "BruteForce-L1" ) )
|
else if( !descriptorMatcherType.compare( "BruteForce-L1" ) )
|
||||||
{
|
{
|
||||||
dm = new BruteForceMatcher<L1<float> >();
|
dm = new BruteForceMatcher<L1<float> >();
|
||||||
@ -345,10 +349,10 @@ Ptr<DescriptorMatcher> DescriptorMatcher::create( const string& descriptorMatche
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* BruteForce L2 specialization
|
* BruteForce SL2 and L2 specialization
|
||||||
*/
|
*/
|
||||||
template<>
|
template<>
|
||||||
void BruteForceMatcher<L2<float> >::knnMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int knn,
|
void BruteForceMatcher<SL2<float> >::knnMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int knn,
|
||||||
const vector<Mat>& masks, bool compactResult )
|
const vector<Mat>& masks, bool compactResult )
|
||||||
{
|
{
|
||||||
#ifndef HAVE_EIGEN
|
#ifndef HAVE_EIGEN
|
||||||
@ -427,7 +431,7 @@ void BruteForceMatcher<L2<float> >::knnMatchImpl( const Mat& queryDescriptors, v
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
e_allDists[bestImgIdx](bestTrainIdx) = -std::numeric_limits<float>::max();
|
e_allDists[bestImgIdx](bestTrainIdx) = -std::numeric_limits<float>::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() );
|
std::sort( curMatches->begin(), curMatches->end() );
|
||||||
}
|
}
|
||||||
@ -436,7 +440,7 @@ void BruteForceMatcher<L2<float> >::knnMatchImpl( const Mat& queryDescriptors, v
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void BruteForceMatcher<L2<float> >::radiusMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
|
void BruteForceMatcher<SL2<float> >::radiusMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
|
||||||
const vector<Mat>& masks, bool compactResult )
|
const vector<Mat>& masks, bool compactResult )
|
||||||
{
|
{
|
||||||
#ifndef HAVE_EIGEN
|
#ifndef HAVE_EIGEN
|
||||||
@ -492,7 +496,7 @@ void BruteForceMatcher<L2<float> >::radiusMatchImpl( const Mat& queryDescriptors
|
|||||||
{
|
{
|
||||||
if( masks.empty() || isPossibleMatch(masks[iIdx], qIdx, tIdx) )
|
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 )
|
if( d < maxDistance )
|
||||||
curMatches->push_back( DMatch( qIdx, tIdx, iIdx, d ) );
|
curMatches->push_back( DMatch( qIdx, tIdx, iIdx, d ) );
|
||||||
}
|
}
|
||||||
@ -504,6 +508,40 @@ void BruteForceMatcher<L2<float> >::radiusMatchImpl( const Mat& queryDescriptors
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void sqrtDistance( vector<vector<DMatch> >& 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<L2<float> >::knnMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int knn,
|
||||||
|
const vector<Mat>& masks, bool compactResult )
|
||||||
|
{
|
||||||
|
BruteForceMatcher<SL2<float> > matcherSL2;
|
||||||
|
matcherSL2.add( getTrainDescriptors() );
|
||||||
|
matcherSL2.knnMatch( queryDescriptors, matches, knn, masks, compactResult );
|
||||||
|
|
||||||
|
sqrtDistance( matches );
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void BruteForceMatcher<L2<float> >::radiusMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
|
||||||
|
const vector<Mat>& masks, bool compactResult )
|
||||||
|
{
|
||||||
|
const float maxDistance2 = maxDistance * maxDistance;
|
||||||
|
BruteForceMatcher<SL2<float> > matcherSL2;
|
||||||
|
matcherSL2.add( getTrainDescriptors() );
|
||||||
|
matcherSL2.radiusMatch( queryDescriptors, matches, maxDistance2, masks, compactResult );
|
||||||
|
|
||||||
|
sqrtDistance( matches );
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Flann based matcher
|
* Flann based matcher
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user