"atomic bomb" commit. Reorganized OpenCV directory structure
This commit is contained in:
1
modules/features2d/CMakeLists.txt
Normal file
1
modules/features2d/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
define_opencv_module(features2d opencv_core opencv_imgproc opencv_calib3d opencv_highgui)
|
||||
1890
modules/features2d/include/opencv2/features2d/features2d.hpp
Normal file
1890
modules/features2d/include/opencv2/features2d/features2d.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1004
modules/features2d/src/calonder.cpp
Normal file
1004
modules/features2d/src/calonder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
505
modules/features2d/src/descriptors.cpp
Normal file
505
modules/features2d/src/descriptors.cpp
Normal file
@@ -0,0 +1,505 @@
|
||||
/*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.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, 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 Intel Corporation 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"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
/****************************************************************************************\
|
||||
* DescriptorExtractor *
|
||||
\****************************************************************************************/
|
||||
/*
|
||||
* DescriptorExtractor
|
||||
*/
|
||||
struct RoiPredicate
|
||||
{
|
||||
RoiPredicate(float _minX, float _minY, float _maxX, float _maxY)
|
||||
: minX(_minX), minY(_minY), maxX(_maxX), maxY(_maxY)
|
||||
{}
|
||||
|
||||
bool operator()( const KeyPoint& keyPt) const
|
||||
{
|
||||
Point2f pt = keyPt.pt;
|
||||
return (pt.x < minX) || (pt.x >= maxX) || (pt.y < minY) || (pt.y >= maxY);
|
||||
}
|
||||
|
||||
float minX, minY, maxX, maxY;
|
||||
};
|
||||
|
||||
void DescriptorExtractor::removeBorderKeypoints( vector<KeyPoint>& keypoints,
|
||||
Size imageSize, int borderPixels )
|
||||
{
|
||||
keypoints.erase( remove_if(keypoints.begin(), keypoints.end(),
|
||||
RoiPredicate((float)borderPixels, (float)borderPixels,
|
||||
(float)(imageSize.width - borderPixels),
|
||||
(float)(imageSize.height - borderPixels))),
|
||||
keypoints.end());
|
||||
}
|
||||
|
||||
/****************************************************************************************\
|
||||
* SiftDescriptorExtractor *
|
||||
\****************************************************************************************/
|
||||
SiftDescriptorExtractor::SiftDescriptorExtractor( double magnification, bool isNormalize,
|
||||
int nOctaves, int nOctaveLayers, int firstOctave )
|
||||
: sift( magnification, isNormalize, nOctaves, nOctaveLayers, firstOctave )
|
||||
{}
|
||||
|
||||
void SiftDescriptorExtractor::compute( const Mat& image,
|
||||
vector<KeyPoint>& keypoints,
|
||||
Mat& descriptors) const
|
||||
{
|
||||
bool useProvidedKeypoints = true;
|
||||
sift(image, Mat(), keypoints, descriptors, useProvidedKeypoints);
|
||||
}
|
||||
|
||||
/****************************************************************************************\
|
||||
* SurfDescriptorExtractor *
|
||||
\****************************************************************************************/
|
||||
SurfDescriptorExtractor::SurfDescriptorExtractor( int nOctaves,
|
||||
int nOctaveLayers, bool extended )
|
||||
: surf( 0.0, nOctaves, nOctaveLayers, extended )
|
||||
{}
|
||||
|
||||
void SurfDescriptorExtractor::compute( const Mat& image,
|
||||
vector<KeyPoint>& keypoints,
|
||||
Mat& descriptors) const
|
||||
{
|
||||
// Compute descriptors for given keypoints
|
||||
vector<float> _descriptors;
|
||||
Mat mask;
|
||||
bool useProvidedKeypoints = true;
|
||||
surf(image, mask, keypoints, _descriptors, useProvidedKeypoints);
|
||||
|
||||
descriptors.create(keypoints.size(), surf.descriptorSize(), CV_32FC1);
|
||||
assert( (int)_descriptors.size() == descriptors.rows * descriptors.cols );
|
||||
std::copy(_descriptors.begin(), _descriptors.end(), descriptors.begin<float>());
|
||||
}
|
||||
|
||||
/****************************************************************************************\
|
||||
* GenericDescriptorMatch *
|
||||
\****************************************************************************************/
|
||||
/*
|
||||
* KeyPointCollection
|
||||
*/
|
||||
void KeyPointCollection::add( const Mat& _image, const vector<KeyPoint>& _points )
|
||||
{
|
||||
// update m_start_indices
|
||||
if( startIndices.empty() )
|
||||
startIndices.push_back(0);
|
||||
else
|
||||
startIndices.push_back(*startIndices.rbegin() + points.rbegin()->size());
|
||||
|
||||
// add image and keypoints
|
||||
images.push_back(_image);
|
||||
points.push_back(_points);
|
||||
}
|
||||
|
||||
KeyPoint KeyPointCollection::getKeyPoint( int index ) const
|
||||
{
|
||||
size_t i = 0;
|
||||
for(; i < startIndices.size() && startIndices[i] <= index; i++);
|
||||
i--;
|
||||
assert(i < startIndices.size() && (size_t)index - startIndices[i] < points[i].size());
|
||||
|
||||
return points[i][index - startIndices[i]];
|
||||
}
|
||||
|
||||
size_t KeyPointCollection::calcKeypointCount() const
|
||||
{
|
||||
if( startIndices.empty() )
|
||||
return 0;
|
||||
return *startIndices.rbegin() + points.rbegin()->size();
|
||||
}
|
||||
|
||||
/*
|
||||
* GenericDescriptorMatch
|
||||
*/
|
||||
void GenericDescriptorMatch::add( KeyPointCollection& collection )
|
||||
{
|
||||
for( size_t i = 0; i < collection.images.size(); i++ )
|
||||
add( collection.images[i], collection.points[i] );
|
||||
}
|
||||
|
||||
void GenericDescriptorMatch::classify( const Mat& image, vector<cv::KeyPoint>& points )
|
||||
{
|
||||
vector<int> keypointIndices;
|
||||
match( image, points, keypointIndices );
|
||||
|
||||
// remap keypoint indices to descriptors
|
||||
for( size_t i = 0; i < keypointIndices.size(); i++ )
|
||||
points[i].class_id = collection.getKeyPoint(keypointIndices[i]).class_id;
|
||||
};
|
||||
|
||||
/****************************************************************************************\
|
||||
* OneWayDescriptorMatch *
|
||||
\****************************************************************************************/
|
||||
OneWayDescriptorMatch::OneWayDescriptorMatch()
|
||||
{}
|
||||
|
||||
OneWayDescriptorMatch::OneWayDescriptorMatch( const Params& _params)
|
||||
{
|
||||
initialize(_params);
|
||||
}
|
||||
|
||||
OneWayDescriptorMatch::~OneWayDescriptorMatch()
|
||||
{}
|
||||
|
||||
void OneWayDescriptorMatch::initialize( const Params& _params)
|
||||
{
|
||||
base.release();
|
||||
params = _params;
|
||||
}
|
||||
|
||||
void OneWayDescriptorMatch::add( const Mat& image, vector<KeyPoint>& keypoints )
|
||||
{
|
||||
if( base.empty() )
|
||||
base = new OneWayDescriptorObject( params.patchSize, params.poseCount, params.trainPath.c_str(),
|
||||
params.pcaConfig.c_str(), params.pcaHrConfig.c_str(),
|
||||
params.pcaDescConfig.c_str());
|
||||
|
||||
size_t trainFeatureCount = keypoints.size();
|
||||
|
||||
base->Allocate( trainFeatureCount );
|
||||
|
||||
IplImage _image = image;
|
||||
for( size_t i = 0; i < keypoints.size(); i++ )
|
||||
base->InitializeDescriptor( i, &_image, keypoints[i], "" );
|
||||
|
||||
collection.add( Mat(), keypoints );
|
||||
|
||||
#if defined(_KDTREE)
|
||||
base->ConvertDescriptorsArrayToTree();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OneWayDescriptorMatch::add( KeyPointCollection& keypoints )
|
||||
{
|
||||
if( base.empty() )
|
||||
base = new OneWayDescriptorObject( params.patchSize, params.poseCount, params.trainPath.c_str(),
|
||||
params.pcaConfig.c_str(), params.pcaHrConfig.c_str(),
|
||||
params.pcaDescConfig.c_str());
|
||||
|
||||
size_t trainFeatureCount = keypoints.calcKeypointCount();
|
||||
|
||||
base->Allocate( trainFeatureCount );
|
||||
|
||||
int count = 0;
|
||||
for( size_t i = 0; i < keypoints.points.size(); i++ )
|
||||
{
|
||||
for( size_t j = 0; j < keypoints.points[i].size(); j++ )
|
||||
{
|
||||
IplImage img = keypoints.images[i];
|
||||
base->InitializeDescriptor( count++, &img, keypoints.points[i][j], "" );
|
||||
}
|
||||
|
||||
collection.add( Mat(), keypoints.points[i] );
|
||||
}
|
||||
|
||||
#if defined(_KDTREE)
|
||||
base->ConvertDescriptorsArrayToTree();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OneWayDescriptorMatch::match( const Mat& image, vector<KeyPoint>& points, vector<int>& indices)
|
||||
{
|
||||
indices.resize(points.size());
|
||||
IplImage _image = image;
|
||||
for( size_t i = 0; i < points.size(); i++ )
|
||||
{
|
||||
int descIdx = -1;
|
||||
int poseIdx = -1;
|
||||
float distance;
|
||||
base->FindDescriptor( &_image, points[i].pt, descIdx, poseIdx, distance );
|
||||
indices[i] = descIdx;
|
||||
}
|
||||
}
|
||||
|
||||
void OneWayDescriptorMatch::classify( const Mat& image, vector<KeyPoint>& points )
|
||||
{
|
||||
IplImage _image = image;
|
||||
for( size_t i = 0; i < points.size(); i++ )
|
||||
{
|
||||
int descIdx = -1;
|
||||
int poseIdx = -1;
|
||||
float distance;
|
||||
base->FindDescriptor(&_image, points[i].pt, descIdx, poseIdx, distance);
|
||||
points[i].class_id = collection.getKeyPoint(descIdx).class_id;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************************\
|
||||
* CalonderDescriptorMatch *
|
||||
\****************************************************************************************/
|
||||
CalonderDescriptorMatch::Params::Params( const RNG& _rng, const PatchGenerator& _patchGen,
|
||||
int _numTrees, int _depth, int _views,
|
||||
size_t _reducedNumDim,
|
||||
int _numQuantBits,
|
||||
bool _printStatus,
|
||||
int _patchSize ) :
|
||||
rng(_rng), patchGen(_patchGen), numTrees(_numTrees), depth(_depth), views(_views),
|
||||
patchSize(_patchSize), reducedNumDim(_reducedNumDim), numQuantBits(_numQuantBits), printStatus(_printStatus)
|
||||
{}
|
||||
|
||||
CalonderDescriptorMatch::Params::Params( const string& _filename )
|
||||
{
|
||||
filename = _filename;
|
||||
}
|
||||
|
||||
CalonderDescriptorMatch::CalonderDescriptorMatch()
|
||||
{}
|
||||
|
||||
CalonderDescriptorMatch::CalonderDescriptorMatch( const Params& _params )
|
||||
{
|
||||
initialize(_params);
|
||||
}
|
||||
|
||||
CalonderDescriptorMatch::~CalonderDescriptorMatch()
|
||||
{}
|
||||
|
||||
void CalonderDescriptorMatch::initialize( const Params& _params )
|
||||
{
|
||||
classifier.release();
|
||||
params = _params;
|
||||
if( !params.filename.empty() )
|
||||
{
|
||||
classifier = new RTreeClassifier;
|
||||
classifier->read( params.filename.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
void CalonderDescriptorMatch::add( const Mat& image, vector<KeyPoint>& keypoints )
|
||||
{
|
||||
if( params.filename.empty() )
|
||||
collection.add( image, keypoints );
|
||||
}
|
||||
|
||||
Mat CalonderDescriptorMatch::extractPatch( const Mat& image, const Point& pt, int patchSize ) const
|
||||
{
|
||||
const int offset = patchSize / 2;
|
||||
return image( Rect(pt.x - offset, pt.y - offset, patchSize, patchSize) );
|
||||
}
|
||||
|
||||
void CalonderDescriptorMatch::calcBestProbAndMatchIdx( const Mat& image, const Point& pt,
|
||||
float& bestProb, int& bestMatchIdx, float* signature )
|
||||
{
|
||||
IplImage roi = extractPatch( image, pt, params.patchSize );
|
||||
classifier->getSignature( &roi, signature );
|
||||
|
||||
bestProb = 0;
|
||||
bestMatchIdx = -1;
|
||||
for( size_t ci = 0; ci < (size_t)classifier->classes(); ci++ )
|
||||
{
|
||||
if( signature[ci] > bestProb )
|
||||
{
|
||||
bestProb = signature[ci];
|
||||
bestMatchIdx = ci;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalonderDescriptorMatch::trainRTreeClassifier()
|
||||
{
|
||||
if( classifier.empty() )
|
||||
{
|
||||
assert( params.filename.empty() );
|
||||
classifier = new RTreeClassifier;
|
||||
|
||||
vector<BaseKeypoint> baseKeyPoints;
|
||||
vector<IplImage> iplImages( collection.images.size() );
|
||||
for( size_t imageIdx = 0; imageIdx < collection.images.size(); imageIdx++ )
|
||||
{
|
||||
iplImages[imageIdx] = collection.images[imageIdx];
|
||||
for( size_t pointIdx = 0; pointIdx < collection.points[imageIdx].size(); pointIdx++ )
|
||||
{
|
||||
BaseKeypoint bkp;
|
||||
KeyPoint kp = collection.points[imageIdx][pointIdx];
|
||||
bkp.x = cvRound(kp.pt.x);
|
||||
bkp.y = cvRound(kp.pt.y);
|
||||
bkp.image = &iplImages[imageIdx];
|
||||
baseKeyPoints.push_back(bkp);
|
||||
}
|
||||
}
|
||||
classifier->train( baseKeyPoints, params.rng, params.patchGen, params.numTrees,
|
||||
params.depth, params.views, params.reducedNumDim, params.numQuantBits,
|
||||
params.printStatus );
|
||||
}
|
||||
}
|
||||
|
||||
void CalonderDescriptorMatch::match( const Mat& image, vector<KeyPoint>& keypoints, vector<int>& indices )
|
||||
{
|
||||
trainRTreeClassifier();
|
||||
|
||||
float bestProb = 0;
|
||||
AutoBuffer<float> signature( classifier->classes() );
|
||||
indices.resize( keypoints.size() );
|
||||
|
||||
for( size_t pi = 0; pi < keypoints.size(); pi++ )
|
||||
calcBestProbAndMatchIdx( image, keypoints[pi].pt, bestProb, indices[pi], signature );
|
||||
}
|
||||
|
||||
void CalonderDescriptorMatch::classify( const Mat& image, vector<KeyPoint>& keypoints )
|
||||
{
|
||||
trainRTreeClassifier();
|
||||
|
||||
AutoBuffer<float> signature( classifier->classes() );
|
||||
for( size_t pi = 0; pi < keypoints.size(); pi++ )
|
||||
{
|
||||
float bestProb = 0;
|
||||
int bestMatchIdx = -1;
|
||||
calcBestProbAndMatchIdx( image, keypoints[pi].pt, bestProb, bestMatchIdx, signature );
|
||||
keypoints[pi].class_id = collection.getKeyPoint(bestMatchIdx).class_id;
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************************\
|
||||
* FernDescriptorMatch *
|
||||
\****************************************************************************************/
|
||||
FernDescriptorMatch::Params::Params( int _nclasses, int _patchSize, int _signatureSize,
|
||||
int _nstructs, int _structSize, int _nviews, int _compressionMethod,
|
||||
const PatchGenerator& _patchGenerator ) :
|
||||
nclasses(_nclasses), patchSize(_patchSize), signatureSize(_signatureSize),
|
||||
nstructs(_nstructs), structSize(_structSize), nviews(_nviews),
|
||||
compressionMethod(_compressionMethod), patchGenerator(_patchGenerator)
|
||||
{}
|
||||
|
||||
FernDescriptorMatch::Params::Params( const string& _filename )
|
||||
{
|
||||
filename = _filename;
|
||||
}
|
||||
|
||||
FernDescriptorMatch::FernDescriptorMatch()
|
||||
{}
|
||||
|
||||
FernDescriptorMatch::FernDescriptorMatch( const Params& _params )
|
||||
{
|
||||
params = _params;
|
||||
}
|
||||
|
||||
FernDescriptorMatch::~FernDescriptorMatch()
|
||||
{}
|
||||
|
||||
void FernDescriptorMatch::initialize( const Params& _params )
|
||||
{
|
||||
classifier.release();
|
||||
params = _params;
|
||||
if( !params.filename.empty() )
|
||||
{
|
||||
classifier = new FernClassifier;
|
||||
FileStorage fs(params.filename, FileStorage::READ);
|
||||
if( fs.isOpened() )
|
||||
classifier->read( fs.getFirstTopLevelNode() );
|
||||
}
|
||||
}
|
||||
|
||||
void FernDescriptorMatch::add( const Mat& image, vector<KeyPoint>& keypoints )
|
||||
{
|
||||
if( params.filename.empty() )
|
||||
collection.add( image, keypoints );
|
||||
}
|
||||
|
||||
void FernDescriptorMatch::trainFernClassifier()
|
||||
{
|
||||
if( classifier.empty() )
|
||||
{
|
||||
assert( params.filename.empty() );
|
||||
|
||||
vector<Point2f> points;
|
||||
vector<Ptr<Mat> > refimgs( collection.images.size() );
|
||||
vector<int> labels;
|
||||
for( size_t imageIdx = 0; imageIdx < collection.images.size(); imageIdx++ )
|
||||
{
|
||||
refimgs[imageIdx] = &collection.images[imageIdx];
|
||||
for( size_t pointIdx = 0; pointIdx < collection.points[imageIdx].size(); pointIdx++ )
|
||||
{
|
||||
points.push_back(collection.points[imageIdx][pointIdx].pt);
|
||||
labels.push_back(imageIdx);
|
||||
}
|
||||
}
|
||||
|
||||
classifier = new FernClassifier( points, refimgs, labels, params.nclasses, params.patchSize,
|
||||
params.signatureSize, params.nstructs, params.structSize, params.nviews,
|
||||
params.compressionMethod, params.patchGenerator );
|
||||
}
|
||||
}
|
||||
|
||||
void FernDescriptorMatch::calcBestProbAndMatchIdx( const Mat& image, const Point2f& pt,
|
||||
float& bestProb, int& bestMatchIdx, vector<float>& signature )
|
||||
{
|
||||
(*classifier)( image, pt, signature);
|
||||
|
||||
bestProb = 0;
|
||||
bestMatchIdx = -1;
|
||||
for( size_t ci = 0; ci < (size_t)classifier->getClassCount(); ci++ )
|
||||
{
|
||||
if( signature[ci] > bestProb )
|
||||
{
|
||||
bestProb = signature[ci];
|
||||
bestMatchIdx = ci;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FernDescriptorMatch::match( const Mat& image, vector<KeyPoint>& keypoints, vector<int>& indices )
|
||||
{
|
||||
trainFernClassifier();
|
||||
|
||||
float bestProb = 0;
|
||||
indices.resize( keypoints.size() );
|
||||
vector<float> signature( (size_t)classifier->getClassCount() );
|
||||
|
||||
for( size_t pi = 0; pi < keypoints.size(); pi++ )
|
||||
calcBestProbAndMatchIdx( image, keypoints[pi].pt, bestProb, indices[pi], signature );
|
||||
}
|
||||
|
||||
void FernDescriptorMatch::classify( const Mat& image, vector<KeyPoint>& keypoints )
|
||||
{
|
||||
trainFernClassifier();
|
||||
|
||||
vector<float> signature( (size_t)classifier->getClassCount() );
|
||||
for( size_t pi = 0; pi < keypoints.size(); pi++ )
|
||||
{
|
||||
float bestProb = 0;
|
||||
int bestMatchIdx = -1;
|
||||
calcBestProbAndMatchIdx( image, keypoints[pi].pt, bestProb, bestMatchIdx, signature );
|
||||
keypoints[pi].class_id = collection.getKeyPoint(bestMatchIdx).class_id;
|
||||
}
|
||||
}
|
||||
178
modules/features2d/src/detectors.cpp
Normal file
178
modules/features2d/src/detectors.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
/*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.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, 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 Intel Corporation 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"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
/*
|
||||
FeatureDetector
|
||||
*/
|
||||
struct MaskPredicate
|
||||
{
|
||||
MaskPredicate( const Mat& _mask ) : mask(_mask)
|
||||
{}
|
||||
MaskPredicate& operator=(const MaskPredicate&) {}
|
||||
bool operator() (const KeyPoint& key_pt) const
|
||||
{
|
||||
return mask.at<uchar>( (int)(key_pt.pt.y + 0.5f), (int)(key_pt.pt.x + 0.5f) ) != 0;
|
||||
}
|
||||
|
||||
const Mat& mask;
|
||||
};
|
||||
|
||||
void FeatureDetector::removeInvalidPoints( const Mat& mask, vector<KeyPoint>& keypoints )
|
||||
{
|
||||
if( mask.empty() )
|
||||
return;
|
||||
|
||||
keypoints.erase(remove_if(keypoints.begin(), keypoints.end(), MaskPredicate(mask)), keypoints.end());
|
||||
};
|
||||
|
||||
/*
|
||||
FastFeatureDetector
|
||||
*/
|
||||
FastFeatureDetector::FastFeatureDetector( int _threshold, bool _nonmaxSuppression )
|
||||
: threshold(_threshold), nonmaxSuppression(_nonmaxSuppression)
|
||||
{}
|
||||
|
||||
void FastFeatureDetector::detectImpl( const Mat& image, const Mat& mask, vector<KeyPoint>& keypoints) const
|
||||
{
|
||||
FAST( image, keypoints, threshold, nonmaxSuppression );
|
||||
removeInvalidPoints( mask, keypoints );
|
||||
}
|
||||
|
||||
/*
|
||||
GoodFeaturesToTrackDetector
|
||||
*/
|
||||
GoodFeaturesToTrackDetector::GoodFeaturesToTrackDetector( int _maxCorners, double _qualityLevel, \
|
||||
double _minDistance, int _blockSize,
|
||||
bool _useHarrisDetector, double _k )
|
||||
: maxCorners(_maxCorners), qualityLevel(_qualityLevel), minDistance(_minDistance),
|
||||
blockSize(_blockSize), useHarrisDetector(_useHarrisDetector), k(_k)
|
||||
{}
|
||||
|
||||
void GoodFeaturesToTrackDetector::detectImpl( const Mat& image, const Mat& mask,
|
||||
vector<KeyPoint>& keypoints ) const
|
||||
{
|
||||
vector<Point2f> corners;
|
||||
goodFeaturesToTrack( image, corners, maxCorners, qualityLevel, minDistance, mask,
|
||||
blockSize, useHarrisDetector, k );
|
||||
keypoints.resize(corners.size());
|
||||
vector<Point2f>::const_iterator corner_it = corners.begin();
|
||||
vector<KeyPoint>::iterator keypoint_it = keypoints.begin();
|
||||
for( ; corner_it != corners.end(); ++corner_it, ++keypoint_it )
|
||||
{
|
||||
*keypoint_it = KeyPoint( *corner_it, 1.f );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MserFeatureDetector
|
||||
*/
|
||||
MserFeatureDetector::MserFeatureDetector( int delta, int minArea, int maxArea,
|
||||
float maxVariation, float minDiversity,
|
||||
int maxEvolution, double areaThreshold,
|
||||
double minMargin, int edgeBlurSize )
|
||||
: mser( delta, minArea, maxArea, maxVariation, minDiversity,
|
||||
maxEvolution, areaThreshold, minMargin, edgeBlurSize )
|
||||
{}
|
||||
|
||||
void MserFeatureDetector::detectImpl( const Mat& image, const Mat& mask, vector<KeyPoint>& keypoints ) const
|
||||
{
|
||||
vector<vector<Point> > msers;
|
||||
mser(image, msers, mask);
|
||||
|
||||
keypoints.resize( msers.size() );
|
||||
vector<vector<Point> >::const_iterator contour_it = msers.begin();
|
||||
vector<KeyPoint>::iterator keypoint_it = keypoints.begin();
|
||||
for( ; contour_it != msers.end(); ++contour_it, ++keypoint_it )
|
||||
{
|
||||
RotatedRect rect = fitEllipse(Mat(*contour_it));
|
||||
*keypoint_it = KeyPoint( rect.center, min(rect.size.height, rect.size.width), rect.angle);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
StarFeatureDetector
|
||||
*/
|
||||
StarFeatureDetector::StarFeatureDetector(int maxSize, int responseThreshold,
|
||||
int lineThresholdProjected,
|
||||
int lineThresholdBinarized,
|
||||
int suppressNonmaxSize)
|
||||
: star( maxSize, responseThreshold, lineThresholdProjected,
|
||||
lineThresholdBinarized, suppressNonmaxSize)
|
||||
{}
|
||||
|
||||
void StarFeatureDetector::detectImpl( const Mat& image, const Mat& mask, vector<KeyPoint>& keypoints) const
|
||||
{
|
||||
star(image, keypoints);
|
||||
removeInvalidPoints(mask, keypoints);
|
||||
}
|
||||
|
||||
/*
|
||||
SiftFeatureDetector
|
||||
*/
|
||||
SiftFeatureDetector::SiftFeatureDetector(double threshold, double edgeThreshold, int angleMode,
|
||||
int nOctaves, int nOctaveLayers, int firstOctave) :
|
||||
sift(threshold, edgeThreshold, angleMode, nOctaves, nOctaveLayers, firstOctave)
|
||||
{
|
||||
}
|
||||
|
||||
void SiftFeatureDetector::detectImpl( const Mat& image, const Mat& mask,
|
||||
vector<KeyPoint>& keypoints) const
|
||||
{
|
||||
sift(image, mask, keypoints);
|
||||
}
|
||||
|
||||
/*
|
||||
SurfFeatureDetector
|
||||
*/
|
||||
SurfFeatureDetector::SurfFeatureDetector( double hessianThreshold, int octaves, int octaveLayers)
|
||||
: surf(hessianThreshold, octaves, octaveLayers)
|
||||
{}
|
||||
|
||||
void SurfFeatureDetector::detectImpl( const Mat& image, const Mat& mask,
|
||||
vector<KeyPoint>& keypoints) const
|
||||
{
|
||||
surf(image, mask, keypoints);
|
||||
}
|
||||
6057
modules/features2d/src/fast.cpp
Normal file
6057
modules/features2d/src/fast.cpp
Normal file
File diff suppressed because it is too large
Load Diff
98
modules/features2d/src/keypoint.cpp
Normal file
98
modules/features2d/src/keypoint.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
/*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) 2008, 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 Intel Corporation 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"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
|
||||
void write(FileStorage& fs, const string& objname, const vector<KeyPoint>& keypoints)
|
||||
{
|
||||
WriteStructContext ws(fs, objname, CV_NODE_SEQ + CV_NODE_FLOW);
|
||||
|
||||
int i, npoints = (int)keypoints.size();
|
||||
for( i = 0; i < npoints; i++ )
|
||||
{
|
||||
const KeyPoint& kpt = keypoints[i];
|
||||
write(fs, kpt.pt.x);
|
||||
write(fs, kpt.pt.y);
|
||||
write(fs, kpt.size);
|
||||
write(fs, kpt.angle);
|
||||
write(fs, kpt.response);
|
||||
write(fs, kpt.octave);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void read(const FileNode& node, vector<KeyPoint>& keypoints)
|
||||
{
|
||||
keypoints.resize(0);
|
||||
FileNodeIterator it = node.begin(), it_end = node.end();
|
||||
for( ; it != it_end; )
|
||||
{
|
||||
KeyPoint kpt;
|
||||
it >> kpt.pt.x >> kpt.pt.y >> kpt.size >> kpt.angle >> kpt.response >> kpt.octave;
|
||||
keypoints.push_back(kpt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void KeyPoint::convert(const std::vector<KeyPoint>& u, std::vector<Point2f>& v)
|
||||
{
|
||||
size_t i, sz = u.size();
|
||||
v.resize(sz);
|
||||
|
||||
for( i = 0; i < sz; i++ )
|
||||
v[i] = u[i].pt;
|
||||
}
|
||||
|
||||
void KeyPoint::convert( const std::vector<Point2f>& u, std::vector<KeyPoint>& v,
|
||||
float size, float response, int octave, int class_id )
|
||||
{
|
||||
size_t i, sz = u.size();
|
||||
v.resize(sz);
|
||||
|
||||
for( i = 0; i < sz; i++ )
|
||||
v[i] = KeyPoint(u[i], size, -1, response, octave, class_id);
|
||||
}
|
||||
|
||||
}
|
||||
1296
modules/features2d/src/mser.cpp
Normal file
1296
modules/features2d/src/mser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1796
modules/features2d/src/oneway.cpp
Normal file
1796
modules/features2d/src/oneway.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1373
modules/features2d/src/planardetect.cpp
Normal file
1373
modules/features2d/src/planardetect.cpp
Normal file
File diff suppressed because it is too large
Load Diff
44
modules/features2d/src/precomp.cpp
Normal file
44
modules/features2d/src/precomp.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
/*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.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, 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 Intel Corporation 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"
|
||||
|
||||
/* End of file. */
|
||||
55
modules/features2d/src/precomp.hpp
Normal file
55
modules/features2d/src/precomp.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/*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*/
|
||||
|
||||
#ifndef __OPENCV_PRECOMP_H__
|
||||
#define __OPENCV_PRECOMP_H__
|
||||
|
||||
#if _MSC_VER >= 1200
|
||||
#pragma warning( disable: 4251 4710 4711 4514 4996 ) /* function AAA selected for automatic inline expansion */
|
||||
#endif
|
||||
|
||||
#include "opencv2/features2d/features2d.hpp"
|
||||
#include "opencv2/imgproc/imgproc.hpp"
|
||||
#include "opencv2/imgproc/imgproc_c.h"
|
||||
#include "opencv2/core/internal.hpp"
|
||||
|
||||
#endif
|
||||
2098
modules/features2d/src/sift.cpp
Normal file
2098
modules/features2d/src/sift.cpp
Normal file
File diff suppressed because it is too large
Load Diff
479
modules/features2d/src/stardetector.cpp
Normal file
479
modules/features2d/src/stardetector.cpp
Normal file
@@ -0,0 +1,479 @@
|
||||
/*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) 2008, 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 Intel Corporation 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"
|
||||
|
||||
static void
|
||||
icvComputeIntegralImages( const CvMat* matI, CvMat* matS, CvMat* matT, CvMat* _FT )
|
||||
{
|
||||
int x, y, rows = matI->rows, cols = matI->cols;
|
||||
const uchar* I = matI->data.ptr;
|
||||
int *S = matS->data.i, *T = matT->data.i, *FT = _FT->data.i;
|
||||
int istep = matI->step, step = matS->step/sizeof(S[0]);
|
||||
|
||||
assert( CV_MAT_TYPE(matI->type) == CV_8UC1 &&
|
||||
CV_MAT_TYPE(matS->type) == CV_32SC1 &&
|
||||
CV_ARE_TYPES_EQ(matS, matT) && CV_ARE_TYPES_EQ(matS, _FT) &&
|
||||
CV_ARE_SIZES_EQ(matS, matT) && CV_ARE_SIZES_EQ(matS, _FT) &&
|
||||
matS->step == matT->step && matS->step == _FT->step &&
|
||||
matI->rows+1 == matS->rows && matI->cols+1 == matS->cols );
|
||||
|
||||
for( x = 0; x <= cols; x++ )
|
||||
S[x] = T[x] = FT[x] = 0;
|
||||
|
||||
S += step; T += step; FT += step;
|
||||
S[0] = T[0] = 0;
|
||||
FT[0] = I[0];
|
||||
for( x = 1; x < cols; x++ )
|
||||
{
|
||||
S[x] = S[x-1] + I[x-1];
|
||||
T[x] = I[x-1];
|
||||
FT[x] = I[x] + I[x-1];
|
||||
}
|
||||
S[cols] = S[cols-1] + I[cols-1];
|
||||
T[cols] = FT[cols] = I[cols-1];
|
||||
|
||||
for( y = 2; y <= rows; y++ )
|
||||
{
|
||||
I += istep, S += step, T += step, FT += step;
|
||||
|
||||
S[0] = S[-step]; S[1] = S[-step+1] + I[0];
|
||||
T[0] = T[-step + 1];
|
||||
T[1] = FT[0] = T[-step + 2] + I[-istep] + I[0];
|
||||
FT[1] = FT[-step + 2] + I[-istep] + I[1] + I[0];
|
||||
|
||||
for( x = 2; x < cols; x++ )
|
||||
{
|
||||
S[x] = S[x - 1] + S[-step + x] - S[-step + x - 1] + I[x - 1];
|
||||
T[x] = T[-step + x - 1] + T[-step + x + 1] - T[-step*2 + x] + I[-istep + x - 1] + I[x - 1];
|
||||
FT[x] = FT[-step + x - 1] + FT[-step + x + 1] - FT[-step*2 + x] + I[x] + I[x-1];
|
||||
}
|
||||
|
||||
S[cols] = S[cols - 1] + S[-step + cols] - S[-step + cols - 1] + I[cols - 1];
|
||||
T[cols] = FT[cols] = T[-step + cols - 1] + I[-istep + cols - 1] + I[cols - 1];
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct CvStarFeature
|
||||
{
|
||||
int area;
|
||||
int* p[8];
|
||||
}
|
||||
CvStarFeature;
|
||||
|
||||
static int
|
||||
icvStarDetectorComputeResponses( const CvMat* img, CvMat* responses, CvMat* sizes,
|
||||
const CvStarDetectorParams* params )
|
||||
{
|
||||
const int MAX_PATTERN = 17;
|
||||
static const int sizes0[] = {1, 2, 3, 4, 6, 8, 11, 12, 16, 22, 23, 32, 45, 46, 64, 90, 128, -1};
|
||||
static const int pairs[][2] = {{1, 0}, {3, 1}, {4, 2}, {5, 3}, {7, 4}, {8, 5}, {9, 6},
|
||||
{11, 8}, {13, 10}, {14, 11}, {15, 12}, {16, 14}, {-1, -1}};
|
||||
float invSizes[MAX_PATTERN][2];
|
||||
int sizes1[MAX_PATTERN];
|
||||
|
||||
#if CV_SSE2
|
||||
__m128 invSizes4[MAX_PATTERN][2];
|
||||
__m128 sizes1_4[MAX_PATTERN];
|
||||
Cv32suf absmask;
|
||||
absmask.i = 0x7fffffff;
|
||||
volatile bool useSIMD = cv::checkHardwareSupport(CV_CPU_SSE2);
|
||||
#endif
|
||||
CvStarFeature f[MAX_PATTERN];
|
||||
|
||||
CvMat *sum = 0, *tilted = 0, *flatTilted = 0;
|
||||
int y, i=0, rows = img->rows, cols = img->cols, step;
|
||||
int border, npatterns=0, maxIdx=0;
|
||||
#ifdef _OPENMP
|
||||
int nthreads = cvGetNumThreads();
|
||||
#endif
|
||||
|
||||
assert( CV_MAT_TYPE(img->type) == CV_8UC1 &&
|
||||
CV_MAT_TYPE(responses->type) == CV_32FC1 &&
|
||||
CV_MAT_TYPE(sizes->type) == CV_16SC1 &&
|
||||
CV_ARE_SIZES_EQ(responses, sizes) );
|
||||
|
||||
while( pairs[i][0] >= 0 && !
|
||||
( sizes0[pairs[i][0]] >= params->maxSize
|
||||
|| sizes0[pairs[i+1][0]] + sizes0[pairs[i+1][0]]/2 >= std::min(rows, cols) ) )
|
||||
{
|
||||
++i;
|
||||
}
|
||||
|
||||
npatterns = i;
|
||||
npatterns += (pairs[npatterns-1][0] >= 0);
|
||||
maxIdx = pairs[npatterns-1][0];
|
||||
|
||||
sum = cvCreateMat( rows + 1, cols + 1, CV_32SC1 );
|
||||
tilted = cvCreateMat( rows + 1, cols + 1, CV_32SC1 );
|
||||
flatTilted = cvCreateMat( rows + 1, cols + 1, CV_32SC1 );
|
||||
step = sum->step/CV_ELEM_SIZE(sum->type);
|
||||
|
||||
icvComputeIntegralImages( img, sum, tilted, flatTilted );
|
||||
|
||||
for( i = 0; i <= maxIdx; i++ )
|
||||
{
|
||||
int ur_size = sizes0[i], t_size = sizes0[i] + sizes0[i]/2;
|
||||
int ur_area = (2*ur_size + 1)*(2*ur_size + 1);
|
||||
int t_area = t_size*t_size + (t_size + 1)*(t_size + 1);
|
||||
|
||||
f[i].p[0] = sum->data.i + (ur_size + 1)*step + ur_size + 1;
|
||||
f[i].p[1] = sum->data.i - ur_size*step + ur_size + 1;
|
||||
f[i].p[2] = sum->data.i + (ur_size + 1)*step - ur_size;
|
||||
f[i].p[3] = sum->data.i - ur_size*step - ur_size;
|
||||
|
||||
f[i].p[4] = tilted->data.i + (t_size + 1)*step + 1;
|
||||
f[i].p[5] = flatTilted->data.i - t_size;
|
||||
f[i].p[6] = flatTilted->data.i + t_size + 1;
|
||||
f[i].p[7] = tilted->data.i - t_size*step + 1;
|
||||
|
||||
f[i].area = ur_area + t_area;
|
||||
sizes1[i] = sizes0[i];
|
||||
}
|
||||
// negate end points of the size range
|
||||
// for a faster rejection of very small or very large features in non-maxima suppression.
|
||||
sizes1[0] = -sizes1[0];
|
||||
sizes1[1] = -sizes1[1];
|
||||
sizes1[maxIdx] = -sizes1[maxIdx];
|
||||
border = sizes0[maxIdx] + sizes0[maxIdx]/2;
|
||||
|
||||
for( i = 0; i < npatterns; i++ )
|
||||
{
|
||||
int innerArea = f[pairs[i][1]].area;
|
||||
int outerArea = f[pairs[i][0]].area - innerArea;
|
||||
invSizes[i][0] = 1.f/outerArea;
|
||||
invSizes[i][1] = 1.f/innerArea;
|
||||
}
|
||||
|
||||
#if CV_SSE2
|
||||
if( useSIMD )
|
||||
{
|
||||
for( i = 0; i < npatterns; i++ )
|
||||
{
|
||||
_mm_store_ps((float*)&invSizes4[i][0], _mm_set1_ps(invSizes[i][0]));
|
||||
_mm_store_ps((float*)&invSizes4[i][1], _mm_set1_ps(invSizes[i][1]));
|
||||
}
|
||||
|
||||
for( i = 0; i <= maxIdx; i++ )
|
||||
_mm_store_ps((float*)&sizes1_4[i], _mm_set1_ps((float)sizes1[i]));
|
||||
}
|
||||
#endif
|
||||
|
||||
for( y = 0; y < border; y++ )
|
||||
{
|
||||
float* r_ptr = (float*)(responses->data.ptr + responses->step*y);
|
||||
float* r_ptr2 = (float*)(responses->data.ptr + responses->step*(rows - 1 - y));
|
||||
short* s_ptr = (short*)(sizes->data.ptr + sizes->step*y);
|
||||
short* s_ptr2 = (short*)(sizes->data.ptr + sizes->step*(rows - 1 - y));
|
||||
|
||||
memset( r_ptr, 0, cols*sizeof(r_ptr[0]));
|
||||
memset( r_ptr2, 0, cols*sizeof(r_ptr2[0]));
|
||||
memset( s_ptr, 0, cols*sizeof(s_ptr[0]));
|
||||
memset( s_ptr2, 0, cols*sizeof(s_ptr2[0]));
|
||||
}
|
||||
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for num_threads(nthreads) schedule(static)
|
||||
#endif
|
||||
for( y = border; y < rows - border; y++ )
|
||||
{
|
||||
int x = border, i;
|
||||
float* r_ptr = (float*)(responses->data.ptr + responses->step*y);
|
||||
short* s_ptr = (short*)(sizes->data.ptr + sizes->step*y);
|
||||
|
||||
memset( r_ptr, 0, border*sizeof(r_ptr[0]));
|
||||
memset( s_ptr, 0, border*sizeof(s_ptr[0]));
|
||||
memset( r_ptr + cols - border, 0, border*sizeof(r_ptr[0]));
|
||||
memset( s_ptr + cols - border, 0, border*sizeof(s_ptr[0]));
|
||||
|
||||
#if CV_SSE2
|
||||
if( useSIMD )
|
||||
{
|
||||
__m128 absmask4 = _mm_set1_ps(absmask.f);
|
||||
for( ; x <= cols - border - 4; x += 4 )
|
||||
{
|
||||
int ofs = y*step + x;
|
||||
__m128 vals[MAX_PATTERN];
|
||||
__m128 bestResponse = _mm_setzero_ps();
|
||||
__m128 bestSize = _mm_setzero_ps();
|
||||
|
||||
for( i = 0; i <= maxIdx; i++ )
|
||||
{
|
||||
const int** p = (const int**)&f[i].p[0];
|
||||
__m128i r0 = _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(p[0]+ofs)),
|
||||
_mm_loadu_si128((const __m128i*)(p[1]+ofs)));
|
||||
__m128i r1 = _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(p[3]+ofs)),
|
||||
_mm_loadu_si128((const __m128i*)(p[2]+ofs)));
|
||||
__m128i r2 = _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(p[4]+ofs)),
|
||||
_mm_loadu_si128((const __m128i*)(p[5]+ofs)));
|
||||
__m128i r3 = _mm_sub_epi32(_mm_loadu_si128((const __m128i*)(p[7]+ofs)),
|
||||
_mm_loadu_si128((const __m128i*)(p[6]+ofs)));
|
||||
r0 = _mm_add_epi32(_mm_add_epi32(r0,r1), _mm_add_epi32(r2,r3));
|
||||
_mm_store_ps((float*)&vals[i], _mm_cvtepi32_ps(r0));
|
||||
}
|
||||
|
||||
for( i = 0; i < npatterns; i++ )
|
||||
{
|
||||
__m128 inner_sum = vals[pairs[i][1]];
|
||||
__m128 outer_sum = _mm_sub_ps(vals[pairs[i][0]], inner_sum);
|
||||
__m128 response = _mm_sub_ps(_mm_mul_ps(inner_sum, invSizes4[i][1]),
|
||||
_mm_mul_ps(outer_sum, invSizes4[i][0]));
|
||||
__m128 swapmask = _mm_cmpgt_ps(_mm_and_ps(response,absmask4),
|
||||
_mm_and_ps(bestResponse,absmask4));
|
||||
bestResponse = _mm_xor_ps(bestResponse,
|
||||
_mm_and_ps(_mm_xor_ps(response,bestResponse), swapmask));
|
||||
bestSize = _mm_xor_ps(bestSize,
|
||||
_mm_and_ps(_mm_xor_ps(sizes1_4[pairs[i][0]], bestSize), swapmask));
|
||||
}
|
||||
|
||||
_mm_storeu_ps(r_ptr + x, bestResponse);
|
||||
_mm_storel_epi64((__m128i*)(s_ptr + x),
|
||||
_mm_packs_epi32(_mm_cvtps_epi32(bestSize),_mm_setzero_si128()));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for( ; x < cols - border; x++ )
|
||||
{
|
||||
int ofs = y*step + x;
|
||||
int vals[MAX_PATTERN];
|
||||
float bestResponse = 0;
|
||||
int bestSize = 0;
|
||||
|
||||
for( i = 0; i <= maxIdx; i++ )
|
||||
{
|
||||
const int** p = (const int**)&f[i].p[0];
|
||||
vals[i] = p[0][ofs] - p[1][ofs] - p[2][ofs] + p[3][ofs] +
|
||||
p[4][ofs] - p[5][ofs] - p[6][ofs] + p[7][ofs];
|
||||
}
|
||||
for( i = 0; i < npatterns; i++ )
|
||||
{
|
||||
int inner_sum = vals[pairs[i][1]];
|
||||
int outer_sum = vals[pairs[i][0]] - inner_sum;
|
||||
float response = inner_sum*invSizes[i][1] - outer_sum*invSizes[i][0];
|
||||
if( fabs(response) > fabs(bestResponse) )
|
||||
{
|
||||
bestResponse = response;
|
||||
bestSize = sizes1[pairs[i][0]];
|
||||
}
|
||||
}
|
||||
|
||||
r_ptr[x] = bestResponse;
|
||||
s_ptr[x] = (short)bestSize;
|
||||
}
|
||||
}
|
||||
|
||||
cvReleaseMat(&sum);
|
||||
cvReleaseMat(&tilted);
|
||||
cvReleaseMat(&flatTilted);
|
||||
|
||||
return border;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
icvStarDetectorSuppressLines( const CvMat* responses, const CvMat* sizes, CvPoint pt,
|
||||
const CvStarDetectorParams* params )
|
||||
{
|
||||
const float* r_ptr = responses->data.fl;
|
||||
int rstep = responses->step/sizeof(r_ptr[0]);
|
||||
const short* s_ptr = sizes->data.s;
|
||||
int sstep = sizes->step/sizeof(s_ptr[0]);
|
||||
int sz = s_ptr[pt.y*sstep + pt.x];
|
||||
int x, y, delta = sz/4, radius = delta*4;
|
||||
float Lxx = 0, Lyy = 0, Lxy = 0;
|
||||
int Lxxb = 0, Lyyb = 0, Lxyb = 0;
|
||||
|
||||
for( y = pt.y - radius; y <= pt.y + radius; y += delta )
|
||||
for( x = pt.x - radius; x <= pt.x + radius; x += delta )
|
||||
{
|
||||
float Lx = r_ptr[y*rstep + x + 1] - r_ptr[y*rstep + x - 1];
|
||||
float Ly = r_ptr[(y+1)*rstep + x] - r_ptr[(y-1)*rstep + x];
|
||||
Lxx += Lx*Lx; Lyy += Ly*Ly; Lxy += Lx*Ly;
|
||||
}
|
||||
|
||||
if( (Lxx + Lyy)*(Lxx + Lyy) >= params->lineThresholdProjected*(Lxx*Lyy - Lxy*Lxy) )
|
||||
return true;
|
||||
|
||||
for( y = pt.y - radius; y <= pt.y + radius; y += delta )
|
||||
for( x = pt.x - radius; x <= pt.x + radius; x += delta )
|
||||
{
|
||||
int Lxb = (s_ptr[y*sstep + x + 1] == sz) - (s_ptr[y*sstep + x - 1] == sz);
|
||||
int Lyb = (s_ptr[(y+1)*sstep + x] == sz) - (s_ptr[(y-1)*sstep + x] == sz);
|
||||
Lxxb += Lxb * Lxb; Lyyb += Lyb * Lyb; Lxyb += Lxb * Lyb;
|
||||
}
|
||||
|
||||
if( (Lxxb + Lyyb)*(Lxxb + Lyyb) >= params->lineThresholdBinarized*(Lxxb*Lyyb - Lxyb*Lxyb) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
icvStarDetectorSuppressNonmax( const CvMat* responses, const CvMat* sizes,
|
||||
CvSeq* keypoints, int border,
|
||||
const CvStarDetectorParams* params )
|
||||
{
|
||||
int x, y, x1, y1, delta = params->suppressNonmaxSize/2;
|
||||
int rows = responses->rows, cols = responses->cols;
|
||||
const float* r_ptr = responses->data.fl;
|
||||
int rstep = responses->step/sizeof(r_ptr[0]);
|
||||
const short* s_ptr = sizes->data.s;
|
||||
int sstep = sizes->step/sizeof(s_ptr[0]);
|
||||
short featureSize = 0;
|
||||
|
||||
for( y = border; y < rows - border; y += delta+1 )
|
||||
for( x = border; x < cols - border; x += delta+1 )
|
||||
{
|
||||
float maxResponse = (float)params->responseThreshold;
|
||||
float minResponse = (float)-params->responseThreshold;
|
||||
CvPoint maxPt = {-1,-1}, minPt = {-1,-1};
|
||||
int tileEndY = MIN(y + delta, rows - border - 1);
|
||||
int tileEndX = MIN(x + delta, cols - border - 1);
|
||||
|
||||
for( y1 = y; y1 <= tileEndY; y1++ )
|
||||
for( x1 = x; x1 <= tileEndX; x1++ )
|
||||
{
|
||||
float val = r_ptr[y1*rstep + x1];
|
||||
if( maxResponse < val )
|
||||
{
|
||||
maxResponse = val;
|
||||
maxPt = cvPoint(x1, y1);
|
||||
}
|
||||
else if( minResponse > val )
|
||||
{
|
||||
minResponse = val;
|
||||
minPt = cvPoint(x1, y1);
|
||||
}
|
||||
}
|
||||
|
||||
if( maxPt.x >= 0 )
|
||||
{
|
||||
for( y1 = maxPt.y - delta; y1 <= maxPt.y + delta; y1++ )
|
||||
for( x1 = maxPt.x - delta; x1 <= maxPt.x + delta; x1++ )
|
||||
{
|
||||
float val = r_ptr[y1*rstep + x1];
|
||||
if( val >= maxResponse && (y1 != maxPt.y || x1 != maxPt.x))
|
||||
goto skip_max;
|
||||
}
|
||||
|
||||
if( (featureSize = s_ptr[maxPt.y*sstep + maxPt.x]) >= 4 &&
|
||||
!icvStarDetectorSuppressLines( responses, sizes, maxPt, params ))
|
||||
{
|
||||
CvStarKeypoint kpt = cvStarKeypoint( maxPt, featureSize, maxResponse );
|
||||
cvSeqPush( keypoints, &kpt );
|
||||
}
|
||||
}
|
||||
skip_max:
|
||||
if( minPt.x >= 0 )
|
||||
{
|
||||
for( y1 = minPt.y - delta; y1 <= minPt.y + delta; y1++ )
|
||||
for( x1 = minPt.x - delta; x1 <= minPt.x + delta; x1++ )
|
||||
{
|
||||
float val = r_ptr[y1*rstep + x1];
|
||||
if( val <= minResponse && (y1 != minPt.y || x1 != minPt.x))
|
||||
goto skip_min;
|
||||
}
|
||||
|
||||
if( (featureSize = s_ptr[minPt.y*sstep + minPt.x]) >= 4 &&
|
||||
!icvStarDetectorSuppressLines( responses, sizes, minPt, params ))
|
||||
{
|
||||
CvStarKeypoint kpt = cvStarKeypoint( minPt, featureSize, minResponse );
|
||||
cvSeqPush( keypoints, &kpt );
|
||||
}
|
||||
}
|
||||
skip_min:
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
CV_IMPL CvSeq*
|
||||
cvGetStarKeypoints( const CvArr* _img, CvMemStorage* storage,
|
||||
CvStarDetectorParams params )
|
||||
{
|
||||
CvMat stub, *img = cvGetMat(_img, &stub);
|
||||
CvSeq* keypoints = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvStarKeypoint), storage );
|
||||
CvMat* responses = cvCreateMat( img->rows, img->cols, CV_32FC1 );
|
||||
CvMat* sizes = cvCreateMat( img->rows, img->cols, CV_16SC1 );
|
||||
|
||||
int border = icvStarDetectorComputeResponses( img, responses, sizes, ¶ms );
|
||||
if( border >= 0 )
|
||||
icvStarDetectorSuppressNonmax( responses, sizes, keypoints, border, ¶ms );
|
||||
|
||||
cvReleaseMat( &responses );
|
||||
cvReleaseMat( &sizes );
|
||||
|
||||
return border >= 0 ? keypoints : 0;
|
||||
}
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
StarDetector::StarDetector()
|
||||
{
|
||||
*(CvStarDetectorParams*)this = cvStarDetectorParams();
|
||||
}
|
||||
|
||||
StarDetector::StarDetector(int _maxSize, int _responseThreshold,
|
||||
int _lineThresholdProjected,
|
||||
int _lineThresholdBinarized,
|
||||
int _suppressNonmaxSize)
|
||||
{
|
||||
*(CvStarDetectorParams*)this = cvStarDetectorParams(_maxSize, _responseThreshold,
|
||||
_lineThresholdProjected, _lineThresholdBinarized, _suppressNonmaxSize);
|
||||
}
|
||||
|
||||
void StarDetector::operator()(const Mat& image, vector<KeyPoint>& keypoints) const
|
||||
{
|
||||
CvMat _image = image;
|
||||
MemStorage storage(cvCreateMemStorage(0));
|
||||
Seq<CvStarKeypoint> kp = cvGetStarKeypoints( &_image, storage, *(const CvStarDetectorParams*)this);
|
||||
Seq<CvStarKeypoint>::iterator it = kp.begin();
|
||||
keypoints.resize(kp.size());
|
||||
size_t i, n = kp.size();
|
||||
for( i = 0; i < n; i++, ++it )
|
||||
{
|
||||
const CvStarKeypoint& kpt = *it;
|
||||
keypoints[i] = KeyPoint(kpt.pt, (float)kpt.size, -1.f, kpt.response, 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
900
modules/features2d/src/surf.cpp
Normal file
900
modules/features2d/src/surf.cpp
Normal file
@@ -0,0 +1,900 @@
|
||||
/* Original code has been submitted by Liu Liu. Here is the copyright.
|
||||
----------------------------------------------------------------------------------
|
||||
* An OpenCV Implementation of SURF
|
||||
* Further Information Refer to "SURF: Speed-Up Robust Feature"
|
||||
* Author: Liu Liu
|
||||
* liuliu.1987+opencv@gmail.com
|
||||
*
|
||||
* There are still serveral lacks for this experimental implementation:
|
||||
* 1.The interpolation of sub-pixel mentioned in article was not implemented yet;
|
||||
* 2.A comparision with original libSurf.so shows that the hessian detector is not a 100% match to their implementation;
|
||||
* 3.Due to above reasons, I recommanded the original one for study and reuse;
|
||||
*
|
||||
* However, the speed of this implementation is something comparable to original one.
|
||||
*
|
||||
* Copyright© 2008, Liu Liu All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
* Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
* Redistributions 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 Contributor 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 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
The following changes have been made, comparing to the original contribution:
|
||||
1. A lot of small optimizations, less memory allocations, got rid of global buffers
|
||||
2. Reversed order of cvGetQuadrangleSubPix and cvResize calls; probably less accurate, but much faster
|
||||
3. The descriptor computing part (which is most expensive) is threaded using OpenMP
|
||||
(subpixel-accurate keypoint localization and scale estimation are still TBD)
|
||||
*/
|
||||
|
||||
/*
|
||||
KeyPoint position and scale interpolation has been implemented as described in
|
||||
the Brown and Lowe paper cited by the SURF paper.
|
||||
|
||||
The sampling step along the x and y axes of the image for the determinant of the
|
||||
Hessian is now the same for each layer in an octave. While this increases the
|
||||
computation time, it ensures that a true 3x3x3 neighbourhood exists, with
|
||||
samples calculated at the same position in the layers above and below. This
|
||||
results in improved maxima detection and non-maxima suppression, and I think it
|
||||
is consistent with the description in the SURF paper.
|
||||
|
||||
The wavelet size sampling interval has also been made consistent. The wavelet
|
||||
size at the first layer of the first octave is now 9 instead of 7. Along with
|
||||
regular position sampling steps, this makes location and scale interpolation
|
||||
easy. I think this is consistent with the SURF paper and original
|
||||
implementation.
|
||||
|
||||
The scaling of the wavelet parameters has been fixed to ensure that the patterns
|
||||
are symmetric around the centre. Previously the truncation caused by integer
|
||||
division in the scaling ratio caused a bias towards the top left of the wavelet,
|
||||
resulting in inconsistent keypoint positions.
|
||||
|
||||
The matrices for the determinant and trace of the Hessian are now reused in each
|
||||
octave.
|
||||
|
||||
The extraction of the patch of pixels surrounding a keypoint used to build a
|
||||
descriptor has been simplified.
|
||||
|
||||
KeyPoint descriptor normalisation has been changed from normalising each 4x4
|
||||
cell (resulting in a descriptor of magnitude 16) to normalising the entire
|
||||
descriptor to magnitude 1.
|
||||
|
||||
The default number of octaves has been increased from 3 to 4 to match the
|
||||
original SURF binary default. The increase in computation time is minimal since
|
||||
the higher octaves are sampled sparsely.
|
||||
|
||||
The default number of layers per octave has been reduced from 3 to 2, to prevent
|
||||
redundant calculation of similar sizes in consecutive octaves. This decreases
|
||||
computation time. The number of features extracted may be less, however the
|
||||
additional features were mostly redundant.
|
||||
|
||||
The radius of the circle of gradient samples used to assign an orientation has
|
||||
been increased from 4 to 6 to match the description in the SURF paper. This is
|
||||
now defined by ORI_RADIUS, and could be made into a parameter.
|
||||
|
||||
The size of the sliding window used in orientation assignment has been reduced
|
||||
from 120 to 60 degrees to match the description in the SURF paper. This is now
|
||||
defined by ORI_WIN, and could be made into a parameter.
|
||||
|
||||
Other options like HAAR_SIZE0, HAAR_SIZE_INC, SAMPLE_STEP0, ORI_SEARCH_INC,
|
||||
ORI_SIGMA and DESC_SIGMA have been separated from the code and documented.
|
||||
These could also be made into parameters.
|
||||
|
||||
Modifications by Ian Mahon
|
||||
|
||||
*/
|
||||
#include "precomp.hpp"
|
||||
|
||||
CvSURFParams cvSURFParams(double threshold, int extended)
|
||||
{
|
||||
CvSURFParams params;
|
||||
params.hessianThreshold = threshold;
|
||||
params.extended = extended;
|
||||
params.nOctaves = 4;
|
||||
params.nOctaveLayers = 2;
|
||||
return params;
|
||||
}
|
||||
|
||||
struct CvSurfHF
|
||||
{
|
||||
int p0, p1, p2, p3;
|
||||
float w;
|
||||
};
|
||||
|
||||
CV_INLINE float
|
||||
icvCalcHaarPattern( const int* origin, const CvSurfHF* f, int n )
|
||||
{
|
||||
double d = 0;
|
||||
for( int k = 0; k < n; k++ )
|
||||
d += (origin[f[k].p0] + origin[f[k].p3] - origin[f[k].p1] - origin[f[k].p2])*f[k].w;
|
||||
return (float)d;
|
||||
}
|
||||
|
||||
static void
|
||||
icvResizeHaarPattern( const int src[][5], CvSurfHF* dst, int n, int oldSize, int newSize, int widthStep )
|
||||
{
|
||||
float ratio = (float)newSize/oldSize;
|
||||
for( int k = 0; k < n; k++ )
|
||||
{
|
||||
int dx1 = cvRound( ratio*src[k][0] );
|
||||
int dy1 = cvRound( ratio*src[k][1] );
|
||||
int dx2 = cvRound( ratio*src[k][2] );
|
||||
int dy2 = cvRound( ratio*src[k][3] );
|
||||
dst[k].p0 = dy1*widthStep + dx1;
|
||||
dst[k].p1 = dy2*widthStep + dx1;
|
||||
dst[k].p2 = dy1*widthStep + dx2;
|
||||
dst[k].p3 = dy2*widthStep + dx2;
|
||||
dst[k].w = src[k][4]/((float)(dx2-dx1)*(dy2-dy1));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Maxima location interpolation as described in "Invariant Features from
|
||||
* Interest Point Groups" by Matthew Brown and David Lowe. This is performed by
|
||||
* fitting a 3D quadratic to a set of neighbouring samples.
|
||||
*
|
||||
* The gradient vector and Hessian matrix at the initial keypoint location are
|
||||
* approximated using central differences. The linear system Ax = b is then
|
||||
* solved, where A is the Hessian, b is the negative gradient, and x is the
|
||||
* offset of the interpolated maxima coordinates from the initial estimate.
|
||||
* This is equivalent to an iteration of Netwon's optimisation algorithm.
|
||||
*
|
||||
* N9 contains the samples in the 3x3x3 neighbourhood of the maxima
|
||||
* dx is the sampling step in x
|
||||
* dy is the sampling step in y
|
||||
* ds is the sampling step in size
|
||||
* point contains the keypoint coordinates and scale to be modified
|
||||
*
|
||||
* Return value is 1 if interpolation was successful, 0 on failure.
|
||||
*/
|
||||
CV_INLINE int
|
||||
icvInterpolateKeypoint( float N9[3][9], int dx, int dy, int ds, CvSURFPoint *point )
|
||||
{
|
||||
int solve_ok;
|
||||
float A[9], x[3], b[3];
|
||||
CvMat matA = cvMat(3, 3, CV_32F, A);
|
||||
CvMat _x = cvMat(3, 1, CV_32F, x);
|
||||
CvMat _b = cvMat(3, 1, CV_32F, b);
|
||||
|
||||
b[0] = -(N9[1][5]-N9[1][3])/2; /* Negative 1st deriv with respect to x */
|
||||
b[1] = -(N9[1][7]-N9[1][1])/2; /* Negative 1st deriv with respect to y */
|
||||
b[2] = -(N9[2][4]-N9[0][4])/2; /* Negative 1st deriv with respect to s */
|
||||
|
||||
A[0] = N9[1][3]-2*N9[1][4]+N9[1][5]; /* 2nd deriv x, x */
|
||||
A[1] = (N9[1][8]-N9[1][6]-N9[1][2]+N9[1][0])/4; /* 2nd deriv x, y */
|
||||
A[2] = (N9[2][5]-N9[2][3]-N9[0][5]+N9[0][3])/4; /* 2nd deriv x, s */
|
||||
A[3] = A[1]; /* 2nd deriv y, x */
|
||||
A[4] = N9[1][1]-2*N9[1][4]+N9[1][7]; /* 2nd deriv y, y */
|
||||
A[5] = (N9[2][7]-N9[2][1]-N9[0][7]+N9[0][1])/4; /* 2nd deriv y, s */
|
||||
A[6] = A[2]; /* 2nd deriv s, x */
|
||||
A[7] = A[5]; /* 2nd deriv s, y */
|
||||
A[8] = N9[0][4]-2*N9[1][4]+N9[2][4]; /* 2nd deriv s, s */
|
||||
|
||||
solve_ok = cvSolve( &matA, &_b, &_x );
|
||||
if( solve_ok )
|
||||
{
|
||||
point->pt.x += x[0]*dx;
|
||||
point->pt.y += x[1]*dy;
|
||||
point->size = cvRound( point->size + x[2]*ds );
|
||||
}
|
||||
return solve_ok;
|
||||
}
|
||||
|
||||
|
||||
/* Wavelet size at first layer of first octave. */
|
||||
const int HAAR_SIZE0 = 9;
|
||||
|
||||
/* Wavelet size increment between layers. This should be an even number,
|
||||
such that the wavelet sizes in an octave are either all even or all odd.
|
||||
This ensures that when looking for the neighbours of a sample, the layers
|
||||
above and below are aligned correctly. */
|
||||
const int HAAR_SIZE_INC = 6;
|
||||
|
||||
|
||||
static CvSeq* icvFastHessianDetector( const CvMat* sum, const CvMat* mask_sum,
|
||||
CvMemStorage* storage, const CvSURFParams* params )
|
||||
{
|
||||
CvSeq* points = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSURFPoint), storage );
|
||||
|
||||
/* Sampling step along image x and y axes at first octave. This is doubled
|
||||
for each additional octave. WARNING: Increasing this improves speed,
|
||||
however keypoint extraction becomes unreliable. */
|
||||
const int SAMPLE_STEP0 = 1;
|
||||
|
||||
|
||||
/* Wavelet Data */
|
||||
const int NX=3, NY=3, NXY=4, NM=1;
|
||||
const int dx_s[NX][5] = { {0, 2, 3, 7, 1}, {3, 2, 6, 7, -2}, {6, 2, 9, 7, 1} };
|
||||
const int dy_s[NY][5] = { {2, 0, 7, 3, 1}, {2, 3, 7, 6, -2}, {2, 6, 7, 9, 1} };
|
||||
const int dxy_s[NXY][5] = { {1, 1, 4, 4, 1}, {5, 1, 8, 4, -1}, {1, 5, 4, 8, -1}, {5, 5, 8, 8, 1} };
|
||||
const int dm[NM][5] = { {0, 0, 9, 9, 1} };
|
||||
CvSurfHF Dx[NX], Dy[NY], Dxy[NXY], Dm;
|
||||
|
||||
CvMat** dets = (CvMat**)cvStackAlloc((params->nOctaveLayers+2)*sizeof(dets[0]));
|
||||
CvMat** traces = (CvMat**)cvStackAlloc((params->nOctaveLayers+2)*sizeof(traces[0]));
|
||||
int *sizes = (int*)cvStackAlloc((params->nOctaveLayers+2)*sizeof(sizes[0]));
|
||||
|
||||
double dx = 0, dy = 0, dxy = 0;
|
||||
int octave, layer, sampleStep, size, margin;
|
||||
int rows, cols;
|
||||
int i, j, sum_i, sum_j;
|
||||
const int* s_ptr;
|
||||
float *det_ptr, *trace_ptr;
|
||||
|
||||
/* Allocate enough space for hessian determinant and trace matrices at the
|
||||
first octave. Clearing these initially or between octaves is not
|
||||
required, since all values that are accessed are first calculated */
|
||||
for( layer = 0; layer <= params->nOctaveLayers+1; layer++ )
|
||||
{
|
||||
dets[layer] = cvCreateMat( (sum->rows-1)/SAMPLE_STEP0, (sum->cols-1)/SAMPLE_STEP0, CV_32FC1 );
|
||||
traces[layer] = cvCreateMat( (sum->rows-1)/SAMPLE_STEP0, (sum->cols-1)/SAMPLE_STEP0, CV_32FC1 );
|
||||
}
|
||||
|
||||
for( octave = 0, sampleStep=SAMPLE_STEP0; octave < params->nOctaves; octave++, sampleStep*=2 )
|
||||
{
|
||||
/* Hessian determinant and trace sample array size in this octave */
|
||||
rows = (sum->rows-1)/sampleStep;
|
||||
cols = (sum->cols-1)/sampleStep;
|
||||
|
||||
/* Calculate the determinant and trace of the hessian */
|
||||
for( layer = 0; layer <= params->nOctaveLayers+1; layer++ )
|
||||
{
|
||||
sizes[layer] = size = (HAAR_SIZE0+HAAR_SIZE_INC*layer)<<octave;
|
||||
icvResizeHaarPattern( dx_s, Dx, NX, 9, size, sum->cols );
|
||||
icvResizeHaarPattern( dy_s, Dy, NY, 9, size, sum->cols );
|
||||
icvResizeHaarPattern( dxy_s, Dxy, NXY, 9, size, sum->cols );
|
||||
/*printf( "octave=%d layer=%d size=%d rows=%d cols=%d\n", octave, layer, size, rows, cols );*/
|
||||
|
||||
margin = (size/2)/sampleStep;
|
||||
for( sum_i=0, i=margin; sum_i<=(sum->rows-1)-size; sum_i+=sampleStep, i++ )
|
||||
{
|
||||
s_ptr = sum->data.i + sum_i*sum->cols;
|
||||
det_ptr = dets[layer]->data.fl + i*dets[layer]->cols + margin;
|
||||
trace_ptr = traces[layer]->data.fl + i*traces[layer]->cols + margin;
|
||||
for( sum_j=0, j=margin; sum_j<=(sum->cols-1)-size; sum_j+=sampleStep, j++ )
|
||||
{
|
||||
dx = icvCalcHaarPattern( s_ptr, Dx, 3 );
|
||||
dy = icvCalcHaarPattern( s_ptr, Dy, 3 );
|
||||
dxy = icvCalcHaarPattern( s_ptr, Dxy, 4 );
|
||||
s_ptr+=sampleStep;
|
||||
*det_ptr++ = (float)(dx*dy - 0.81*dxy*dxy);
|
||||
*trace_ptr++ = (float)(dx + dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Find maxima in the determinant of the hessian */
|
||||
for( layer = 1; layer <= params->nOctaveLayers; layer++ )
|
||||
{
|
||||
size = sizes[layer];
|
||||
icvResizeHaarPattern( dm, &Dm, NM, 9, size, mask_sum ? mask_sum->cols : sum->cols );
|
||||
|
||||
/* Ignore pixels without a 3x3 neighbourhood in the layer above */
|
||||
margin = (sizes[layer+1]/2)/sampleStep+1;
|
||||
for( i = margin; i < rows-margin; i++ )
|
||||
{
|
||||
det_ptr = dets[layer]->data.fl + i*dets[layer]->cols;
|
||||
trace_ptr = traces[layer]->data.fl + i*traces[layer]->cols;
|
||||
for( j = margin; j < cols-margin; j++ )
|
||||
{
|
||||
float val0 = det_ptr[j];
|
||||
if( val0 > params->hessianThreshold )
|
||||
{
|
||||
/* Coordinates for the start of the wavelet in the sum image. There
|
||||
is some integer division involved, so don't try to simplify this
|
||||
(cancel out sampleStep) without checking the result is the same */
|
||||
int sum_i = sampleStep*(i-(size/2)/sampleStep);
|
||||
int sum_j = sampleStep*(j-(size/2)/sampleStep);
|
||||
|
||||
/* The 3x3x3 neighbouring samples around the maxima.
|
||||
The maxima is included at N9[1][4] */
|
||||
int c = dets[layer]->cols;
|
||||
const float *det1 = dets[layer-1]->data.fl + i*c + j;
|
||||
const float *det2 = dets[layer]->data.fl + i*c + j;
|
||||
const float *det3 = dets[layer+1]->data.fl + i*c + j;
|
||||
float N9[3][9] = { { det1[-c-1], det1[-c], det1[-c+1],
|
||||
det1[-1] , det1[0] , det1[1],
|
||||
det1[c-1] , det1[c] , det1[c+1] },
|
||||
{ det2[-c-1], det2[-c], det2[-c+1],
|
||||
det2[-1] , det2[0] , det2[1],
|
||||
det2[c-1] , det2[c] , det2[c+1 ] },
|
||||
{ det3[-c-1], det3[-c], det3[-c+1],
|
||||
det3[-1 ], det3[0] , det3[1],
|
||||
det3[c-1] , det3[c] , det3[c+1 ] } };
|
||||
|
||||
/* Check the mask - why not just check the mask at the center of the wavelet? */
|
||||
if( mask_sum )
|
||||
{
|
||||
const int* mask_ptr = mask_sum->data.i + mask_sum->cols*sum_i + sum_j;
|
||||
float mval = icvCalcHaarPattern( mask_ptr, &Dm, 1 );
|
||||
if( mval < 0.5 )
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Non-maxima suppression. val0 is at N9[1][4]*/
|
||||
if( val0 > N9[0][0] && val0 > N9[0][1] && val0 > N9[0][2] &&
|
||||
val0 > N9[0][3] && val0 > N9[0][4] && val0 > N9[0][5] &&
|
||||
val0 > N9[0][6] && val0 > N9[0][7] && val0 > N9[0][8] &&
|
||||
val0 > N9[1][0] && val0 > N9[1][1] && val0 > N9[1][2] &&
|
||||
val0 > N9[1][3] && val0 > N9[1][5] &&
|
||||
val0 > N9[1][6] && val0 > N9[1][7] && val0 > N9[1][8] &&
|
||||
val0 > N9[2][0] && val0 > N9[2][1] && val0 > N9[2][2] &&
|
||||
val0 > N9[2][3] && val0 > N9[2][4] && val0 > N9[2][5] &&
|
||||
val0 > N9[2][6] && val0 > N9[2][7] && val0 > N9[2][8] )
|
||||
{
|
||||
/* Calculate the wavelet center coordinates for the maxima */
|
||||
double center_i = sum_i + (double)(size-1)/2;
|
||||
double center_j = sum_j + (double)(size-1)/2;
|
||||
|
||||
CvSURFPoint point = cvSURFPoint( cvPoint2D32f(center_j,center_i),
|
||||
CV_SIGN(trace_ptr[j]), sizes[layer], 0, val0 );
|
||||
|
||||
/* Interpolate maxima location within the 3x3x3 neighbourhood */
|
||||
int ds = sizes[layer]-sizes[layer-1];
|
||||
int interp_ok = icvInterpolateKeypoint( N9, sampleStep, sampleStep, ds, &point );
|
||||
|
||||
/* Sometimes the interpolation step gives a negative size etc. */
|
||||
if( interp_ok && point.size >= 1 &&
|
||||
point.pt.x >= 0 && point.pt.x <= (sum->cols-1) &&
|
||||
point.pt.y >= 0 && point.pt.y <= (sum->rows-1) )
|
||||
{
|
||||
/*printf( "KeyPoint %f %f %d\n", point.pt.x, point.pt.y, point.size );*/
|
||||
cvSeqPush( points, &point );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Clean-up */
|
||||
for( layer = 0; layer <= params->nOctaveLayers+1; layer++ )
|
||||
{
|
||||
cvReleaseMat( &dets[layer] );
|
||||
cvReleaseMat( &traces[layer] );
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
struct SURFInvoker
|
||||
{
|
||||
enum { ORI_RADIUS = 6, ORI_WIN = 60, PATCH_SZ = 20 };
|
||||
|
||||
static const int ORI_SEARCH_INC;
|
||||
static const float ORI_SIGMA;
|
||||
static const float DESC_SIGMA;
|
||||
|
||||
SURFInvoker( const CvSURFParams* _params,
|
||||
CvSeq* _keypoints, CvSeq* _descriptors,
|
||||
const CvMat* _img, const CvMat* _sum,
|
||||
const CvPoint* _apt, const float* _aptw,
|
||||
int _nangle0, const float* _DW )
|
||||
{
|
||||
params = _params;
|
||||
keypoints = _keypoints;
|
||||
descriptors = _descriptors;
|
||||
img = _img;
|
||||
sum = _sum;
|
||||
apt = _apt;
|
||||
aptw = _aptw;
|
||||
nangle0 = _nangle0;
|
||||
DW = _DW;
|
||||
}
|
||||
|
||||
void operator()(const BlockedRange& range) const
|
||||
{
|
||||
/* X and Y gradient wavelet data */
|
||||
const int NX=2, NY=2;
|
||||
int dx_s[NX][5] = {{0, 0, 2, 4, -1}, {2, 0, 4, 4, 1}};
|
||||
int dy_s[NY][5] = {{0, 0, 4, 2, 1}, {0, 2, 4, 4, -1}};
|
||||
|
||||
const int descriptor_size = params->extended ? 128 : 64;
|
||||
|
||||
const int max_ori_samples = (2*ORI_RADIUS+1)*(2*ORI_RADIUS+1);
|
||||
float X[max_ori_samples], Y[max_ori_samples], angle[max_ori_samples];
|
||||
uchar PATCH[PATCH_SZ+1][PATCH_SZ+1];
|
||||
float DX[PATCH_SZ][PATCH_SZ], DY[PATCH_SZ][PATCH_SZ];
|
||||
|
||||
CvMat matX = cvMat(1, max_ori_samples, CV_32F, X);
|
||||
CvMat matY = cvMat(1, max_ori_samples, CV_32F, Y);
|
||||
CvMat _angle = cvMat(1, max_ori_samples, CV_32F, angle);
|
||||
CvMat _patch = cvMat(PATCH_SZ+1, PATCH_SZ+1, CV_8U, PATCH);
|
||||
|
||||
int k, k1 = range.begin(), k2 = range.end();
|
||||
int maxSize = 0;
|
||||
|
||||
for( k = k1; k < k2; k++ )
|
||||
maxSize = std::max(maxSize, ((CvSURFPoint*)cvGetSeqElem( keypoints, k ))->size);
|
||||
|
||||
maxSize = cvCeil((PATCH_SZ+1)*maxSize*1.2f/9.0f);
|
||||
Ptr<CvMat> winbuf = cvCreateMat( 1, maxSize*maxSize, CV_8U );
|
||||
|
||||
for( k = k1; k < k2; k++ )
|
||||
{
|
||||
const int* sum_ptr = sum->data.i;
|
||||
int sum_cols = sum->cols;
|
||||
int i, j, kk, x, y, nangle;
|
||||
|
||||
float* vec;
|
||||
CvSurfHF dx_t[NX], dy_t[NY];
|
||||
|
||||
CvSURFPoint* kp = (CvSURFPoint*)cvGetSeqElem( keypoints, k );
|
||||
int size = kp->size;
|
||||
CvPoint2D32f center = kp->pt;
|
||||
|
||||
/* The sampling intervals and wavelet sized for selecting an orientation
|
||||
and building the keypoint descriptor are defined relative to 's' */
|
||||
float s = (float)size*1.2f/9.0f;
|
||||
|
||||
/* To find the dominant orientation, the gradients in x and y are
|
||||
sampled in a circle of radius 6s using wavelets of size 4s.
|
||||
We ensure the gradient wavelet size is even to ensure the
|
||||
wavelet pattern is balanced and symmetric around its center */
|
||||
int grad_wav_size = 2*cvRound( 2*s );
|
||||
if ( sum->rows < grad_wav_size || sum->cols < grad_wav_size )
|
||||
{
|
||||
/* when grad_wav_size is too big,
|
||||
* the sampling of gradient will be meaningless
|
||||
* mark keypoint for deletion. */
|
||||
kp->size = -1;
|
||||
continue;
|
||||
}
|
||||
icvResizeHaarPattern( dx_s, dx_t, NX, 4, grad_wav_size, sum->cols );
|
||||
icvResizeHaarPattern( dy_s, dy_t, NY, 4, grad_wav_size, sum->cols );
|
||||
for( kk = 0, nangle = 0; kk < nangle0; kk++ )
|
||||
{
|
||||
const int* ptr;
|
||||
float vx, vy;
|
||||
x = cvRound( center.x + apt[kk].x*s - (float)(grad_wav_size-1)/2 );
|
||||
y = cvRound( center.y + apt[kk].y*s - (float)(grad_wav_size-1)/2 );
|
||||
if( (unsigned)y >= (unsigned)(sum->rows - grad_wav_size) ||
|
||||
(unsigned)x >= (unsigned)(sum->cols - grad_wav_size) )
|
||||
continue;
|
||||
ptr = sum_ptr + x + y*sum_cols;
|
||||
vx = icvCalcHaarPattern( ptr, dx_t, 2 );
|
||||
vy = icvCalcHaarPattern( ptr, dy_t, 2 );
|
||||
X[nangle] = vx*aptw[kk]; Y[nangle] = vy*aptw[kk];
|
||||
nangle++;
|
||||
}
|
||||
if ( nangle == 0 )
|
||||
{
|
||||
/* No gradient could be sampled because the keypoint is too
|
||||
* near too one or more of the sides of the image. As we
|
||||
* therefore cannot find a dominant direction, we skip this
|
||||
* keypoint and mark it for later deletion from the sequence. */
|
||||
kp->size = -1;
|
||||
continue;
|
||||
}
|
||||
matX.cols = matY.cols = _angle.cols = nangle;
|
||||
cvCartToPolar( &matX, &matY, 0, &_angle, 1 );
|
||||
|
||||
float bestx = 0, besty = 0, descriptor_mod = 0;
|
||||
for( i = 0; i < 360; i += ORI_SEARCH_INC )
|
||||
{
|
||||
float sumx = 0, sumy = 0, temp_mod;
|
||||
for( j = 0; j < nangle; j++ )
|
||||
{
|
||||
int d = std::abs(cvRound(angle[j]) - i);
|
||||
if( d < ORI_WIN/2 || d > 360-ORI_WIN/2 )
|
||||
{
|
||||
sumx += X[j];
|
||||
sumy += Y[j];
|
||||
}
|
||||
}
|
||||
temp_mod = sumx*sumx + sumy*sumy;
|
||||
if( temp_mod > descriptor_mod )
|
||||
{
|
||||
descriptor_mod = temp_mod;
|
||||
bestx = sumx;
|
||||
besty = sumy;
|
||||
}
|
||||
}
|
||||
|
||||
float descriptor_dir = cvFastArctan( besty, bestx );
|
||||
kp->dir = descriptor_dir;
|
||||
|
||||
if( !descriptors )
|
||||
continue;
|
||||
|
||||
descriptor_dir *= (float)(CV_PI/180);
|
||||
|
||||
/* Extract a window of pixels around the keypoint of size 20s */
|
||||
int win_size = (int)((PATCH_SZ+1)*s);
|
||||
CV_Assert( winbuf->cols >= win_size*win_size );
|
||||
|
||||
CvMat win = cvMat(win_size, win_size, CV_8U, winbuf->data.ptr);
|
||||
float sin_dir = sin(descriptor_dir);
|
||||
float cos_dir = cos(descriptor_dir) ;
|
||||
|
||||
/* Subpixel interpolation version (slower). Subpixel not required since
|
||||
the pixels will all get averaged when we scale down to 20 pixels */
|
||||
/*
|
||||
float w[] = { cos_dir, sin_dir, center.x,
|
||||
-sin_dir, cos_dir , center.y };
|
||||
CvMat W = cvMat(2, 3, CV_32F, w);
|
||||
cvGetQuadrangleSubPix( img, &win, &W );
|
||||
*/
|
||||
|
||||
/* Nearest neighbour version (faster) */
|
||||
float win_offset = -(float)(win_size-1)/2;
|
||||
float start_x = center.x + win_offset*cos_dir + win_offset*sin_dir;
|
||||
float start_y = center.y - win_offset*sin_dir + win_offset*cos_dir;
|
||||
uchar* WIN = win.data.ptr;
|
||||
for( i = 0; i < win_size; i++, start_x += sin_dir, start_y += cos_dir )
|
||||
{
|
||||
float pixel_x = start_x;
|
||||
float pixel_y = start_y;
|
||||
for( j = 0; j < win_size; j++, pixel_x += cos_dir, pixel_y -= sin_dir )
|
||||
{
|
||||
int x = std::min(std::max(cvRound(pixel_x), 0), img->cols-1);
|
||||
int y = std::min(std::max(cvRound(pixel_y), 0), img->rows-1);
|
||||
WIN[i*win_size + j] = img->data.ptr[y*img->step + x];
|
||||
}
|
||||
}
|
||||
|
||||
/* Scale the window to size PATCH_SZ so each pixel's size is s. This
|
||||
makes calculating the gradients with wavelets of size 2s easy */
|
||||
cvResize( &win, &_patch, CV_INTER_AREA );
|
||||
|
||||
/* Calculate gradients in x and y with wavelets of size 2s */
|
||||
for( i = 0; i < PATCH_SZ; i++ )
|
||||
for( j = 0; j < PATCH_SZ; j++ )
|
||||
{
|
||||
float dw = DW[i*PATCH_SZ + j];
|
||||
float vx = (PATCH[i][j+1] - PATCH[i][j] + PATCH[i+1][j+1] - PATCH[i+1][j])*dw;
|
||||
float vy = (PATCH[i+1][j] - PATCH[i][j] + PATCH[i+1][j+1] - PATCH[i][j+1])*dw;
|
||||
DX[i][j] = vx;
|
||||
DY[i][j] = vy;
|
||||
}
|
||||
|
||||
/* Construct the descriptor */
|
||||
vec = (float*)cvGetSeqElem( descriptors, k );
|
||||
for( kk = 0; kk < (int)(descriptors->elem_size/sizeof(vec[0])); kk++ )
|
||||
vec[kk] = 0;
|
||||
double square_mag = 0;
|
||||
if( params->extended )
|
||||
{
|
||||
/* 128-bin descriptor */
|
||||
for( i = 0; i < 4; i++ )
|
||||
for( j = 0; j < 4; j++ )
|
||||
{
|
||||
for( y = i*5; y < i*5+5; y++ )
|
||||
{
|
||||
for( x = j*5; x < j*5+5; x++ )
|
||||
{
|
||||
float tx = DX[y][x], ty = DY[y][x];
|
||||
if( ty >= 0 )
|
||||
{
|
||||
vec[0] += tx;
|
||||
vec[1] += (float)fabs(tx);
|
||||
} else {
|
||||
vec[2] += tx;
|
||||
vec[3] += (float)fabs(tx);
|
||||
}
|
||||
if ( tx >= 0 )
|
||||
{
|
||||
vec[4] += ty;
|
||||
vec[5] += (float)fabs(ty);
|
||||
} else {
|
||||
vec[6] += ty;
|
||||
vec[7] += (float)fabs(ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
for( kk = 0; kk < 8; kk++ )
|
||||
square_mag += vec[kk]*vec[kk];
|
||||
vec += 8;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 64-bin descriptor */
|
||||
for( i = 0; i < 4; i++ )
|
||||
for( j = 0; j < 4; j++ )
|
||||
{
|
||||
for( y = i*5; y < i*5+5; y++ )
|
||||
{
|
||||
for( x = j*5; x < j*5+5; x++ )
|
||||
{
|
||||
float tx = DX[y][x], ty = DY[y][x];
|
||||
vec[0] += tx; vec[1] += ty;
|
||||
vec[2] += (float)fabs(tx); vec[3] += (float)fabs(ty);
|
||||
}
|
||||
}
|
||||
for( kk = 0; kk < 4; kk++ )
|
||||
square_mag += vec[kk]*vec[kk];
|
||||
vec+=4;
|
||||
}
|
||||
}
|
||||
|
||||
/* unit vector is essential for contrast invariance */
|
||||
vec = (float*)cvGetSeqElem( descriptors, k );
|
||||
double scale = 1./(sqrt(square_mag) + DBL_EPSILON);
|
||||
for( kk = 0; kk < descriptor_size; kk++ )
|
||||
vec[kk] = (float)(vec[kk]*scale);
|
||||
}
|
||||
}
|
||||
|
||||
const CvSURFParams* params;
|
||||
const CvMat* img;
|
||||
const CvMat* sum;
|
||||
CvSeq* keypoints;
|
||||
CvSeq* descriptors;
|
||||
const CvPoint* apt;
|
||||
const float* aptw;
|
||||
int nangle0;
|
||||
const float* DW;
|
||||
};
|
||||
|
||||
const int SURFInvoker::ORI_SEARCH_INC = 5;
|
||||
const float SURFInvoker::ORI_SIGMA = 2.5f;
|
||||
const float SURFInvoker::DESC_SIGMA = 3.3f;
|
||||
|
||||
}
|
||||
|
||||
|
||||
CV_IMPL void
|
||||
cvExtractSURF( const CvArr* _img, const CvArr* _mask,
|
||||
CvSeq** _keypoints, CvSeq** _descriptors,
|
||||
CvMemStorage* storage, CvSURFParams params,
|
||||
int useProvidedKeyPts)
|
||||
{
|
||||
const int ORI_RADIUS = cv::SURFInvoker::ORI_RADIUS;
|
||||
const float ORI_SIGMA = cv::SURFInvoker::ORI_SIGMA;
|
||||
const float DESC_SIGMA = cv::SURFInvoker::DESC_SIGMA;
|
||||
|
||||
CvMat *sum = 0, *mask1 = 0, *mask_sum = 0;
|
||||
|
||||
if( _keypoints && !useProvidedKeyPts ) // If useProvidedKeyPts!=0 we'll use current contents of "*_keypoints"
|
||||
*_keypoints = 0;
|
||||
if( _descriptors )
|
||||
*_descriptors = 0;
|
||||
|
||||
CvSeq *keypoints, *descriptors = 0;
|
||||
CvMat imghdr, *img = cvGetMat(_img, &imghdr);
|
||||
CvMat maskhdr, *mask = _mask ? cvGetMat(_mask, &maskhdr) : 0;
|
||||
|
||||
const int max_ori_samples = (2*ORI_RADIUS+1)*(2*ORI_RADIUS+1);
|
||||
int descriptor_size = params.extended ? 128 : 64;
|
||||
const int descriptor_data_type = CV_32F;
|
||||
const int PATCH_SZ = 20;
|
||||
float DW[PATCH_SZ][PATCH_SZ];
|
||||
CvMat _DW = cvMat(PATCH_SZ, PATCH_SZ, CV_32F, DW);
|
||||
CvPoint apt[max_ori_samples];
|
||||
float aptw[max_ori_samples];
|
||||
int i, j, nangle0 = 0, N;
|
||||
|
||||
CV_Assert(img != 0);
|
||||
CV_Assert(CV_MAT_TYPE(img->type) == CV_8UC1);
|
||||
CV_Assert(mask == 0 || (CV_ARE_SIZES_EQ(img,mask) && CV_MAT_TYPE(mask->type) == CV_8UC1));
|
||||
CV_Assert(storage != 0);
|
||||
CV_Assert(params.hessianThreshold >= 0);
|
||||
CV_Assert(params.nOctaves > 0);
|
||||
CV_Assert(params.nOctaveLayers > 0);
|
||||
|
||||
sum = cvCreateMat( img->rows+1, img->cols+1, CV_32SC1 );
|
||||
cvIntegral( img, sum );
|
||||
|
||||
// Compute keypoints only if we are not asked for evaluating the descriptors are some given locations:
|
||||
if (!useProvidedKeyPts)
|
||||
{
|
||||
if( mask )
|
||||
{
|
||||
mask1 = cvCreateMat( img->height, img->width, CV_8UC1 );
|
||||
mask_sum = cvCreateMat( img->height+1, img->width+1, CV_32SC1 );
|
||||
cvMinS( mask, 1, mask1 );
|
||||
cvIntegral( mask1, mask_sum );
|
||||
}
|
||||
keypoints = icvFastHessianDetector( sum, mask_sum, storage, ¶ms );
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Assert(useProvidedKeyPts && (_keypoints != 0) && (*_keypoints != 0));
|
||||
keypoints = *_keypoints;
|
||||
}
|
||||
|
||||
N = keypoints->total;
|
||||
if( _descriptors )
|
||||
{
|
||||
descriptors = cvCreateSeq( 0, sizeof(CvSeq),
|
||||
descriptor_size*CV_ELEM_SIZE(descriptor_data_type), storage );
|
||||
cvSeqPushMulti( descriptors, 0, N );
|
||||
}
|
||||
|
||||
/* Coordinates and weights of samples used to calculate orientation */
|
||||
cv::Mat matG = cv::getGaussianKernel( 2*ORI_RADIUS+1, ORI_SIGMA, CV_32F );
|
||||
const float* G = (const float*)matG.data;
|
||||
|
||||
for( i = -ORI_RADIUS; i <= ORI_RADIUS; i++ )
|
||||
{
|
||||
for( j = -ORI_RADIUS; j <= ORI_RADIUS; j++ )
|
||||
{
|
||||
if( i*i + j*j <= ORI_RADIUS*ORI_RADIUS )
|
||||
{
|
||||
apt[nangle0] = cvPoint(j,i);
|
||||
aptw[nangle0++] = G[i+ORI_RADIUS]*G[j+ORI_RADIUS];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Gaussian used to weight descriptor samples */
|
||||
double c2 = 1./(DESC_SIGMA*DESC_SIGMA*2);
|
||||
double gs = 0;
|
||||
for( i = 0; i < PATCH_SZ; i++ )
|
||||
{
|
||||
for( j = 0; j < PATCH_SZ; j++ )
|
||||
{
|
||||
double x = j - (float)(PATCH_SZ-1)/2, y = i - (float)(PATCH_SZ-1)/2;
|
||||
double val = exp(-(x*x+y*y)*c2);
|
||||
DW[i][j] = (float)val;
|
||||
gs += val;
|
||||
}
|
||||
}
|
||||
cvScale( &_DW, &_DW, 1./gs );
|
||||
|
||||
cv::parallel_for(cv::BlockedRange(0, N),
|
||||
cv::SURFInvoker(¶ms, keypoints, descriptors, img, sum,
|
||||
apt, aptw, nangle0, &DW[0][0]));
|
||||
//cv::SURFInvoker(¶ms, keypoints, descriptors, img, sum,
|
||||
// apt, aptw, nangle0, &DW[0][0])(cv::BlockedRange(0, N));
|
||||
|
||||
/* remove keypoints that were marked for deletion */
|
||||
for ( i = 0; i < N; i++ )
|
||||
{
|
||||
CvSURFPoint* kp = (CvSURFPoint*)cvGetSeqElem( keypoints, i );
|
||||
if ( kp->size == -1 )
|
||||
{
|
||||
cvSeqRemove( keypoints, i );
|
||||
if ( _descriptors )
|
||||
cvSeqRemove( descriptors, i );
|
||||
i--;
|
||||
N--;
|
||||
}
|
||||
}
|
||||
|
||||
if( _keypoints && !useProvidedKeyPts )
|
||||
*_keypoints = keypoints;
|
||||
if( _descriptors )
|
||||
*_descriptors = descriptors;
|
||||
|
||||
cvReleaseMat( &sum );
|
||||
if (mask1) cvReleaseMat( &mask1 );
|
||||
if (mask_sum) cvReleaseMat( &mask_sum );
|
||||
}
|
||||
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
SURF::SURF()
|
||||
{
|
||||
hessianThreshold = 100;
|
||||
extended = 1;
|
||||
nOctaves = 4;
|
||||
nOctaveLayers = 2;
|
||||
}
|
||||
|
||||
SURF::SURF(double _threshold, int _nOctaves, int _nOctaveLayers, bool _extended)
|
||||
{
|
||||
hessianThreshold = _threshold;
|
||||
extended = _extended;
|
||||
nOctaves = _nOctaves;
|
||||
nOctaveLayers = _nOctaveLayers;
|
||||
}
|
||||
|
||||
int SURF::descriptorSize() const { return extended ? 128 : 64; }
|
||||
|
||||
|
||||
static int getPointOctave(const CvSURFPoint& kpt, const CvSURFParams& params)
|
||||
{
|
||||
int octave = 0, layer = 0, best_octave = 0;
|
||||
float min_diff = FLT_MAX;
|
||||
for( octave = 1; octave < params.nOctaves; octave++ )
|
||||
for( layer = 0; layer < params.nOctaveLayers; layer++ )
|
||||
{
|
||||
float diff = std::abs(kpt.size - (float)((HAAR_SIZE0 + HAAR_SIZE_INC*layer) << octave));
|
||||
if( min_diff > diff )
|
||||
{
|
||||
min_diff = diff;
|
||||
best_octave = octave;
|
||||
if( min_diff == 0 )
|
||||
return best_octave;
|
||||
}
|
||||
}
|
||||
return best_octave;
|
||||
}
|
||||
|
||||
|
||||
void SURF::operator()(const Mat& img, const Mat& mask,
|
||||
vector<KeyPoint>& keypoints) const
|
||||
{
|
||||
CvMat _img = img, _mask, *pmask = 0;
|
||||
if( mask.data )
|
||||
pmask = &(_mask = mask);
|
||||
MemStorage storage(cvCreateMemStorage(0));
|
||||
Seq<CvSURFPoint> kp;
|
||||
cvExtractSURF(&_img, pmask, &kp.seq, 0, storage, *(const CvSURFParams*)this, 0);
|
||||
Seq<CvSURFPoint>::iterator it = kp.begin();
|
||||
size_t i, n = kp.size();
|
||||
keypoints.resize(n);
|
||||
for( i = 0; i < n; i++, ++it )
|
||||
{
|
||||
const CvSURFPoint& kpt = *it;
|
||||
keypoints[i] = KeyPoint(kpt.pt, (float)kpt.size, kpt.dir,
|
||||
kpt.hessian, getPointOctave(kpt, *this));
|
||||
}
|
||||
}
|
||||
|
||||
void SURF::operator()(const Mat& img, const Mat& mask,
|
||||
vector<KeyPoint>& keypoints,
|
||||
vector<float>& descriptors,
|
||||
bool useProvidedKeypoints) const
|
||||
{
|
||||
CvMat _img = img, _mask, *pmask = 0;
|
||||
if( mask.data )
|
||||
pmask = &(_mask = mask);
|
||||
MemStorage storage(cvCreateMemStorage(0));
|
||||
Seq<CvSURFPoint> kp;
|
||||
CvSeq* d = 0;
|
||||
size_t i, n;
|
||||
if( useProvidedKeypoints )
|
||||
{
|
||||
kp = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSURFPoint), storage);
|
||||
n = keypoints.size();
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
const KeyPoint& kpt = keypoints[i];
|
||||
kp.push_back(cvSURFPoint(kpt.pt, 1, cvRound(kpt.size), kpt.angle, kpt.response));
|
||||
}
|
||||
}
|
||||
|
||||
cvExtractSURF(&_img, pmask, &kp.seq, &d, storage,
|
||||
*(const CvSURFParams*)this, useProvidedKeypoints);
|
||||
if( !useProvidedKeypoints )
|
||||
{
|
||||
Seq<CvSURFPoint>::iterator it = kp.begin();
|
||||
size_t i, n = kp.size();
|
||||
keypoints.resize(n);
|
||||
for( i = 0; i < n; i++, ++it )
|
||||
{
|
||||
const CvSURFPoint& kpt = *it;
|
||||
keypoints[i] = KeyPoint(kpt.pt, (float)kpt.size, kpt.dir,
|
||||
kpt.hessian, getPointOctave(kpt, *this));
|
||||
}
|
||||
}
|
||||
descriptors.resize(d ? d->total*d->elem_size/sizeof(float) : 0);
|
||||
if(descriptors.size() != 0)
|
||||
cvCvtSeqToArray(d, &descriptors[0]);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user