Commit e406dfee authored by Maria Dimashova's avatar Maria Dimashova

refactored features2d and sample on matching to many images

parent cf0d9da6
......@@ -1231,34 +1231,34 @@ protected:
class CV_EXPORTS FeatureDetector
{
public:
virtual ~FeatureDetector() {}
virtual ~FeatureDetector();
/*
* Detect keypoints in an image. Must be implemented by the subclass.
*
* Detect keypoints in an image.
* image The image.
* keypoints The detected keypoints.
* mask Mask specifying where to look for keypoints (optional). Must be a char
* matrix with non-zero values in the region of interest.
*/
virtual void detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const = 0;
void detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
/*
* Detect keypoints in an image set.
*
* images Image collection.
* pointCollection Collection of keypoints detected in an input images.
* masks Masks for each input image.
* images Image collection.
* keypoints Collection of keypoints detected in an input images. keypoints[i] is a set of keypoints detected in an images[i].
* masks Masks for image set. masks[i] is a mask for images[i].
*/
void detect( const vector<Mat>& imageCollection, vector<vector<KeyPoint> >& pointCollection, const vector<Mat>& masks=vector<Mat>() ) const;
void detect( const vector<Mat>& images, vector<vector<KeyPoint> >& keypoints, const vector<Mat>& masks=vector<Mat>() ) const;
virtual void read( const FileNode& ) {}
virtual void write( FileStorage& ) const {}
// Read detector object from a file node
virtual void read( const FileNode& );
// Read detector object from a file node
virtual void write( FileStorage& ) const;
protected:
virtual void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const = 0;
/*
* Remove keypoints that are not in the mask.
*
* Helper function, useful when wrapping a library call for keypoint detection that
* does not support a mask argument.
*/
......@@ -1268,13 +1268,13 @@ protected:
class CV_EXPORTS FastFeatureDetector : public FeatureDetector
{
public:
FastFeatureDetector( int _threshold=1, bool _nonmaxSuppression=true );
virtual void detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
FastFeatureDetector( int threshold=10, bool nonmaxSuppression=true );
virtual void read( const FileNode& fn );
virtual void write( FileStorage& fs ) const;
protected:
virtual void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
int threshold;
bool nonmaxSuppression;
};
......@@ -1283,102 +1283,123 @@ protected:
class CV_EXPORTS GoodFeaturesToTrackDetector : public FeatureDetector
{
public:
GoodFeaturesToTrackDetector( int _maxCorners, double _qualityLevel, double _minDistance,
int _blockSize=3, bool _useHarrisDetector=false, double _k=0.04 );
virtual void detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
class CV_EXPORTS Params
{
public:
Params( int maxCorners=1000, double qualityLevel=0.01, double minDistance=1.,
int blockSize=3, bool useHarrisDetector=false, double k=0.04 );
void read( const FileNode& fn );
void write( FileStorage& fs ) const;
int maxCorners;
double qualityLevel;
double minDistance;
int blockSize;
bool useHarrisDetector;
double k;
};
GoodFeaturesToTrackDetector( const GoodFeaturesToTrackDetector::Params& params=GoodFeaturesToTrackDetector::Params() );
GoodFeaturesToTrackDetector( int maxCorners, double qualityLevel, double minDistance,
int blockSize=3, bool useHarrisDetector=false, double k=0.04 );
virtual void read( const FileNode& fn );
virtual void write( FileStorage& fs ) const;
protected:
int maxCorners;
double qualityLevel;
double minDistance;
int blockSize;
bool useHarrisDetector;
double k;
virtual void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
Params params;
};
class CV_EXPORTS MserFeatureDetector : public FeatureDetector
{
public:
MserFeatureDetector( CvMSERParams params=cvMSERParams () );
MserFeatureDetector( CvMSERParams params=cvMSERParams() );
MserFeatureDetector( int delta, int minArea, int maxArea, double maxVariation, double minDiversity,
int maxEvolution, double areaThreshold, double minMargin, int edgeBlurSize );
virtual void detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
virtual void read( const FileNode& fn );
virtual void write( FileStorage& fs ) const;
protected:
virtual void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
MSER mser;
};
class CV_EXPORTS StarFeatureDetector : public FeatureDetector
{
public:
StarFeatureDetector( int maxSize=16, int responseThreshold=30, int lineThresholdProjected = 10,
StarFeatureDetector( const CvStarDetectorParams& params=cvStarDetectorParams() );
StarFeatureDetector( int maxSize, int responseThreshold=30, int lineThresholdProjected = 10,
int lineThresholdBinarized=8, int suppressNonmaxSize=5 );
virtual void detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
virtual void read( const FileNode& fn );
virtual void write( FileStorage& fs ) const;
protected:
virtual void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
StarDetector star;
};
class CV_EXPORTS SiftFeatureDetector : public FeatureDetector
{
public:
SiftFeatureDetector( double threshold=SIFT::DetectorParams::GET_DEFAULT_THRESHOLD(),
double edgeThreshold=SIFT::DetectorParams::GET_DEFAULT_EDGE_THRESHOLD(),
SiftFeatureDetector( const SIFT::DetectorParams& detectorParams=SIFT::DetectorParams(),
const SIFT::CommonParams& commonParams=SIFT::CommonParams() );
SiftFeatureDetector( double threshold, double edgeThreshold,
int nOctaves=SIFT::CommonParams::DEFAULT_NOCTAVES,
int nOctaveLayers=SIFT::CommonParams::DEFAULT_NOCTAVE_LAYERS,
int firstOctave=SIFT::CommonParams::DEFAULT_FIRST_OCTAVE,
int angleMode=SIFT::CommonParams::FIRST_ANGLE );
virtual void detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
virtual void read( const FileNode& fn );
virtual void write( FileStorage& fs ) const;
protected:
virtual void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
SIFT sift;
};
class CV_EXPORTS SurfFeatureDetector : public FeatureDetector
{
public:
SurfFeatureDetector( double hessianThreshold = 400., int octaves = 3, int octaveLayers = 4 );
virtual void detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
SurfFeatureDetector( double hessianThreshold=400., int octaves=3, int octaveLayers=4 );
virtual void read( const FileNode& fn );
virtual void write( FileStorage& fs ) const;
protected:
virtual void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
SURF surf;
};
class CV_EXPORTS DenseFeatureDetector : public FeatureDetector
{
public:
DenseFeatureDetector() : initFeatureScale(1), featureScaleLevels(1), featureScaleMul(0.1f),
initXyStep(6), initImgBound(0), varyXyStepWithScale(true), varyImgBoundWithScale(false) {}
DenseFeatureDetector( float _initFeatureScale, int _featureScaleLevels=1, float _featureScaleMul=0.1f,
int _initXyStep=6, int _initImgBound=0, bool _varyXyStepWithScale=true, bool _varyImgBoundWithScale=false ) :
initFeatureScale(_initFeatureScale), featureScaleLevels(_featureScaleLevels), featureScaleMul(_featureScaleMul),
initXyStep(_initXyStep), initImgBound(_initImgBound), varyXyStepWithScale(_varyXyStepWithScale), varyImgBoundWithScale(_varyImgBoundWithScale) {}
virtual void detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
// todo read/write
protected:
float initFeatureScale;
int featureScaleLevels;
float featureScaleMul;
class CV_EXPORTS Params
{
public:
Params( float initFeatureScale=1.f, int featureScaleLevels=1, float featureScaleMul=0.1f,
int initXyStep=6, int initImgBound=0, bool varyXyStepWithScale=true, bool varyImgBoundWithScale=false );
float initFeatureScale;
int featureScaleLevels;
float featureScaleMul;
int initXyStep;
int initImgBound;
bool varyXyStepWithScale;
bool varyImgBoundWithScale;
};
DenseFeatureDetector( const DenseFeatureDetector::Params& params=DenseFeatureDetector::Params() );
// TODO implement read/write
int initXyStep;
int initImgBound;
protected:
virtual void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
bool varyXyStepWithScale;
bool varyImgBoundWithScale;
Params params;
};
/*
......@@ -1397,13 +1418,12 @@ public:
*/
GridAdaptedFeatureDetector( const Ptr<FeatureDetector>& detector, int maxTotalKeypoints,
int gridRows=4, int gridCols=4 );
virtual void detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
// todo read/write
virtual void read( const FileNode& ) {}
virtual void write( FileStorage& ) const {}
// TODO implement read/write
protected:
virtual void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
Ptr<FeatureDetector> detector;
int maxTotalKeypoints;
int gridRows;
......@@ -1418,13 +1438,12 @@ class CV_EXPORTS PyramidAdaptedFeatureDetector : public FeatureDetector
{
public:
PyramidAdaptedFeatureDetector( const Ptr<FeatureDetector>& detector, int levels=2 );
virtual void detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
// todo read/write
virtual void read( const FileNode& ) {}
virtual void write( FileStorage& ) const {}
// TODO implement read/write
protected:
virtual void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;
Ptr<FeatureDetector> detector;
int levels;
};
......@@ -1450,38 +1469,39 @@ CV_EXPORTS Ptr<FeatureDetector> createFeatureDetector( const string& detectorTyp
class CV_EXPORTS DescriptorExtractor
{
public:
virtual ~DescriptorExtractor() {}
virtual ~DescriptorExtractor();
/*
* Compute the descriptors for a set of keypoints in an image.
* Must be implemented by the subclass.
*
* image The image.
* keypoints The keypoints. Keypoints for which a descriptor cannot be computed are removed.
* descriptors The descriptors. Row i is the descriptor for keypoint i.
* keypoints The input keypoints. Keypoints for which a descriptor cannot be computed are removed.
* descriptors Copmputed descriptors. Row i is the descriptor for keypoint i.
*/
virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const = 0;
void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;
/*
* Compute the descriptors for a keypoints collection detected in image collection.
*
* imageCollection Image collection.
* pointCollection Keypoints collection. pointCollection[i] is keypoints detected in imageCollection[i].
* descCollection Descriptor collection. descCollection[i] is descriptors computed for pointCollection[i].
* images Image collection.
* keypoints Input keypoints collection. keypoints[i] is keypoints detected in images[i].
* Keypoints for which a descriptor cannot be computed are removed.
* descriptors Descriptor collection. descriptors[i] is descriptors computed for keypoints[i].
*/
void compute( const vector<Mat>& imageCollection, vector<vector<KeyPoint> >& pointCollection, vector<Mat>& descCollection ) const;
void compute( const vector<Mat>& images, vector<vector<KeyPoint> >& keypoints, vector<Mat>& descriptors ) const;
virtual void read( const FileNode& ) {}
virtual void write( FileStorage& ) const {}
virtual void read( const FileNode& );
virtual void write( FileStorage& ) const;
virtual int descriptorSize() const = 0;
virtual int descriptorType() const = 0;
protected:
virtual void computeImpl( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const = 0;
/*
* Remove keypoints within border_pixels of an image edge.
* Remove keypoints within borderPixels of an image edge.
*/
static void removeBorderKeypoints( vector<KeyPoint>& keypoints,
Size imageSize, int borderPixels );
Size imageSize, int borderSize );
};
/*
......@@ -1490,21 +1510,23 @@ protected:
class CV_EXPORTS SiftDescriptorExtractor : public DescriptorExtractor
{
public:
SiftDescriptorExtractor( double magnification=SIFT::DescriptorParams::GET_DEFAULT_MAGNIFICATION(),
bool isNormalize=true, bool recalculateAngles=true,
SiftDescriptorExtractor( const SIFT::DescriptorParams& descriptorParams=SIFT::DescriptorParams(),
const SIFT::CommonParams& commonParams=SIFT::CommonParams() );
SiftDescriptorExtractor( double magnification, bool isNormalize=true, bool recalculateAngles=true,
int nOctaves=SIFT::CommonParams::DEFAULT_NOCTAVES,
int nOctaveLayers=SIFT::CommonParams::DEFAULT_NOCTAVE_LAYERS,
int firstOctave=SIFT::CommonParams::DEFAULT_FIRST_OCTAVE,
int angleMode=SIFT::CommonParams::FIRST_ANGLE );
virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;
virtual void read( const FileNode &fn );
virtual void write( FileStorage &fs ) const;
virtual int descriptorSize() const { return sift.descriptorSize(); }
virtual int descriptorType() const { return CV_32FC1; }
virtual int descriptorSize() const;
virtual int descriptorType() const;
protected:
virtual void computeImpl( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;
SIFT sift;
};
......@@ -1514,17 +1536,17 @@ protected:
class CV_EXPORTS SurfDescriptorExtractor : public DescriptorExtractor
{
public:
SurfDescriptorExtractor( int nOctaves=4,
int nOctaveLayers=2, bool extended=false );
SurfDescriptorExtractor( int nOctaves=4, int nOctaveLayers=2, bool extended=false );
virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;
virtual void read( const FileNode &fn );
virtual void write( FileStorage &fs ) const;
virtual int descriptorSize() const { return surf.descriptorSize(); }
virtual int descriptorType() const { return CV_32FC1; }
virtual int descriptorSize() const;
virtual int descriptorType() const;
protected:
virtual void computeImpl( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;
SURF surf;
};
......@@ -1537,7 +1559,6 @@ class CV_EXPORTS CalonderDescriptorExtractor : public DescriptorExtractor
public:
CalonderDescriptorExtractor( const string& classifierFile );
virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;
virtual void read( const FileNode &fn );
virtual void write( FileStorage &fs ) const;
......@@ -1545,6 +1566,8 @@ public:
virtual int descriptorType() const { return DataType<T>::type; }
protected:
virtual void computeImpl( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;
RTreeClassifier classifier_;
static const int BORDER_SIZE = 16;
};
......@@ -1556,23 +1579,24 @@ CalonderDescriptorExtractor<T>::CalonderDescriptorExtractor(const std::string& c
}
template<typename T>
void CalonderDescriptorExtractor<T>::compute( const cv::Mat& image,
void CalonderDescriptorExtractor<T>::computeImpl( const cv::Mat& image,
std::vector<cv::KeyPoint>& keypoints,
cv::Mat& descriptors) const
{
// Cannot compute descriptors for keypoints on the image border.
removeBorderKeypoints(keypoints, image.size(), BORDER_SIZE);
// Cannot compute descriptors for keypoints on the image border.
removeBorderKeypoints(keypoints, image.size(), BORDER_SIZE);
/// @todo Check 16-byte aligned
descriptors.create(keypoints.size(), classifier_.classes(), cv::DataType<T>::type);
/// @todo Check 16-byte aligned
descriptors.create(keypoints.size(), classifier_.classes(), cv::DataType<T>::type);
int patchSize = RandomizedTree::PATCH_SIZE;
int offset = patchSize / 2;
for (size_t i = 0; i < keypoints.size(); ++i) {
cv::Point2f pt = keypoints[i].pt;
IplImage ipl = image( Rect((int)(pt.x - offset), (int)(pt.y - offset), patchSize, patchSize) );
classifier_.getSignature( &ipl, descriptors.ptr<T>(i));
}
int patchSize = RandomizedTree::PATCH_SIZE;
int offset = patchSize / 2;
for (size_t i = 0; i < keypoints.size(); ++i)
{
cv::Point2f pt = keypoints[i].pt;
IplImage ipl = image( Rect((int)(pt.x - offset), (int)(pt.y - offset), patchSize, patchSize) );
classifier_.getSignature( &ipl, descriptors.ptr<T>(i));
}
}
template<typename T>
......@@ -1595,18 +1619,18 @@ void CalonderDescriptorExtractor<T>::write( FileStorage& ) const
class CV_EXPORTS OpponentColorDescriptorExtractor : public DescriptorExtractor
{
public:
OpponentColorDescriptorExtractor( const Ptr<DescriptorExtractor>& dextractor );
virtual void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;
OpponentColorDescriptorExtractor( const Ptr<DescriptorExtractor>& descriptorExtractor );
virtual void read( const FileNode& );
virtual void write( FileStorage& ) const;
virtual int descriptorSize() const { return 3*dextractor->descriptorSize(); }
virtual int descriptorType() const { return dextractor->descriptorType(); }
virtual int descriptorSize() const;
virtual int descriptorType() const;
protected:
Ptr<DescriptorExtractor> dextractor;
virtual void computeImpl( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;
Ptr<DescriptorExtractor> descriptorExtractor;
};
/*
......@@ -1615,98 +1639,24 @@ protected:
class CV_EXPORTS BriefDescriptorExtractor : public DescriptorExtractor
{
public:
BriefDescriptorExtractor(int bytes = 32);
static const int PATCH_SIZE = 48;
static const int KERNEL_SIZE = 9;
virtual void compute(const Mat& image, std::vector<KeyPoint>& keypoints, Mat& descriptors) const;
BriefDescriptorExtractor(int bytes = 32);
virtual int descriptorSize() const
{
return bytes_;
}
virtual int descriptorType() const
{
return CV_8UC1;
}
virtual int descriptorSize() const;
virtual int descriptorType() const;
/// @todo read and write for brief
//virtual void read(const FileNode& fn);
//virtual void write(FileStorage& fs) const;
/// @todo read and write for brief
protected:
static const int PATCH_SIZE = 48;
static const int KERNEL_SIZE = 9;
int bytes_;
typedef void(*PixelTestFn)(const Mat&, const std::vector<KeyPoint>&, Mat&);
PixelTestFn test_fn_;
static int smoothedSum(const Mat& sum, const KeyPoint& pt, int y, int x);
static void pixelTests16(const Mat& sum, const std::vector<KeyPoint>& keypoints, Mat& descriptors);
static void pixelTests32(const Mat& sum, const std::vector<KeyPoint>& keypoints, Mat& descriptors);
static void pixelTests64(const Mat& sum, const std::vector<KeyPoint>& keypoints, Mat& descriptors);
};
inline int BriefDescriptorExtractor::smoothedSum(const Mat& sum, const KeyPoint& pt, int y, int x)
{
static const int HALF_KERNEL = KERNEL_SIZE / 2;
int img_y = (int)(pt.pt.y + 0.5) + y;
int img_x = (int)(pt.pt.x + 0.5) + x;
return sum.at<int> (img_y + HALF_KERNEL + 1, img_x + HALF_KERNEL + 1) - sum.at<int> (img_y + HALF_KERNEL + 1,
img_x - HALF_KERNEL)
- sum.at<int> (img_y - HALF_KERNEL, img_x + HALF_KERNEL + 1) + sum.at<int> (img_y - HALF_KERNEL, img_x
- HALF_KERNEL);
}
struct CV_EXPORTS HammingLUT
{
typedef unsigned char ValueType;
typedef int ResultType;
ResultType operator()(const unsigned char* a, const unsigned char* b, int size) const
{
ResultType result = 0;
for (int i = 0; i < size; i++)
{
result += byteBitsLookUp(a[i] ^ b[i]);
}
return result;
}
/** \brief given a byte, count the bits using a compile time generated look up table
* \param b the byte to count bits. The look up table has an entry for all
* values of b, where that entry is the number of bits.
* \return the number of bits in byte b
*/
static unsigned char byteBitsLookUp(unsigned char b);
};
virtual void computeImpl(const Mat& image, std::vector<KeyPoint>& keypoints, Mat& descriptors) const;
#if __GNUC__
/// Hamming distance functor
/// @todo Variable-length version, maybe default size=0 and specialize
/// @todo Need to choose C/SSE4 at runtime, but amortize this at matcher level for efficiency...
//template<int size>
struct Hamming
{
typedef unsigned char ValueType;
typedef int ResultType;
typedef void(*PixelTestFn)(const Mat&, const std::vector<KeyPoint>&, Mat&);
ResultType operator()(const unsigned char* a, const unsigned char* b, int size) const
{
/// @todo Non-GCC-specific version
ResultType result = 0;
for (int i = 0; i < size; i += sizeof(unsigned long))
{
unsigned long a2 = *reinterpret_cast<const unsigned long*> (a + i);
unsigned long b2 = *reinterpret_cast<const unsigned long*> (b + i);
result += __builtin_popcountl(a2 ^ b2);
}
return result;
}
int bytes_;
PixelTestFn test_fn_;
};
#else
typedef HammingLUT Hamming;
#endif
CV_EXPORTS Ptr<DescriptorExtractor> createDescriptorExtractor( const string& descriptorExtractorType );
......@@ -1766,6 +1716,57 @@ struct CV_EXPORTS L1
}
};
/*
* Hamming distance (city block distance) functor
*/
struct CV_EXPORTS HammingLUT
{
typedef unsigned char ValueType;
typedef int ResultType;
ResultType operator()( const unsigned char* a, const unsigned char* b, int size ) const
{
ResultType result = 0;
for (int i = 0; i < size; i++)
{
result += byteBitsLookUp(a[i] ^ b[i]);
}
return result;
}
/** \brief given a byte, count the bits using a compile time generated look up table
* \param b the byte to count bits. The look up table has an entry for all
* values of b, where that entry is the number of bits.
* \return the number of bits in byte b
*/
static unsigned char byteBitsLookUp(unsigned char b);
};
#if __GNUC__
/// Hamming distance functor
/// @todo Variable-length version, maybe default size=0 and specialize
/// @todo Need to choose C/SSE4 at runtime, but amortize this at matcher level for efficiency...
struct Hamming
{
typedef unsigned char ValueType;
typedef int ResultType;
ResultType operator()(const unsigned char* a, const unsigned char* b, int size) const
{
/// @todo Non-GCC-specific version
ResultType result = 0;
for (int i = 0; i < size; i += sizeof(unsigned long))
{
unsigned long a2 = *reinterpret_cast<const unsigned long*> (a + i);
unsigned long b2 = *reinterpret_cast<const unsigned long*> (b + i);
result += __builtin_popcountl(a2 ^ b2);
}
return result;
}
};
#else
typedef HammingLUT Hamming;
#endif
/****************************************************************************************\
* DMatch *
......@@ -1783,7 +1784,7 @@ struct CV_EXPORTS DMatch
int queryIdx; // query descriptor index
int trainIdx; // train descriptor index
int imgIdx; // train image index
int imgIdx; // train image index
float distance;
......@@ -1803,111 +1804,126 @@ struct CV_EXPORTS DMatch
class CV_EXPORTS DescriptorMatcher
{
public:
virtual ~DescriptorMatcher() {}
/*
virtual ~DescriptorMatcher();
/*
* Add descriptors to train descriptor collection.
* descCollection Descriptors to add. Each descCollection[i] is from one image.
* descriptors Descriptors to add. Each descriptors[i] is a descriptors set from one image.
*/
virtual void add( const vector<Mat>& descCollection );
virtual void add( const vector<Mat>& descriptors );
/*
* Get descriptor collection.
* Get train descriptors collection.
*/
const vector<Mat>& getTrainDescCollection() const { return trainDescCollection; }
const vector<Mat>& getTrainDescriptors() const;
/*
* Clear inner data (train image collection).
* Clear train descriptors collection.
*/
virtual void clear();
virtual bool supportMask() = 0;
/*
* Return true if there are not train descriptors in collection.
*/
bool empty() const;
/*
* Return true if the matcher supports mask in match methods.
*/
virtual bool isMaskSupported() const = 0;
/*
* Train matcher (e.g. train flann index)
* Train matcher (e.g. train flann index).
* In all methods to match the method train() is run every time before matching.
* Some descriptor matchers (e.g. BruteForceMatcher) have empty implementation
* of this method, other matchers realy train their inner structures
* (e.g. FlannBasedMatcher trains flann::Index). So nonempty implementation
* of train() should check the class object state and do traing/retraining
* only if the state requires that (e.g. FlannBasedMatcher trains flann::Index
* if it has not trained yet or if new descriptors have been added to the train
* collection).
*/
virtual void train() = 0;
virtual void train();
/*
* Group of methods to match descriptors from image pair.
* Method train() is run in this methods.
*/
// Find one best match for each query descriptor (if mask is empty).
void match( const Mat& queryDescs, const Mat& trainDescs, vector<DMatch>& matches,
const Mat& mask=Mat() ) const;
// Find knn best matches for each query descriptor (in increasing order of distances).
// compactResult is used when mask is not empty. If compactResult is false matches vector will have the same size as queryDescs rows.
// If compactResult is true matches vector will not contain matches for fully masked out query descriptors.
void knnMatch( const Mat& queryDescs, const Mat& trainDescs, vector<vector<DMatch> >& matches, int knn,
void match( const Mat& queryDescriptors, const Mat& trainDescriptors,
vector<DMatch>& matches, const Mat& mask=Mat() ) const;
// Find k best matches for each query descriptor (in increasing order of distances).
// compactResult is used when mask is not empty. If compactResult is false matches
// vector will have the same size as queryDescriptors rows. If compactResult is true
// matches vector will not contain matches for fully masked out query descriptors.
void knnMatch( const Mat& queryDescriptors, const Mat& trainDescriptors,
vector<vector<DMatch> >& matches, int k,
const Mat& mask=Mat(), bool compactResult=false ) const;
// Find best matches for each query descriptor which have distance less than maxDistance (in increasing order of distances).
void radiusMatch( const Mat& queryDescs, const Mat& trainDescs, vector<vector<DMatch> >& matches, float maxDistance,
// Find best matches for each query descriptor which have distance less than
// maxDistance (in increasing order of distances).
void radiusMatch( const Mat& queryDescriptors, const Mat& trainDescriptors,
vector<vector<DMatch> >& matches, float maxDistance,
const Mat& mask=Mat(), bool compactResult=false ) const;
/*
* Group of methods to match descriptors from one image to image set.
* See description of similar methods for matching image pair above.
*/
void match( const Mat& queryDescs, vector<DMatch>& matches,
void match( const Mat& queryDescriptors, vector<DMatch>& matches,
const vector<Mat>& masks=vector<Mat>() );
void knnMatch( const Mat& queryDescs, vector<vector<DMatch> >& matches, int knn,
void knnMatch( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int k,
const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );
void radiusMatch( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );
void radiusMatch( const Mat& queryDescs, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );
// Reads matcher object from a file node
virtual void read( const FileNode& ) {}
virtual void read( const FileNode& );
// Writes matcher object to a file storage
virtual void write( FileStorage& ) const {}
virtual void write( FileStorage& ) const;
// Clone the matcher. If emptyTrainData is false the method create deep copy of the object, i.e. copies
// both parameters and train data. If emptyTrainData is true the method create object copy with current parameters
// but with empty train data.
virtual Ptr<DescriptorMatcher> clone( bool emptyTrainData=false ) const = 0;
protected:
/*
* Class to work with descriptors from several images as with one merged matrix.
* It is used e.g. in FlannBasedMatcher
* It is used e.g. in FlannBasedMatcher.
*/
class CV_EXPORTS DescriptorCollection
{
public:
DescriptorCollection() {}
virtual ~DescriptorCollection() {}
DescriptorCollection();
DescriptorCollection( const DescriptorCollection& collection );
virtual ~DescriptorCollection();
// descCollection will be merged to dmatrix here
void set( const vector<Mat>& descCollection );
// Vector of matrices "descriptors" will be merged to one matrix "mergedDescriptors" here.
void set( const vector<Mat>& descriptors );
virtual void clear();
const Mat& getDescriptors() const { return dmatrix; }
const Mat& getDescriptors() const;
const Mat getDescriptor( int imgIdx, int localDescIdx ) const;
const Mat getDescriptor( int globalDescIdx ) const;
void getLocalIdx( int globalDescIdx, int& imgIdx, int& localDescIdx ) const;
int size() const { return dmatrix.rows; }
int size() const;
protected:
Mat dmatrix;
Mat mergedDescriptors;
vector<int> startIdxs;
};
// create matcher clone with current parameters but with empty data
virtual Ptr<DescriptorMatcher> cloneWithoutData() const = 0;
virtual void knnMatchImpl( const Mat& queryDescs, vector<vector<DMatch> >& matches, int knn,
const vector<Mat>& masks, bool compactResult ) = 0;
virtual void radiusMatchImpl( const Mat& queryDescs, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks, bool compactResult ) = 0;
// In fact the matching is implemented only by the following two methods. These methods suppose
// that the class object has been trained already. Public match methods call these methods
// after calling train().
virtual void knnMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int k,
const vector<Mat>& masks=vector<Mat>(), bool compactResult=false ) = 0;
virtual void radiusMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks=vector<Mat>(), bool compactResult=false ) = 0;
static bool possibleMatch( const Mat& mask, int index_1, int index_2 )
{
return mask.empty() || mask.at<uchar>(index_1, index_2);
}
static bool maskedOut( const vector<Mat>& masks, int queryDescIdx )
{
size_t outCount = 0;
for( size_t i = 0; i < masks.size(); i++ )
{
if( !masks[i].empty() && (countNonZero(masks[i].row(queryDescIdx)) == 0) )
outCount++;
}
static bool isPossibleMatch( const Mat& mask, int queryIdx, int trainIdx );
static bool isMaskedOut( const vector<Mat>& masks, int queryIdx );
return !masks.empty() && outCount == masks.size() ;
}
static Mat clone_op( Mat m ) { return m.clone(); }
void checkMasks( const vector<Mat>& masks, int queryDescriptorsCount ) const;
// Collection of descriptors from train images.
vector<Mat> trainDescCollection;
};
......@@ -1927,64 +1943,78 @@ public:
BruteForceMatcher( Distance d = Distance() ) : distance(d) {}
virtual ~BruteForceMatcher() {}
virtual void train() {}
virtual bool supportMask() { return true; }
virtual bool isMaskSupported() const { return true; }
virtual Ptr<DescriptorMatcher> clone( bool emptyTrainData=false ) const;
protected:
virtual Ptr<DescriptorMatcher> cloneWithoutData() const { return new BruteForceMatcher(distance); }
virtual void knnMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int k,
const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );
virtual void radiusMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );
virtual void knnMatchImpl( const Mat& queryDescs, vector<vector<DMatch> >& matches, int knn,
const vector<Mat>& masks, bool compactResult );
virtual void radiusMatchImpl( const Mat& queryDescs, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks, bool compactResult );
Distance distance;
private:
/*
* Next two methods are used to implement specialization
* Next two methods are used to implement specialization.
*/
static void bfKnnMatchImpl( BruteForceMatcher<Distance>& matcher,
const Mat& queryDescs, vector<vector<DMatch> >& matches, int knn,
const vector<Mat>& masks, bool compactResult );
static void bfRadiusMatchImpl( BruteForceMatcher<Distance>& matcher,
const Mat& queryDescs, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks, bool compactResult );
static void commonKnnMatchImpl( BruteForceMatcher<Distance>& matcher,
const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int k,
const vector<Mat>& masks, bool compactResult );
static void commonRadiusMatchImpl( BruteForceMatcher<Distance>& matcher,
const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks, bool compactResult );
};
template<class Distance>
void BruteForceMatcher<Distance>::knnMatchImpl( const Mat& queryDescs, vector<vector<DMatch> >& matches, int knn,
Ptr<DescriptorMatcher> BruteForceMatcher<Distance>::clone( bool emptyTrainData ) const
{
BruteForceMatcher* matcher = new BruteForceMatcher(distance);
if( !emptyTrainData )
{
transform( trainDescCollection.begin(), trainDescCollection.end(),
matcher->trainDescCollection.begin(), clone_op );
}
return matcher;
}
template<class Distance>
void BruteForceMatcher<Distance>::knnMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int k,
const vector<Mat>& masks, bool compactResult )
{
bfKnnMatchImpl( *this, queryDescs, matches, knn, masks, compactResult );
commonKnnMatchImpl( *this, queryDescriptors, matches, k, masks, compactResult );
}
template<class Distance>
void BruteForceMatcher<Distance>::radiusMatchImpl( const Mat& queryDescs, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks, bool compactResult )
void BruteForceMatcher<Distance>::radiusMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches,
float maxDistance, const vector<Mat>& masks, bool compactResult )
{
bfRadiusMatchImpl( *this, queryDescs, matches, maxDistance, masks, compactResult );
commonRadiusMatchImpl( *this, queryDescriptors, matches, maxDistance, masks, compactResult );
}
template<class Distance>
inline void BruteForceMatcher<Distance>::bfKnnMatchImpl( BruteForceMatcher<Distance>& matcher,
const Mat& queryDescs, vector<vector<DMatch> >& matches, int knn,
inline void BruteForceMatcher<Distance>::commonKnnMatchImpl( BruteForceMatcher<Distance>& matcher,
const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int knn,
const vector<Mat>& masks, bool compactResult )
{
typedef typename Distance::ValueType ValueType;
typedef typename Distance::ResultType DistanceType;
CV_Assert( DataType<ValueType>::type == queryDescs.type() || queryDescs.empty() );
CV_Assert( masks.empty() || masks.size() == matcher.trainDescCollection.size() );
int dimension = queryDescs.cols;
matches.reserve(queryDescs.rows);
CV_DbgAssert( !queryDescriptors.empty() );
CV_Assert( DataType<ValueType>::type == queryDescriptors.type() );
int dimension = queryDescriptors.cols;
matches.reserve(queryDescriptors.rows);
size_t imgCount = matcher.trainDescCollection.size();
vector<Mat> allDists( imgCount ); // distances between one query descriptor and all train descriptors
for( size_t i = 0; i < imgCount; i++ )
allDists[i] = Mat( 1, matcher.trainDescCollection[i].rows, DataType<DistanceType>::type );
if( matcher.trainDescCollection[i].rows )
allDists[i] = Mat( 1, matcher.trainDescCollection[i].rows, DataType<DistanceType>::type );
for( int qIdx = 0; qIdx < queryDescs.rows; qIdx++ )
for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ )
{
if( matcher.maskedOut( masks, qIdx ) )
if( matcher.isMaskedOut( masks, qIdx ) )
{
if( !compactResult ) // push empty vector
matches.push_back( vector<DMatch>() );
......@@ -1994,17 +2024,15 @@ inline void BruteForceMatcher<Distance>::bfKnnMatchImpl( BruteForceMatcher<Dista
// 1. compute distances between i-th query descriptor and all train descriptors
for( size_t iIdx = 0; iIdx < imgCount; iIdx++ )
{
CV_Assert( masks.empty() || masks[iIdx].empty() ||
( masks[iIdx].rows == queryDescs.rows && masks[iIdx].cols == matcher.trainDescCollection[iIdx].rows &&
masks[iIdx].type() == CV_8UC1 ) );
CV_Assert( DataType<ValueType>::type == matcher.trainDescCollection[iIdx].type() || matcher.trainDescCollection[iIdx].empty() );
CV_Assert( queryDescs.cols == matcher.trainDescCollection[iIdx].cols );
CV_Assert( queryDescriptors.cols == matcher.trainDescCollection[iIdx].cols ||
matcher.trainDescCollection[iIdx].empty() );
const ValueType* d1 = (const ValueType*)(queryDescs.data + queryDescs.step*qIdx);
const ValueType* d1 = (const ValueType*)(queryDescriptors.data + queryDescriptors.step*qIdx);
allDists[iIdx].setTo( Scalar::all(std::numeric_limits<DistanceType>::max()) );
for( int tIdx = 0; tIdx < matcher.trainDescCollection[iIdx].rows; tIdx++ )
{
if( masks.empty() || matcher.possibleMatch(masks[iIdx], qIdx, tIdx) )
if( masks.empty() || matcher.isPossibleMatch(masks[iIdx], qIdx, tIdx) )
{
const ValueType* d2 = (const ValueType*)(matcher.trainDescCollection[iIdx].data +
matcher.trainDescCollection[iIdx].step*tIdx);
......@@ -2013,20 +2041,23 @@ inline void BruteForceMatcher<Distance>::bfKnnMatchImpl( BruteForceMatcher<Dista
}
}
// 2. choose knn nearest matches for query[i]
// 2. choose k nearest matches for query[i]
matches.push_back( vector<DMatch>() );
vector<vector<DMatch> >::reverse_iterator curMatches = matches.rbegin();
for( int k = 0; k < knn; k++ )
{
DMatch bestMatch;
bestMatch.distance = std::numeric_limits<DistanceType>::max();
bestMatch.distance = std::numeric_limits<float>::max();
for( size_t iIdx = 0; iIdx < imgCount; iIdx++ )
{
double minVal;
Point minLoc;
minMaxLoc( allDists[iIdx], &minVal, 0, &minLoc, 0 );
if( minVal < bestMatch.distance )
bestMatch = DMatch( qIdx, minLoc.x, iIdx, minVal );
if( !allDists[iIdx].empty() )
{
double minVal;
Point minLoc;
minMaxLoc( allDists[iIdx], &minVal, 0, &minLoc, 0 );
if( minVal < bestMatch.distance )
bestMatch = DMatch( qIdx, minLoc.x, iIdx, (float)minVal );
}
}
if( bestMatch.trainIdx == -1 )
break;
......@@ -2041,22 +2072,22 @@ inline void BruteForceMatcher<Distance>::bfKnnMatchImpl( BruteForceMatcher<Dista
}
template<class Distance>
inline void BruteForceMatcher<Distance>::bfRadiusMatchImpl( BruteForceMatcher<Distance>& matcher,
const Mat& queryDescs, vector<vector<DMatch> >& matches, float maxDistance,
inline void BruteForceMatcher<Distance>::commonRadiusMatchImpl( BruteForceMatcher<Distance>& matcher,
const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks, bool compactResult )
{
typedef typename Distance::ValueType ValueType;
typedef typename Distance::ResultType DistanceType;
CV_Assert( DataType<ValueType>::type == queryDescs.type() || queryDescs.empty() );
CV_Assert( masks.empty() || masks.size() == matcher.trainDescCollection.size() );
int dimension = queryDescs.cols;
matches.reserve(queryDescs.rows);
CV_DbgAssert( !queryDescriptors.empty() );
CV_Assert( DataType<ValueType>::type == queryDescriptors.type() );
int dimension = queryDescriptors.cols;
matches.reserve(queryDescriptors.rows);
size_t imgCount = matcher.trainDescCollection.size();
for( int qIdx = 0; qIdx < queryDescs.rows; qIdx++ )
for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ )
{
if( matcher.maskedOut( masks, qIdx ) )
if( matcher.isMaskedOut( masks, qIdx ) )
{
if( !compactResult ) // push empty vector
matches.push_back( vector<DMatch>() );
......@@ -2067,23 +2098,21 @@ inline void BruteForceMatcher<Distance>::bfRadiusMatchImpl( BruteForceMatcher<Di
vector<vector<DMatch> >::reverse_iterator curMatches = matches.rbegin();
for( size_t iIdx = 0; iIdx < imgCount; iIdx++ )
{
CV_Assert( masks.empty() || masks[iIdx].empty() ||
( masks[iIdx].rows == queryDescs.rows && masks[iIdx].cols == matcher.trainDescCollection[iIdx].rows &&
masks[iIdx].type() == CV_8UC1 ) );
CV_Assert( DataType<ValueType>::type == matcher.trainDescCollection[iIdx].type() ||
matcher.trainDescCollection[iIdx].empty() );
CV_Assert( queryDescs.cols == matcher.trainDescCollection[iIdx].cols );
CV_Assert( queryDescriptors.cols == matcher.trainDescCollection[iIdx].cols ||
matcher.trainDescCollection[iIdx].empty() );
const ValueType* d1 = (const ValueType*)(queryDescs.data + queryDescs.step*qIdx);
const ValueType* d1 = (const ValueType*)(queryDescriptors.data + queryDescriptors.step*qIdx);
for( int tIdx = 0; tIdx < matcher.trainDescCollection[iIdx].rows; tIdx++ )
{
if( masks.empty() || matcher.possibleMatch(masks[iIdx], qIdx, tIdx) )
if( masks.empty() || matcher.isPossibleMatch(masks[iIdx], qIdx, tIdx) )
{
const ValueType* d2 = (const ValueType*)(matcher.trainDescCollection[iIdx].data +
matcher.trainDescCollection[iIdx].step*tIdx);
DistanceType d = matcher.distance(d1, d2, dimension);
if( d < maxDistance )
curMatches->push_back( DMatch( qIdx, tIdx, iIdx, d ) );
curMatches->push_back( DMatch( qIdx, tIdx, iIdx, (float)d ) );
}
}
}
......@@ -2096,11 +2125,11 @@ inline void BruteForceMatcher<Distance>::bfRadiusMatchImpl( BruteForceMatcher<Di
* BruteForceMatcher L2 specialization
*/
template<>
void BruteForceMatcher<L2<float> >::knnMatchImpl( const Mat& queryDescs, vector<vector<DMatch> >& matches, int knn,
void BruteForceMatcher<L2<float> >::knnMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int k,
const vector<Mat>& masks, bool compactResult );
template<>
void BruteForceMatcher<L2<float> >::radiusMatchImpl( const Mat& queryDescs, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks, bool compactResult );
void BruteForceMatcher<L2<float> >::radiusMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches,
float maxDistance, const vector<Mat>& masks, bool compactResult );
/*
* Flann based matcher
......@@ -2108,26 +2137,27 @@ void BruteForceMatcher<L2<float> >::radiusMatchImpl( const Mat& queryDescs, vect
class CV_EXPORTS FlannBasedMatcher : public DescriptorMatcher
{
public:
FlannBasedMatcher( const Ptr<flann::IndexParams>& _indexParams=new flann::KDTreeIndexParams(),
const Ptr<flann::SearchParams>& _searchParams=new flann::SearchParams() );
FlannBasedMatcher( const Ptr<flann::IndexParams>& indexParams=new flann::KDTreeIndexParams(),
const Ptr<flann::SearchParams>& searchParams=new flann::SearchParams() );
virtual void add( const vector<Mat>& descCollection );
virtual void add( const vector<Mat>& descriptors );
virtual void clear();
virtual void train();
virtual bool supportMask() { return false; }
protected:
virtual Ptr<DescriptorMatcher> cloneWithoutData() const { return new FlannBasedMatcher(indexParams, searchParams); }
// masks is ignored (unsupported)
virtual void knnMatchImpl( const Mat& queryDescs, vector<vector<DMatch> >& matches, int knn,
const vector<Mat>& masks, bool compactResult );
virtual void radiusMatchImpl( const Mat& queryDescs, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks, bool compactResult );
virtual bool isMaskSupported() const;
virtual Ptr<DescriptorMatcher> clone( bool emptyTrainData=false ) const;
static void convertToDMatches( const DescriptorCollection& collection, const Mat& indices, const Mat& dists,
protected:
static void convertToDMatches( const DescriptorCollection& descriptors,
const Mat& indices, const Mat& distances,
vector<vector<DMatch> >& matches );
virtual void knnMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int k,
const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );
virtual void radiusMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );
Ptr<flann::IndexParams> indexParams;
Ptr<flann::SearchParams> searchParams;
Ptr<flann::Index> flannIndex;
......@@ -2151,81 +2181,106 @@ typedef GenericDescriptorMatcher GenericDescriptorMatch;
class CV_EXPORTS GenericDescriptorMatcher
{
public:
GenericDescriptorMatcher() {}
virtual ~GenericDescriptorMatcher() {}
GenericDescriptorMatcher();
virtual ~GenericDescriptorMatcher();
/*
* Set train collection: images and keypoints from them.
* imgCollection Image collection.
* pointCollection Keypoint collection detected on imgCollection.
* Add train collection: images and keypoints from them.
* images A set of train images.
* ketpoints Keypoint collection that have been detected on train images.
*
* Keypoints for which a descriptor cannot be computed are removed. Such keypoints
* must be filtered in this method befor adding keypoints to train collection "trainPointCollection".
* If inheritor class need perform such prefiltering the method add() must be overloaded.
* In the other class methods programmer has access to the train keypoints by a constant link.
*/
virtual void add( const vector<Mat>& imgCollection,
vector<vector<KeyPoint> >& pointCollection );
virtual void add( const vector<Mat>& images,
vector<vector<KeyPoint> >& keypoints );
const vector<Mat>& getTrainImgCollection() const { return trainPointCollection.getImages(); }
const vector<vector<KeyPoint> >& getTrainPointCollection() const { return trainPointCollection.getKeypoints(); }
const vector<Mat>& getTrainImages() const;
const vector<vector<KeyPoint> >& getTrainKeypoints() const;
// Clears keypoints storing in collection
/*
* Clear images and keypoints storing in train collection.
*/
virtual void clear();
virtual void train() = 0;
virtual bool supportMask() = 0;
/*
* Returns true if matcher supports mask to match descriptors.
*/
virtual bool isMaskSupported() = 0;
/*
* Train some inner structures (e.g. flann index or decision trees).
* train() methods is run every time in matching methods. So the method implementation
* should has a check whether these inner structures need be trained/retrained or not.
*/
virtual void train();
/*
* Classifies query keypoints.
* queryImage The query image
* queryPoints Keypoints from the query image
* queryKeypoints Keypoints from the query image
* trainImage The train image
* trainPoints Keypoints from the train image
* trainKeypoints Keypoints from the train image
*/
// Classify keypoints from query image under one train image.
virtual void classify( const Mat& queryImage, vector<KeyPoint>& queryPoints,
const Mat& trainImage, vector<KeyPoint>& trainPoints ) const;
virtual void classify( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
const Mat& trainImage, vector<KeyPoint>& trainKeypoints ) const;
// Classify keypoints from query image under train image collection.
virtual void classify( const Mat& queryImage, vector<KeyPoint>& queryPoints );
virtual void classify( const Mat& queryImage, vector<KeyPoint>& queryKeypoints );
/*
* Group of methods to match keypoints from image pair.
* Keypoints for which a descriptor cannot be computed are removed.
* train() method is called here.
*/
// Find one best match for each query descriptor (if mask is empty).
void match( const Mat& queryImg, vector<KeyPoint>& queryPoints,
const Mat& trainImg, vector<KeyPoint>& trainPoints,
void match( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
const Mat& trainImage, vector<KeyPoint>& trainKeypoints,
vector<DMatch>& matches, const Mat& mask=Mat() ) const;
// Find knn best matches for each query keypoint (in increasing order of distances).
// compactResult is used when mask is not empty. If compactResult is false matches vector will have the same size as queryDescs rows.
// Find k best matches for each query keypoint (in increasing order of distances).
// compactResult is used when mask is not empty. If compactResult is false matches
// vector will have the same size as queryDescriptors rows.
// If compactResult is true matches vector will not contain matches for fully masked out query descriptors.
void knnMatch( const Mat& queryImg, vector<KeyPoint>& queryPoints,
const Mat& trainImg, vector<KeyPoint>& trainPoints,
vector<vector<DMatch> >& matches, int knn, const Mat& mask=Mat(), bool compactResult=false ) const;
void knnMatch( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
const Mat& trainImage, vector<KeyPoint>& trainKeypoints,
vector<vector<DMatch> >& matches, int k,
const Mat& mask=Mat(), bool compactResult=false ) const;
// Find best matches for each query descriptor which have distance less than maxDistance (in increasing order of distances).
void radiusMatch( const Mat& queryImg, vector<KeyPoint>& queryPoints,
const Mat& trainImg, vector<KeyPoint>& trainPoints,
vector<vector<DMatch> >& matches, float maxDistance, const Mat& mask=Mat(), bool compactResult=false ) const;
void radiusMatch( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
const Mat& trainImage, vector<KeyPoint>& trainKeypoints,
vector<vector<DMatch> >& matches, float maxDistance,
const Mat& mask=Mat(), bool compactResult=false ) const;
/*
* Group of methods to match keypoints from one image to image set.
* See description of similar methods for matching image pair above.
*/
void match( const Mat& queryImg, vector<KeyPoint>& queryPoints,
void match( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
vector<DMatch>& matches, const vector<Mat>& masks=vector<Mat>() );
void knnMatch( const Mat& queryImg, vector<KeyPoint>& queryPoints,
vector<vector<DMatch> >& matches, int knn, const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );
void radiusMatch( const Mat& queryImg, vector<KeyPoint>& queryPoints,
vector<vector<DMatch> >& matches, float maxDistance, const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );
void knnMatch( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
vector<vector<DMatch> >& matches, int k,
const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );
void radiusMatch( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks=vector<Mat>(), bool compactResult=false );
// Reads matcher object from a file node
virtual void read( const FileNode& ) {}
virtual void read( const FileNode& );
// Writes matcher object to a file storage
virtual void write( FileStorage& ) const {}
virtual void write( FileStorage& ) const;
protected:
virtual Ptr<GenericDescriptorMatcher> createEmptyMatcherCopy() const = 0;
// Clone the matcher. If emptyTrainData is false the method create deep copy of the object, i.e. copies
// both parameters and train data. If emptyTrainData is true the method create object copy with current parameters
// but with empty train data.
virtual Ptr<GenericDescriptorMatcher> clone( bool emptyTrainData=false ) const = 0;
virtual void knnMatchImpl( const Mat& queryImg, vector<KeyPoint>& queryPoints,
vector<vector<DMatch> >& matches, int knn,
protected:
// In fact the matching is implemented only by the following two methods. These methods suppose
// that the class object has been trained already. Public match methods call these methods
// after calling train().
virtual void knnMatchImpl( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
vector<vector<DMatch> >& matches, int k,
const vector<Mat>& masks, bool compactResult ) = 0;
virtual void radiusMatchImpl( const Mat& queryImg, vector<KeyPoint>& queryPoints,
virtual void radiusMatchImpl( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks, bool compactResult ) = 0;
/*
......@@ -2234,32 +2289,34 @@ protected:
class CV_EXPORTS KeyPointCollection
{
public:
KeyPointCollection() : size(0) {}
void add( const vector<Mat>& _images, const vector<vector<KeyPoint> >& _points );
KeyPointCollection();
KeyPointCollection( const KeyPointCollection& collection );
void add( const vector<Mat>& images, const vector<vector<KeyPoint> >& keypoints );
void clear();
// Returns the total number of keypoints in the collection
size_t pointCount() const { return size; }
size_t imageCount() const { return images.size(); }
size_t keypointCount() const;
size_t imageCount() const;
const vector<vector<KeyPoint> >& getKeypoints() const { return points; }
const vector<KeyPoint>& getKeypoints( int imgIdx ) const { CV_Assert( imgIdx < (int)imageCount() ); return points[imgIdx]; }
const vector<vector<KeyPoint> >& getKeypoints() const;
const vector<KeyPoint>& getKeypoints( int imgIdx ) const;
const KeyPoint& getKeyPoint( int imgIdx, int localPointIdx ) const;
const KeyPoint& getKeyPoint( int globalPointIdx ) const;
void getLocalIdx( int globalPointIdx, int& imgIdx, int& localPointIdx ) const;
const vector<Mat>& getImages() const { return images; }
const Mat& getImage( int imgIdx ) const { CV_Assert( imgIdx < (int)imageCount() ); return images[imgIdx]; }
const vector<Mat>& getImages() const;
const Mat& getImage( int imgIdx ) const;
protected:
int size;
int pointCount;
vector<Mat> images;
vector<vector<KeyPoint> > points;
// global indices of the first points in each image,
// startIndices.size() = points.size()
vector<vector<KeyPoint> > keypoints;
// global indices of the first points in each image, startIndices.size() = keypoints.size()
vector<int> startIndices;
private:
static Mat clone_op( Mat m ) { return m.clone(); }
};
KeyPointCollection trainPointCollection;
......@@ -2274,7 +2331,7 @@ typedef OneWayDescriptorMatcher OneWayDescriptorMatch;
class CV_EXPORTS OneWayDescriptorMatcher : public GenericDescriptorMatcher
{
public:
class Params
class CV_EXPORTS Params
{
public:
static const int POSE_COUNT = 500;
......@@ -2284,16 +2341,12 @@ public:
static float GET_MAX_SCALE() { return 1.5f; }
static float GET_STEP_SCALE() { return 1.2f; }
Params( int _poseCount = POSE_COUNT,
Size _patchSize = Size(PATCH_WIDTH, PATCH_HEIGHT),
string _pcaFilename = string(),
string _trainPath = string(),
string _trainImagesList = string(),
float _minScale = GET_MIN_SCALE(), float _maxScale = GET_MAX_SCALE(),
float _stepScale = GET_STEP_SCALE() ) :
poseCount(_poseCount), patchSize(_patchSize), pcaFilename(_pcaFilename),
trainPath(_trainPath), trainImagesList(_trainImagesList),
minScale(_minScale), maxScale(_maxScale), stepScale(_stepScale) {}
Params( int poseCount = POSE_COUNT,
Size patchSize = Size(PATCH_WIDTH, PATCH_HEIGHT),
string pcaFilename = string(),
string trainPath = string(), string trainImagesList = string(),
float minScale = GET_MIN_SCALE(), float maxScale = GET_MAX_SCALE(),
float stepScale = GET_STEP_SCALE() );
int poseCount;
Size patchSize;
......@@ -2304,38 +2357,34 @@ public:
float minScale, maxScale, stepScale;
};
// Equivalent to calling PointMatchOneWay() followed by Initialize(_params)
OneWayDescriptorMatcher( const Params& _params=Params() );
OneWayDescriptorMatcher( const Params& params=Params() );
virtual ~OneWayDescriptorMatcher();
void initialize( const Params& _params, const Ptr<OneWayDescriptorBase>& _base=Ptr<OneWayDescriptorBase>() );
void initialize( const Params& params, const Ptr<OneWayDescriptorBase>& base=Ptr<OneWayDescriptorBase>() );
// Clears keypoints storing in collection and OneWayDescriptorBase
virtual void clear ();
virtual void clear();
virtual void train();
virtual bool supportMask() { return false; }
virtual bool isMaskSupported();
// Reads match object from a file node
virtual void read( const FileNode &fn );
// Writes match object to a file storage
virtual void write( FileStorage& fs ) const;
protected:
virtual Ptr<GenericDescriptorMatcher> createEmptyMatcherCopy() const { return new OneWayDescriptorMatcher( params ); }
virtual Ptr<GenericDescriptorMatcher> clone( bool emptyTrainData=false ) const;
protected:
// Matches a set of keypoints from a single image of the training set. A rectangle with a center in a keypoint
// and size (patch_width/2*scale, patch_height/2*scale) is cropped from the source image for each
// keypoint. scale is iterated from DescriptorOneWayParams::min_scale to DescriptorOneWayParams::max_scale.
// The minimum distance to each training patch with all its affine poses is found over all scales.
// The class ID of a match is returned for each keypoint. The distance is calculated over PCA components
// loaded with DescriptorOneWay::Initialize, kd tree is used for finding minimum distances.
virtual void knnMatchImpl( const Mat& queryImg, vector<KeyPoint>& queryPoints,
vector<vector<DMatch> >& matches, int knn,
virtual void knnMatchImpl( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
vector<vector<DMatch> >& matches, int k,
const vector<Mat>& masks, bool compactResult );
virtual void radiusMatchImpl( const Mat& queryImg, vector<KeyPoint>& queryPoints,
virtual void radiusMatchImpl( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks, bool compactResult );
......@@ -2356,16 +2405,16 @@ public:
class CV_EXPORTS Params
{
public:
Params( int _nclasses=0,
int _patchSize=FernClassifier::PATCH_SIZE,
int _signatureSize=FernClassifier::DEFAULT_SIGNATURE_SIZE,
int _nstructs=FernClassifier::DEFAULT_STRUCTS,
int _structSize=FernClassifier::DEFAULT_STRUCT_SIZE,
int _nviews=FernClassifier::DEFAULT_VIEWS,
int _compressionMethod=FernClassifier::COMPRESSION_NONE,
Params( int nclasses=0,
int patchSize=FernClassifier::PATCH_SIZE,
int signatureSize=FernClassifier::DEFAULT_SIGNATURE_SIZE,
int nstructs=FernClassifier::DEFAULT_STRUCTS,
int structSize=FernClassifier::DEFAULT_STRUCT_SIZE,
int nviews=FernClassifier::DEFAULT_VIEWS,
int compressionMethod=FernClassifier::COMPRESSION_NONE,
const PatchGenerator& patchGenerator=PatchGenerator() );
Params( const string& _filename );
Params( const string& filename );
int nclasses;
int patchSize;
......@@ -2379,25 +2428,25 @@ public:
string filename;
};
FernDescriptorMatcher( const Params& _params=Params() );
FernDescriptorMatcher( const Params& params=Params() );
virtual ~FernDescriptorMatcher();
virtual void clear();
virtual void train();
virtual bool supportMask() { return false; }
virtual bool isMaskSupported();
virtual void read( const FileNode &fn );
virtual void write( FileStorage& fs ) const;
protected:
virtual Ptr<GenericDescriptorMatcher> createEmptyMatcherCopy() const { return new FernDescriptorMatcher( params ); }
virtual Ptr<GenericDescriptorMatcher> clone( bool emptyTrainData=false ) const;
virtual void knnMatchImpl( const Mat& queryImg, vector<KeyPoint>& queryPoints,
vector<vector<DMatch> >& matches, int knn,
protected:
virtual void knnMatchImpl( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
vector<vector<DMatch> >& matches, int k,
const vector<Mat>& masks, bool compactResult );
virtual void radiusMatchImpl( const Mat& queryImg, vector<KeyPoint>& queryPoints,
virtual void radiusMatchImpl( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks, bool compactResult );
......@@ -2425,9 +2474,8 @@ typedef VectorDescriptorMatcher VectorDescriptorMatch;
class CV_EXPORTS VectorDescriptorMatcher : public GenericDescriptorMatcher
{
public:
VectorDescriptorMatcher( const Ptr<DescriptorExtractor>& _extractor, const Ptr<DescriptorMatcher>& _matcher )
: extractor( _extractor ), matcher( _matcher ) { CV_Assert( !extractor.empty() && !matcher.empty() ); }
virtual ~VectorDescriptorMatcher() {}
VectorDescriptorMatcher( const Ptr<DescriptorExtractor>& extractor, const Ptr<DescriptorMatcher>& matcher );
virtual ~VectorDescriptorMatcher();
virtual void add( const vector<Mat>& imgCollection,
vector<vector<KeyPoint> >& pointCollection );
......@@ -2436,18 +2484,18 @@ public:
virtual void train();
virtual bool supportMask() { return matcher->supportMask(); }
virtual bool isMaskSupported();
virtual void read( const FileNode& fn );
virtual void write( FileStorage& fs ) const;
protected:
virtual Ptr<GenericDescriptorMatcher> createEmptyMatcherCopy() const { return new VectorDescriptorMatcher(extractor, matcher); }
virtual Ptr<GenericDescriptorMatcher> clone( bool emptyTrainData=false ) const;
virtual void knnMatchImpl( const Mat& queryImg, vector<KeyPoint>& queryPoints,
vector<vector<DMatch> >& matches, int knn,
protected:
virtual void knnMatchImpl( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
vector<vector<DMatch> >& matches, int k,
const vector<Mat>& masks, bool compactResult );
virtual void radiusMatchImpl( const Mat& queryImg, vector<KeyPoint>& queryPoints,
virtual void radiusMatchImpl( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks, bool compactResult );
......@@ -2520,12 +2568,12 @@ CV_EXPORTS void evaluateGenericDescriptorMatcher( const Mat& img1, const Mat& im
class CV_EXPORTS BOWTrainer
{
public:
BOWTrainer(){}
virtual ~BOWTrainer(){}
BOWTrainer();
virtual ~BOWTrainer();
void add( const Mat& descriptors );
const vector<Mat>& getDescriptors() const { return descriptors; }
int descripotorsCount() const { return descriptors.empty() ? 0 : size; }
const vector<Mat>& getDescriptors() const;
int descripotorsCount() const;
virtual void clear();
......@@ -2552,7 +2600,7 @@ class CV_EXPORTS BOWKMeansTrainer : public BOWTrainer
public:
BOWKMeansTrainer( int clusterCount, const TermCriteria& termcrit=TermCriteria(),
int attempts=3, int flags=KMEANS_PP_CENTERS );
virtual ~BOWKMeansTrainer(){}
virtual ~BOWKMeansTrainer();
// Returns trained vocabulary (i.e. cluster centers).
virtual Mat cluster() const;
......@@ -2574,14 +2622,16 @@ class CV_EXPORTS BOWImgDescriptorExtractor
public:
BOWImgDescriptorExtractor( const Ptr<DescriptorExtractor>& dextractor,
const Ptr<DescriptorMatcher>& dmatcher );
virtual ~BOWImgDescriptorExtractor(){}
virtual ~BOWImgDescriptorExtractor();
void setVocabulary( const Mat& vocabulary );
const Mat& getVocabulary() const { return vocabulary; }
const Mat& getVocabulary() const;
void compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& imgDescriptor,
vector<vector<int> >* pointIdxsOfClusters=0, Mat* descriptors=0 ); //not constant because DescriptorMatcher::match is not constant
int descriptorSize() const { return vocabulary.empty() ? 0 : vocabulary.rows; }
int descriptorType() const { return CV_32FC1; }
vector<vector<int> >* pointIdxsOfClusters=0, Mat* descriptors=0 );
// compute() is not constant because DescriptorMatcher::match is not constant
int descriptorSize() const;
int descriptorType() const;
protected:
Mat vocabulary;
......
......@@ -46,6 +46,12 @@ using namespace std;
namespace cv
{
BOWTrainer::BOWTrainer()
{}
BOWTrainer::~BOWTrainer()
{}
void BOWTrainer::add( const Mat& _descriptors )
{
CV_Assert( !_descriptors.empty() );
......@@ -63,6 +69,16 @@ void BOWTrainer::add( const Mat& _descriptors )
descriptors.push_back(_descriptors);
}
const vector<Mat>& BOWTrainer::getDescriptors() const
{
return descriptors;
}
int BOWTrainer::descripotorsCount() const
{
return descriptors.empty() ? 0 : size;
}
void BOWTrainer::clear()
{
descriptors.clear();
......@@ -91,6 +107,9 @@ Mat BOWKMeansTrainer::cluster() const
return cluster( mergedDescriptors );
}
BOWKMeansTrainer::~BOWKMeansTrainer()
{}
Mat BOWKMeansTrainer::cluster( const Mat& descriptors ) const
{
Mat labels, vocabulary;
......@@ -104,6 +123,9 @@ BOWImgDescriptorExtractor::BOWImgDescriptorExtractor( const Ptr<DescriptorExtrac
dextractor(_dextractor), dmatcher(_dmatcher)
{}
BOWImgDescriptorExtractor::~BOWImgDescriptorExtractor()
{}
void BOWImgDescriptorExtractor::setVocabulary( const Mat& _vocabulary )
{
dmatcher->clear();
......@@ -111,6 +133,11 @@ void BOWImgDescriptorExtractor::setVocabulary( const Mat& _vocabulary )
dmatcher->add( vector<Mat>(1, vocabulary) );
}
const Mat& BOWImgDescriptorExtractor::getVocabulary() const
{
return vocabulary;
}
void BOWImgDescriptorExtractor::compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& imgDescriptor,
vector<vector<int> >* pointIdxsOfClusters, Mat* _descriptors )
{
......@@ -153,4 +180,14 @@ void BOWImgDescriptorExtractor::compute( const Mat& image, vector<KeyPoint>& key
imgDescriptor /= descriptors.rows;
}
int BOWImgDescriptorExtractor::descriptorSize() const
{
return vocabulary.empty() ? 0 : vocabulary.rows;
}
int BOWImgDescriptorExtractor::descriptorType() const
{
return CV_32FC1;
}
}
......@@ -44,78 +44,101 @@
#include <algorithm>
#include <vector>
namespace cv
using namespace cv;
inline int smoothedSum(const Mat& sum, const KeyPoint& pt, int y, int x)
{
static const int HALF_KERNEL = BriefDescriptorExtractor::KERNEL_SIZE / 2;
BriefDescriptorExtractor::BriefDescriptorExtractor(int bytes) :
bytes_(bytes), test_fn_(NULL)
int img_y = (int)(pt.pt.y + 0.5) + y;
int img_x = (int)(pt.pt.x + 0.5) + x;
return sum.at<int>(img_y + HALF_KERNEL + 1, img_x + HALF_KERNEL + 1)
- sum.at<int>(img_y + HALF_KERNEL + 1, img_x - HALF_KERNEL)
- sum.at<int>(img_y - HALF_KERNEL, img_x + HALF_KERNEL + 1)
+ sum.at<int>(img_y - HALF_KERNEL, img_x - HALF_KERNEL);
}
void pixelTests16(const Mat& sum, const std::vector<KeyPoint>& keypoints, Mat& descriptors)
{
switch (bytes)
{
case 16:
test_fn_ = pixelTests16;
break;
case 32:
test_fn_ = pixelTests32;
break;
case 64:
test_fn_ = pixelTests64;
break;
default:
CV_Error(CV_StsBadArg, "bytes must be 16, 32, or 64");
}
for (int i = 0; i < (int)keypoints.size(); ++i)
{
uchar* desc = descriptors.ptr(i);
const KeyPoint& pt = keypoints[i];
#include "generated_16.i"
}
}
void BriefDescriptorExtractor::compute(const Mat& image, std::vector<KeyPoint>& keypoints, Mat& descriptors) const
void pixelTests32(const Mat& sum, const std::vector<KeyPoint>& keypoints, Mat& descriptors)
{
// Construct integral image for fast smoothing (box filter)
Mat sum;
///TODO allow the user to pass in a precomputed integral image
//if(image.type() == CV_32S)
// sum = image;
//else
integral(image, sum, CV_32S);
for (int i = 0; i < (int)keypoints.size(); ++i)
{
uchar* desc = descriptors.ptr(i);
const KeyPoint& pt = keypoints[i];
//Remove keypoints very close to the border
removeBorderKeypoints(keypoints, image.size(), PATCH_SIZE/2 + KERNEL_SIZE/2);
#include "generated_32.i"
}
}
void pixelTests64(const Mat& sum, const std::vector<KeyPoint>& keypoints, Mat& descriptors)
{
for (int i = 0; i < (int)keypoints.size(); ++i)
{
uchar* desc = descriptors.ptr(i);
const KeyPoint& pt = keypoints[i];
descriptors = Mat::zeros(keypoints.size(), bytes_, CV_8U);
test_fn_(sum, keypoints, descriptors);
#include "generated_64.i"
}
}
void BriefDescriptorExtractor::pixelTests16(const Mat& sum, const std::vector<KeyPoint>& keypoints, Mat& descriptors)
namespace cv
{
for (int i = 0; i < (int)keypoints.size(); ++i)
{
uchar* desc = descriptors.ptr(i);
const KeyPoint& pt = keypoints[i];
#include "generated_16.i"
}
BriefDescriptorExtractor::BriefDescriptorExtractor(int bytes) :
bytes_(bytes), test_fn_(NULL)
{
switch (bytes)
{
case 16:
test_fn_ = pixelTests16;
break;
case 32:
test_fn_ = pixelTests32;
break;
case 64:
test_fn_ = pixelTests64;
break;
default:
CV_Error(CV_StsBadArg, "bytes must be 16, 32, or 64");
}
}
void BriefDescriptorExtractor::pixelTests32(const Mat& sum, const std::vector<KeyPoint>& keypoints, Mat& descriptors)
int BriefDescriptorExtractor::descriptorSize() const
{
for (int i = 0; i < (int)keypoints.size(); ++i)
{
uchar* desc = descriptors.ptr(i);
const KeyPoint& pt = keypoints[i];
return bytes_;
}
#include "generated_32.i"
}
int BriefDescriptorExtractor::descriptorType() const
{
return CV_8UC1;
}
void BriefDescriptorExtractor::pixelTests64(const Mat& sum, const std::vector<KeyPoint>& keypoints, Mat& descriptors)
void BriefDescriptorExtractor::computeImpl(const Mat& image, std::vector<KeyPoint>& keypoints, Mat& descriptors) const
{
for (int i = 0; i < (int)keypoints.size(); ++i)
{
uchar* desc = descriptors.ptr(i);
const KeyPoint& pt = keypoints[i];
// Construct integral image for fast smoothing (box filter)
Mat sum;
#include "generated_64.i"
}
///TODO allow the user to pass in a precomputed integral image
//if(image.type() == CV_32S)
// sum = image;
//else
integral(image, sum, CV_32S);
//Remove keypoints very close to the border
removeBorderKeypoints(keypoints, image.size(), PATCH_SIZE/2 + KERNEL_SIZE/2);
descriptors = Mat::zeros(keypoints.size(), bytes_, CV_8U);
test_fn_(sum, keypoints, descriptors);
}
/**
......@@ -125,282 +148,285 @@ void BriefDescriptorExtractor::pixelTests64(const Mat& sum, const std::vector<Ke
template<unsigned char b>
struct ByteBits
{
/**
* number of bits in the byte given by the template constant
*/
enum
{
COUNT = ((b >> 0) & 1) +
((b >> 1) & 1) +
((b >> 2) & 1) +
((b >> 3) & 1) +
((b >> 4) & 1) +
((b >> 5) & 1) +
((b >> 6) & 1) +
((b >> 7) & 1)
};
/**
* number of bits in the byte given by the template constant
*/
enum
{
COUNT = ((b >> 0) & 1) +
((b >> 1) & 1) +
((b >> 2) & 1) +
((b >> 3) & 1) +
((b >> 4) & 1) +
((b >> 5) & 1) +
((b >> 6) & 1) +
((b >> 7) & 1)
};
};
unsigned char HammingLUT::byteBitsLookUp(unsigned char b){
static const unsigned char table[256] =
{
ByteBits<0>::COUNT,
ByteBits<1>::COUNT,
ByteBits<2>::COUNT,
ByteBits<3>::COUNT,
ByteBits<4>::COUNT,
ByteBits<5>::COUNT,
ByteBits<6>::COUNT,
ByteBits<7>::COUNT,
ByteBits<8>::COUNT,
ByteBits<9>::COUNT,
ByteBits<10>::COUNT,
ByteBits<11>::COUNT,
ByteBits<12>::COUNT,
ByteBits<13>::COUNT,
ByteBits<14>::COUNT,
ByteBits<15>::COUNT,
ByteBits<16>::COUNT,
ByteBits<17>::COUNT,
ByteBits<18>::COUNT,
ByteBits<19>::COUNT,
ByteBits<20>::COUNT,
ByteBits<21>::COUNT,
ByteBits<22>::COUNT,
ByteBits<23>::COUNT,
ByteBits<24>::COUNT,
ByteBits<25>::COUNT,
ByteBits<26>::COUNT,
ByteBits<27>::COUNT,
ByteBits<28>::COUNT,
ByteBits<29>::COUNT,
ByteBits<30>::COUNT,
ByteBits<31>::COUNT,
ByteBits<32>::COUNT,
ByteBits<33>::COUNT,
ByteBits<34>::COUNT,
ByteBits<35>::COUNT,
ByteBits<36>::COUNT,
ByteBits<37>::COUNT,
ByteBits<38>::COUNT,
ByteBits<39>::COUNT,
ByteBits<40>::COUNT,
ByteBits<41>::COUNT,
ByteBits<42>::COUNT,
ByteBits<43>::COUNT,
ByteBits<44>::COUNT,
ByteBits<45>::COUNT,
ByteBits<46>::COUNT,
ByteBits<47>::COUNT,
ByteBits<48>::COUNT,
ByteBits<49>::COUNT,
ByteBits<50>::COUNT,
ByteBits<51>::COUNT,
ByteBits<52>::COUNT,
ByteBits<53>::COUNT,
ByteBits<54>::COUNT,
ByteBits<55>::COUNT,
ByteBits<56>::COUNT,
ByteBits<57>::COUNT,
ByteBits<58>::COUNT,
ByteBits<59>::COUNT,
ByteBits<60>::COUNT,
ByteBits<61>::COUNT,
ByteBits<62>::COUNT,
ByteBits<63>::COUNT,
ByteBits<64>::COUNT,
ByteBits<65>::COUNT,
ByteBits<66>::COUNT,
ByteBits<67>::COUNT,
ByteBits<68>::COUNT,
ByteBits<69>::COUNT,
ByteBits<70>::COUNT,
ByteBits<71>::COUNT,
ByteBits<72>::COUNT,
ByteBits<73>::COUNT,
ByteBits<74>::COUNT,
ByteBits<75>::COUNT,
ByteBits<76>::COUNT,
ByteBits<77>::COUNT,
ByteBits<78>::COUNT,
ByteBits<79>::COUNT,
ByteBits<80>::COUNT,
ByteBits<81>::COUNT,
ByteBits<82>::COUNT,
ByteBits<83>::COUNT,
ByteBits<84>::COUNT,
ByteBits<85>::COUNT,
ByteBits<86>::COUNT,
ByteBits<87>::COUNT,
ByteBits<88>::COUNT,
ByteBits<89>::COUNT,
ByteBits<90>::COUNT,
ByteBits<91>::COUNT,
ByteBits<92>::COUNT,
ByteBits<93>::COUNT,
ByteBits<94>::COUNT,
ByteBits<95>::COUNT,
ByteBits<96>::COUNT,
ByteBits<97>::COUNT,
ByteBits<98>::COUNT,
ByteBits<99>::COUNT,
ByteBits<100>::COUNT,
ByteBits<101>::COUNT,
ByteBits<102>::COUNT,
ByteBits<103>::COUNT,
ByteBits<104>::COUNT,
ByteBits<105>::COUNT,
ByteBits<106>::COUNT,
ByteBits<107>::COUNT,
ByteBits<108>::COUNT,
ByteBits<109>::COUNT,
ByteBits<110>::COUNT,
ByteBits<111>::COUNT,
ByteBits<112>::COUNT,
ByteBits<113>::COUNT,
ByteBits<114>::COUNT,
ByteBits<115>::COUNT,
ByteBits<116>::COUNT,
ByteBits<117>::COUNT,
ByteBits<118>::COUNT,
ByteBits<119>::COUNT,
ByteBits<120>::COUNT,
ByteBits<121>::COUNT,
ByteBits<122>::COUNT,
ByteBits<123>::COUNT,
ByteBits<124>::COUNT,
ByteBits<125>::COUNT,
ByteBits<126>::COUNT,
ByteBits<127>::COUNT,
ByteBits<128>::COUNT,
ByteBits<129>::COUNT,
ByteBits<130>::COUNT,
ByteBits<131>::COUNT,
ByteBits<132>::COUNT,
ByteBits<133>::COUNT,
ByteBits<134>::COUNT,
ByteBits<135>::COUNT,
ByteBits<136>::COUNT,
ByteBits<137>::COUNT,
ByteBits<138>::COUNT,
ByteBits<139>::COUNT,
ByteBits<140>::COUNT,
ByteBits<141>::COUNT,
ByteBits<142>::COUNT,
ByteBits<143>::COUNT,
ByteBits<144>::COUNT,
ByteBits<145>::COUNT,
ByteBits<146>::COUNT,
ByteBits<147>::COUNT,
ByteBits<148>::COUNT,
ByteBits<149>::COUNT,
ByteBits<150>::COUNT,
ByteBits<151>::COUNT,
ByteBits<152>::COUNT,
ByteBits<153>::COUNT,
ByteBits<154>::COUNT,
ByteBits<155>::COUNT,
ByteBits<156>::COUNT,
ByteBits<157>::COUNT,
ByteBits<158>::COUNT,
ByteBits<159>::COUNT,
ByteBits<160>::COUNT,
ByteBits<161>::COUNT,
ByteBits<162>::COUNT,
ByteBits<163>::COUNT,
ByteBits<164>::COUNT,
ByteBits<165>::COUNT,
ByteBits<166>::COUNT,
ByteBits<167>::COUNT,
ByteBits<168>::COUNT,
ByteBits<169>::COUNT,
ByteBits<170>::COUNT,
ByteBits<171>::COUNT,
ByteBits<172>::COUNT,
ByteBits<173>::COUNT,
ByteBits<174>::COUNT,
ByteBits<175>::COUNT,
ByteBits<176>::COUNT,
ByteBits<177>::COUNT,
ByteBits<178>::COUNT,
ByteBits<179>::COUNT,
ByteBits<180>::COUNT,
ByteBits<181>::COUNT,
ByteBits<182>::COUNT,
ByteBits<183>::COUNT,
ByteBits<184>::COUNT,
ByteBits<185>::COUNT,
ByteBits<186>::COUNT,
ByteBits<187>::COUNT,
ByteBits<188>::COUNT,
ByteBits<189>::COUNT,
ByteBits<190>::COUNT,
ByteBits<191>::COUNT,
ByteBits<192>::COUNT,
ByteBits<193>::COUNT,
ByteBits<194>::COUNT,
ByteBits<195>::COUNT,
ByteBits<196>::COUNT,
ByteBits<197>::COUNT,
ByteBits<198>::COUNT,
ByteBits<199>::COUNT,
ByteBits<200>::COUNT,
ByteBits<201>::COUNT,
ByteBits<202>::COUNT,
ByteBits<203>::COUNT,
ByteBits<204>::COUNT,
ByteBits<205>::COUNT,
ByteBits<206>::COUNT,
ByteBits<207>::COUNT,
ByteBits<208>::COUNT,
ByteBits<209>::COUNT,
ByteBits<210>::COUNT,
ByteBits<211>::COUNT,
ByteBits<212>::COUNT,
ByteBits<213>::COUNT,
ByteBits<214>::COUNT,
ByteBits<215>::COUNT,
ByteBits<216>::COUNT,
ByteBits<217>::COUNT,
ByteBits<218>::COUNT,
ByteBits<219>::COUNT,
ByteBits<220>::COUNT,
ByteBits<221>::COUNT,
ByteBits<222>::COUNT,
ByteBits<223>::COUNT,
ByteBits<224>::COUNT,
ByteBits<225>::COUNT,
ByteBits<226>::COUNT,
ByteBits<227>::COUNT,
ByteBits<228>::COUNT,
ByteBits<229>::COUNT,
ByteBits<230>::COUNT,
ByteBits<231>::COUNT,
ByteBits<232>::COUNT,
ByteBits<233>::COUNT,
ByteBits<234>::COUNT,
ByteBits<235>::COUNT,
ByteBits<236>::COUNT,
ByteBits<237>::COUNT,
ByteBits<238>::COUNT,
ByteBits<239>::COUNT,
ByteBits<240>::COUNT,
ByteBits<241>::COUNT,
ByteBits<242>::COUNT,
ByteBits<243>::COUNT,
ByteBits<244>::COUNT,
ByteBits<245>::COUNT,
ByteBits<246>::COUNT,
ByteBits<247>::COUNT,
ByteBits<248>::COUNT,
ByteBits<249>::COUNT,
ByteBits<250>::COUNT,
ByteBits<251>::COUNT,
ByteBits<252>::COUNT,
ByteBits<253>::COUNT,
ByteBits<254>::COUNT,
ByteBits<255>::COUNT
};
return table[b];
unsigned char HammingLUT::byteBitsLookUp(unsigned char b)
{
static const unsigned char table[256] =
{
ByteBits<0>::COUNT,
ByteBits<1>::COUNT,
ByteBits<2>::COUNT,
ByteBits<3>::COUNT,
ByteBits<4>::COUNT,
ByteBits<5>::COUNT,
ByteBits<6>::COUNT,
ByteBits<7>::COUNT,
ByteBits<8>::COUNT,
ByteBits<9>::COUNT,
ByteBits<10>::COUNT,
ByteBits<11>::COUNT,
ByteBits<12>::COUNT,
ByteBits<13>::COUNT,
ByteBits<14>::COUNT,
ByteBits<15>::COUNT,
ByteBits<16>::COUNT,
ByteBits<17>::COUNT,
ByteBits<18>::COUNT,
ByteBits<19>::COUNT,
ByteBits<20>::COUNT,
ByteBits<21>::COUNT,
ByteBits<22>::COUNT,
ByteBits<23>::COUNT,
ByteBits<24>::COUNT,
ByteBits<25>::COUNT,
ByteBits<26>::COUNT,
ByteBits<27>::COUNT,
ByteBits<28>::COUNT,
ByteBits<29>::COUNT,
ByteBits<30>::COUNT,
ByteBits<31>::COUNT,
ByteBits<32>::COUNT,
ByteBits<33>::COUNT,
ByteBits<34>::COUNT,
ByteBits<35>::COUNT,
ByteBits<36>::COUNT,
ByteBits<37>::COUNT,
ByteBits<38>::COUNT,
ByteBits<39>::COUNT,
ByteBits<40>::COUNT,
ByteBits<41>::COUNT,
ByteBits<42>::COUNT,
ByteBits<43>::COUNT,
ByteBits<44>::COUNT,
ByteBits<45>::COUNT,
ByteBits<46>::COUNT,
ByteBits<47>::COUNT,
ByteBits<48>::COUNT,
ByteBits<49>::COUNT,
ByteBits<50>::COUNT,
ByteBits<51>::COUNT,
ByteBits<52>::COUNT,
ByteBits<53>::COUNT,
ByteBits<54>::COUNT,
ByteBits<55>::COUNT,
ByteBits<56>::COUNT,
ByteBits<57>::COUNT,
ByteBits<58>::COUNT,
ByteBits<59>::COUNT,
ByteBits<60>::COUNT,
ByteBits<61>::COUNT,
ByteBits<62>::COUNT,
ByteBits<63>::COUNT,
ByteBits<64>::COUNT,
ByteBits<65>::COUNT,
ByteBits<66>::COUNT,
ByteBits<67>::COUNT,
ByteBits<68>::COUNT,
ByteBits<69>::COUNT,
ByteBits<70>::COUNT,
ByteBits<71>::COUNT,
ByteBits<72>::COUNT,
ByteBits<73>::COUNT,
ByteBits<74>::COUNT,
ByteBits<75>::COUNT,
ByteBits<76>::COUNT,
ByteBits<77>::COUNT,
ByteBits<78>::COUNT,
ByteBits<79>::COUNT,
ByteBits<80>::COUNT,
ByteBits<81>::COUNT,
ByteBits<82>::COUNT,
ByteBits<83>::COUNT,
ByteBits<84>::COUNT,
ByteBits<85>::COUNT,
ByteBits<86>::COUNT,
ByteBits<87>::COUNT,
ByteBits<88>::COUNT,
ByteBits<89>::COUNT,
ByteBits<90>::COUNT,
ByteBits<91>::COUNT,
ByteBits<92>::COUNT,
ByteBits<93>::COUNT,
ByteBits<94>::COUNT,
ByteBits<95>::COUNT,
ByteBits<96>::COUNT,
ByteBits<97>::COUNT,
ByteBits<98>::COUNT,
ByteBits<99>::COUNT,
ByteBits<100>::COUNT,
ByteBits<101>::COUNT,
ByteBits<102>::COUNT,
ByteBits<103>::COUNT,
ByteBits<104>::COUNT,
ByteBits<105>::COUNT,
ByteBits<106>::COUNT,
ByteBits<107>::COUNT,
ByteBits<108>::COUNT,
ByteBits<109>::COUNT,
ByteBits<110>::COUNT,
ByteBits<111>::COUNT,
ByteBits<112>::COUNT,
ByteBits<113>::COUNT,
ByteBits<114>::COUNT,
ByteBits<115>::COUNT,
ByteBits<116>::COUNT,
ByteBits<117>::COUNT,
ByteBits<118>::COUNT,
ByteBits<119>::COUNT,
ByteBits<120>::COUNT,
ByteBits<121>::COUNT,
ByteBits<122>::COUNT,
ByteBits<123>::COUNT,
ByteBits<124>::COUNT,
ByteBits<125>::COUNT,
ByteBits<126>::COUNT,
ByteBits<127>::COUNT,
ByteBits<128>::COUNT,
ByteBits<129>::COUNT,
ByteBits<130>::COUNT,
ByteBits<131>::COUNT,
ByteBits<132>::COUNT,
ByteBits<133>::COUNT,
ByteBits<134>::COUNT,
ByteBits<135>::COUNT,
ByteBits<136>::COUNT,
ByteBits<137>::COUNT,
ByteBits<138>::COUNT,
ByteBits<139>::COUNT,
ByteBits<140>::COUNT,
ByteBits<141>::COUNT,
ByteBits<142>::COUNT,
ByteBits<143>::COUNT,
ByteBits<144>::COUNT,
ByteBits<145>::COUNT,
ByteBits<146>::COUNT,
ByteBits<147>::COUNT,
ByteBits<148>::COUNT,
ByteBits<149>::COUNT,
ByteBits<150>::COUNT,
ByteBits<151>::COUNT,
ByteBits<152>::COUNT,
ByteBits<153>::COUNT,
ByteBits<154>::COUNT,
ByteBits<155>::COUNT,
ByteBits<156>::COUNT,
ByteBits<157>::COUNT,
ByteBits<158>::COUNT,
ByteBits<159>::COUNT,
ByteBits<160>::COUNT,
ByteBits<161>::COUNT,
ByteBits<162>::COUNT,
ByteBits<163>::COUNT,
ByteBits<164>::COUNT,
ByteBits<165>::COUNT,
ByteBits<166>::COUNT,
ByteBits<167>::COUNT,
ByteBits<168>::COUNT,
ByteBits<169>::COUNT,
ByteBits<170>::COUNT,
ByteBits<171>::COUNT,
ByteBits<172>::COUNT,
ByteBits<173>::COUNT,
ByteBits<174>::COUNT,
ByteBits<175>::COUNT,
ByteBits<176>::COUNT,
ByteBits<177>::COUNT,
ByteBits<178>::COUNT,
ByteBits<179>::COUNT,
ByteBits<180>::COUNT,
ByteBits<181>::COUNT,
ByteBits<182>::COUNT,
ByteBits<183>::COUNT,
ByteBits<184>::COUNT,
ByteBits<185>::COUNT,
ByteBits<186>::COUNT,
ByteBits<187>::COUNT,
ByteBits<188>::COUNT,
ByteBits<189>::COUNT,
ByteBits<190>::COUNT,
ByteBits<191>::COUNT,
ByteBits<192>::COUNT,
ByteBits<193>::COUNT,
ByteBits<194>::COUNT,
ByteBits<195>::COUNT,
ByteBits<196>::COUNT,
ByteBits<197>::COUNT,
ByteBits<198>::COUNT,
ByteBits<199>::COUNT,
ByteBits<200>::COUNT,
ByteBits<201>::COUNT,
ByteBits<202>::COUNT,
ByteBits<203>::COUNT,
ByteBits<204>::COUNT,
ByteBits<205>::COUNT,
ByteBits<206>::COUNT,
ByteBits<207>::COUNT,
ByteBits<208>::COUNT,
ByteBits<209>::COUNT,
ByteBits<210>::COUNT,
ByteBits<211>::COUNT,
ByteBits<212>::COUNT,
ByteBits<213>::COUNT,
ByteBits<214>::COUNT,
ByteBits<215>::COUNT,
ByteBits<216>::COUNT,
ByteBits<217>::COUNT,
ByteBits<218>::COUNT,
ByteBits<219>::COUNT,
ByteBits<220>::COUNT,
ByteBits<221>::COUNT,
ByteBits<222>::COUNT,
ByteBits<223>::COUNT,
ByteBits<224>::COUNT,
ByteBits<225>::COUNT,
ByteBits<226>::COUNT,
ByteBits<227>::COUNT,
ByteBits<228>::COUNT,
ByteBits<229>::COUNT,
ByteBits<230>::COUNT,
ByteBits<231>::COUNT,
ByteBits<232>::COUNT,
ByteBits<233>::COUNT,
ByteBits<234>::COUNT,
ByteBits<235>::COUNT,
ByteBits<236>::COUNT,
ByteBits<237>::COUNT,
ByteBits<238>::COUNT,
ByteBits<239>::COUNT,
ByteBits<240>::COUNT,
ByteBits<241>::COUNT,
ByteBits<242>::COUNT,
ByteBits<243>::COUNT,
ByteBits<244>::COUNT,
ByteBits<245>::COUNT,
ByteBits<246>::COUNT,
ByteBits<247>::COUNT,
ByteBits<248>::COUNT,
ByteBits<249>::COUNT,
ByteBits<250>::COUNT,
ByteBits<251>::COUNT,
ByteBits<252>::COUNT,
ByteBits<253>::COUNT,
ByteBits<254>::COUNT,
ByteBits<255>::COUNT
};
return table[b];
}
} // namespace cv
......@@ -67,6 +67,21 @@ struct RoiPredicate
float minX, minY, maxX, maxY;
};
DescriptorExtractor::~DescriptorExtractor()
{}
void DescriptorExtractor::compute( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const
{
if( image.empty() || keypoints.empty() )
return;
// Check keypoints are in image. Do filter bad points here?
//for( size_t i = 0; i < keypoints.size(); i++ )
// CV_Assert( Rect(0,0, image.cols, image.rows).contains(keypoints[i].pt) );
computeImpl( image, keypoints, descriptors );
}
void DescriptorExtractor::compute( const vector<Mat>& imageCollection, vector<vector<KeyPoint> >& pointCollection, vector<Mat>& descCollection ) const
{
descCollection.resize( imageCollection.size() );
......@@ -74,27 +89,42 @@ void DescriptorExtractor::compute( const vector<Mat>& imageCollection, vector<ve
compute( imageCollection[i], pointCollection[i], descCollection[i] );
}
void DescriptorExtractor::read( const FileNode& )
{}
void DescriptorExtractor::write( FileStorage& ) const
{}
void DescriptorExtractor::removeBorderKeypoints( vector<KeyPoint>& keypoints,
Size imageSize, int borderPixels )
Size imageSize, int borderSize )
{
keypoints.erase( remove_if(keypoints.begin(), keypoints.end(),
RoiPredicate((float)borderPixels, (float)borderPixels,
(float)(imageSize.width - borderPixels),
(float)(imageSize.height - borderPixels))),
keypoints.end());
if( borderSize > 0)
{
keypoints.erase( remove_if(keypoints.begin(), keypoints.end(),
RoiPredicate((float)borderSize, (float)borderSize,
(float)(imageSize.width - borderSize),
(float)(imageSize.height - borderSize))),
keypoints.end() );
}
}
/****************************************************************************************\
* SiftDescriptorExtractor *
\****************************************************************************************/
SiftDescriptorExtractor::SiftDescriptorExtractor(const SIFT::DescriptorParams& descriptorParams,
const SIFT::CommonParams& commonParams)
: sift( descriptorParams.magnification, descriptorParams.isNormalize, descriptorParams.recalculateAngles,
commonParams.nOctaves, commonParams.nOctaveLayers, commonParams.firstOctave, commonParams.angleMode )
{}
SiftDescriptorExtractor::SiftDescriptorExtractor( double magnification, bool isNormalize, bool recalculateAngles,
int nOctaves, int nOctaveLayers, int firstOctave, int angleMode )
: sift( magnification, isNormalize, recalculateAngles, nOctaves, nOctaveLayers, firstOctave, angleMode )
{}
void SiftDescriptorExtractor::compute( const Mat& image,
vector<KeyPoint>& keypoints,
Mat& descriptors) const
void SiftDescriptorExtractor::computeImpl( const Mat& image,
vector<KeyPoint>& keypoints,
Mat& descriptors) const
{
bool useProvidedKeypoints = true;
Mat grayImage = image;
......@@ -131,6 +161,16 @@ void SiftDescriptorExtractor::write (FileStorage &fs) const
fs << "angleMode" << commParams.angleMode;
}
int SiftDescriptorExtractor::descriptorSize() const
{
return sift.descriptorSize();
}
int SiftDescriptorExtractor::descriptorType() const
{
return CV_32FC1;
}
/****************************************************************************************\
* SurfDescriptorExtractor *
\****************************************************************************************/
......@@ -139,9 +179,9 @@ SurfDescriptorExtractor::SurfDescriptorExtractor( int nOctaves,
: surf( 0.0, nOctaves, nOctaveLayers, extended )
{}
void SurfDescriptorExtractor::compute( const Mat& image,
vector<KeyPoint>& keypoints,
Mat& descriptors) const
void SurfDescriptorExtractor::computeImpl( const Mat& image,
vector<KeyPoint>& keypoints,
Mat& descriptors) const
{
// Compute descriptors for given keypoints
vector<float> _descriptors;
......@@ -175,11 +215,21 @@ void SurfDescriptorExtractor::write( FileStorage &fs ) const
fs << "extended" << surf.extended;
}
int SurfDescriptorExtractor::descriptorSize() const
{
return surf.descriptorSize();
}
int SurfDescriptorExtractor::descriptorType() const
{
return CV_32FC1;
}
/****************************************************************************************\
* OpponentColorDescriptorExtractor *
\****************************************************************************************/
OpponentColorDescriptorExtractor::OpponentColorDescriptorExtractor( const Ptr<DescriptorExtractor>& _dextractor ) :
dextractor(_dextractor)
OpponentColorDescriptorExtractor::OpponentColorDescriptorExtractor( const Ptr<DescriptorExtractor>& _descriptorExtractor ) :
descriptorExtractor(_descriptorExtractor)
{}
void convertBGRImageToOpponentColorSpace( const Mat& bgrImage, vector<Mat>& opponentChannels )
......@@ -246,33 +296,42 @@ void convertBGRImageToOpponentColorSpace( const Mat& bgrImage, vector<Mat>& oppo
}
}
void OpponentColorDescriptorExtractor::compute( const Mat& bgrImage, vector<KeyPoint>& keypoints, Mat& descriptors ) const
void OpponentColorDescriptorExtractor::computeImpl( const Mat& bgrImage, vector<KeyPoint>& keypoints, Mat& descriptors ) const
{
vector<Mat> opponentChannels;
convertBGRImageToOpponentColorSpace( bgrImage, opponentChannels );
// Compute descriptors three times, once for each Opponent channel
// and concatenate into a single color surf descriptor
int descriptorSize = dextractor->descriptorSize();
int descriptorSize = descriptorExtractor->descriptorSize();
descriptors.create( static_cast<int>(keypoints.size()), 3*descriptorSize, CV_32FC1 );
for( int i = 0; i < 3/*channel count*/; i++ )
{
CV_Assert( opponentChannels[i].type() == CV_8UC1 );
Mat opponentDescriptors = descriptors.colRange( i*descriptorSize, (i+1)*descriptorSize );
dextractor->compute( opponentChannels[i], keypoints, opponentDescriptors );
descriptorExtractor->compute( opponentChannels[i], keypoints, opponentDescriptors );
}
}
void OpponentColorDescriptorExtractor::read( const FileNode& fn )
{
dextractor->read( fn );
descriptorExtractor->read(fn);
}
void OpponentColorDescriptorExtractor::write( FileStorage& fs ) const
{
dextractor->write( fs );
descriptorExtractor->write(fs);
}
int OpponentColorDescriptorExtractor::descriptorSize() const
{
return 3*descriptorExtractor->descriptorSize();
}
int OpponentColorDescriptorExtractor::descriptorType() const
{
return descriptorExtractor->descriptorType();
}
/****************************************************************************************\
* Factory function for descriptor extractor creating *
\****************************************************************************************/
......
......@@ -61,6 +61,21 @@ struct MaskPredicate
const Mat& mask;
};
FeatureDetector::~FeatureDetector()
{}
void FeatureDetector::detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
{
keypoints.clear();
if( image.empty() )
return;
CV_Assert( mask.empty() || (mask.type() == CV_8UC1 && mask.size() == image.size()) );
detectImpl( image, keypoints, mask );
}
void FeatureDetector::detect(const vector<Mat>& imageCollection, vector<vector<KeyPoint> >& pointCollection, const vector<Mat>& masks ) const
{
pointCollection.resize( imageCollection.size() );
......@@ -76,6 +91,12 @@ void FeatureDetector::removeInvalidPoints( const Mat& mask, vector<KeyPoint>& ke
keypoints.erase(remove_if(keypoints.begin(), keypoints.end(), MaskPredicate(mask)), keypoints.end());
};
void FeatureDetector::read( const FileNode& )
{}
void FeatureDetector::write( FileStorage& ) const
{}
/*
* FastFeatureDetector
*/
......@@ -95,7 +116,7 @@ void FastFeatureDetector::write (FileStorage& fs) const
fs << "nonmaxSuppression" << nonmaxSuppression;
}
void FastFeatureDetector::detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
void FastFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
{
Mat grayImage = image;
if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY );
......@@ -106,14 +127,13 @@ void FastFeatureDetector::detect( const Mat& image, vector<KeyPoint>& 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)
GoodFeaturesToTrackDetector::Params::Params( 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::read (const FileNode& fn)
void GoodFeaturesToTrackDetector::Params::read (const FileNode& fn)
{
maxCorners = fn["maxCorners"];
qualityLevel = fn["qualityLevel"];
......@@ -123,7 +143,7 @@ void GoodFeaturesToTrackDetector::read (const FileNode& fn)
k = fn["k"];
}
void GoodFeaturesToTrackDetector::write (FileStorage& fs) const
void GoodFeaturesToTrackDetector::Params::write (FileStorage& fs) const
{
fs << "maxCorners" << maxCorners;
fs << "qualityLevel" << qualityLevel;
......@@ -133,20 +153,40 @@ void GoodFeaturesToTrackDetector::write (FileStorage& fs) const
fs << "k" << k;
}
void GoodFeaturesToTrackDetector::detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask) const
GoodFeaturesToTrackDetector::GoodFeaturesToTrackDetector( const Params& _params ) : params(_params)
{}
GoodFeaturesToTrackDetector::GoodFeaturesToTrackDetector( int maxCorners, double qualityLevel,
double minDistance, int blockSize,
bool useHarrisDetector, double k )
{
params = Params( maxCorners, qualityLevel, minDistance, blockSize, useHarrisDetector, k );
}
void GoodFeaturesToTrackDetector::read (const FileNode& fn)
{
params.read(fn);
}
void GoodFeaturesToTrackDetector::write (FileStorage& fs) const
{
params.write(fs);
}
void GoodFeaturesToTrackDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask) const
{
Mat grayImage = image;
if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY );
vector<Point2f> corners;
goodFeaturesToTrack( grayImage, corners, maxCorners, qualityLevel, minDistance, mask,
blockSize, useHarrisDetector, k );
goodFeaturesToTrack( grayImage, corners, params.maxCorners, params.qualityLevel, params.minDistance, mask,
params.blockSize, params.useHarrisDetector, params.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, (float)blockSize );
*keypoint_it = KeyPoint( *corner_it, (float)params.blockSize );
}
}
......@@ -198,13 +238,12 @@ void MserFeatureDetector::write (FileStorage& fs) const
}
void MserFeatureDetector::detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
void MserFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
{
vector<vector<Point> > msers;
mser(image, msers, mask);
keypoints.clear();
vector<vector<Point> >::const_iterator contour_it = msers.begin();
for( ; contour_it != msers.end(); ++contour_it )
{
......@@ -220,6 +259,12 @@ void MserFeatureDetector::detect( const Mat& image, vector<KeyPoint>& keypoints,
/*
* StarFeatureDetector
*/
StarFeatureDetector::StarFeatureDetector( const CvStarDetectorParams& params )
: star( params.maxSize, params.responseThreshold, params.lineThresholdProjected,
params.lineThresholdBinarized, params.suppressNonmaxSize)
{}
StarFeatureDetector::StarFeatureDetector(int maxSize, int responseThreshold,
int lineThresholdProjected,
int lineThresholdBinarized,
......@@ -251,7 +296,7 @@ void StarFeatureDetector::write (FileStorage& fs) const
fs << "suppressNonmaxSize" << star.suppressNonmaxSize;
}
void StarFeatureDetector::detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
void StarFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
{
Mat grayImage = image;
if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY );
......@@ -263,13 +308,20 @@ void StarFeatureDetector::detect( const Mat& image, vector<KeyPoint>& keypoints,
/*
* SiftFeatureDetector
*/
SiftFeatureDetector::SiftFeatureDetector(double threshold, double edgeThreshold,
int nOctaves, int nOctaveLayers, int firstOctave, int angleMode) :
SiftFeatureDetector::SiftFeatureDetector( const SIFT::DetectorParams &detectorParams,
const SIFT::CommonParams &commonParams )
: sift(detectorParams.threshold, detectorParams.edgeThreshold,
commonParams.nOctaves, commonParams.nOctaveLayers, commonParams.firstOctave, commonParams.angleMode)
{
}
SiftFeatureDetector::SiftFeatureDetector( double threshold, double edgeThreshold,
int nOctaves, int nOctaveLayers, int firstOctave, int angleMode ) :
sift(threshold, edgeThreshold, nOctaves, nOctaveLayers, firstOctave, angleMode)
{
}
void SiftFeatureDetector::read (const FileNode& fn)
void SiftFeatureDetector::read( const FileNode& fn )
{
double threshold = fn["threshold"];
double edgeThreshold = fn["edgeThreshold"];
......@@ -296,7 +348,7 @@ void SiftFeatureDetector::write (FileStorage& fs) const
}
void SiftFeatureDetector::detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
void SiftFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
{
Mat grayImage = image;
if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY );
......@@ -329,7 +381,7 @@ void SurfFeatureDetector::write (FileStorage& fs) const
fs << "octaveLayers" << surf.nOctaveLayers;
}
void SurfFeatureDetector::detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
void SurfFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
{
Mat grayImage = image;
if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY );
......@@ -340,14 +392,24 @@ void SurfFeatureDetector::detect( const Mat& image, vector<KeyPoint>& keypoints,
/*
* DenseFeatureDetector
*/
void DenseFeatureDetector::detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
{
keypoints.clear();
DenseFeatureDetector::Params::Params( float _initFeatureScale, int _featureScaleLevels,
float _featureScaleMul, int _initXyStep,
int _initImgBound, bool _varyXyStepWithScale,
bool _varyImgBoundWithScale ) :
initFeatureScale(_initFeatureScale), featureScaleLevels(_featureScaleLevels),
featureScaleMul(_featureScaleMul), initXyStep(_initXyStep), initImgBound(_initImgBound),
varyXyStepWithScale(_varyXyStepWithScale), varyImgBoundWithScale(_varyImgBoundWithScale)
{}
DenseFeatureDetector::DenseFeatureDetector(const DenseFeatureDetector::Params &_params) : params(_params)
{}
float curScale = initFeatureScale;
int curStep = initXyStep;
int curBound = initImgBound;
for( int curLevel = 0; curLevel < featureScaleLevels; curLevel++ )
void DenseFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
{
float curScale = params.initFeatureScale;
int curStep = params.initXyStep;
int curBound = params.initImgBound;
for( int curLevel = 0; curLevel < params.featureScaleLevels; curLevel++ )
{
for( int x = curBound; x < image.cols - curBound; x += curStep )
{
......@@ -357,9 +419,9 @@ void DenseFeatureDetector::detect( const Mat& image, vector<KeyPoint>& keypoints
}
}
curScale = curScale * featureScaleMul;
if( varyXyStepWithScale ) curStep = static_cast<int>( curStep * featureScaleMul + 0.5f );
if( varyImgBoundWithScale ) curBound = static_cast<int>( curBound * featureScaleMul + 0.5f );
curScale = curScale * params.featureScaleMul;
if( params.varyXyStepWithScale ) curStep = static_cast<int>( curStep * params.featureScaleMul + 0.5f );
if( params.varyImgBoundWithScale ) curBound = static_cast<int>( curBound * params.featureScaleMul + 0.5f );
}
removeInvalidPoints( mask, keypoints );
......@@ -391,9 +453,8 @@ void keepStrongest( int N, vector<KeyPoint>& keypoints )
}
}
void GridAdaptedFeatureDetector::detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
void GridAdaptedFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
{
keypoints.clear();
keypoints.reserve(maxTotalKeypoints);
int maxPerCell = maxTotalKeypoints / (gridRows * gridCols);
......@@ -430,7 +491,7 @@ PyramidAdaptedFeatureDetector::PyramidAdaptedFeatureDetector( const Ptr<FeatureD
: detector(_detector), levels(_levels)
{}
void PyramidAdaptedFeatureDetector::detect( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
void PyramidAdaptedFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask ) const
{
Mat src = image;
for( int l = 0, multiplier = 1; l <= levels; ++l, multiplier *= 2 )
......@@ -463,12 +524,11 @@ Ptr<FeatureDetector> createFeatureDetector( const string& detectorType )
FeatureDetector* fd = 0;
if( !detectorType.compare( "FAST" ) )
{
fd = new FastFeatureDetector( 10/*threshold*/, true/*nonmax_suppression*/ );
fd = new FastFeatureDetector();
}
else if( !detectorType.compare( "STAR" ) )
{
fd = new StarFeatureDetector( 16/*max_size*/, 5/*response_threshold*/, 10/*line_threshold_projected*/,
8/*line_threshold_binarized*/, 5/*suppress_nonmax_size*/ );
fd = new StarFeatureDetector();
}
else if( !detectorType.compare( "SIFT" ) )
{
......@@ -477,23 +537,21 @@ Ptr<FeatureDetector> createFeatureDetector( const string& detectorType )
}
else if( !detectorType.compare( "SURF" ) )
{
fd = new SurfFeatureDetector( 400./*hessian_threshold*/, 3 /*octaves*/, 4/*octave_layers*/ );
fd = new SurfFeatureDetector();
}
else if( !detectorType.compare( "MSER" ) )
{
fd = new MserFeatureDetector( 5/*delta*/, 60/*min_area*/, 14400/*_max_area*/, 0.25f/*max_variation*/,
0.2/*min_diversity*/, 200/*max_evolution*/, 1.01/*area_threshold*/, 0.003/*min_margin*/,
5/*edge_blur_size*/ );
fd = new MserFeatureDetector();
}
else if( !detectorType.compare( "GFTT" ) )
{
fd = new GoodFeaturesToTrackDetector( 1000/*maxCorners*/, 0.01/*qualityLevel*/, 1./*minDistance*/,
3/*int _blockSize*/, false/*useHarrisDetector*/, 0.04/*k*/ );
fd = new GoodFeaturesToTrackDetector();
}
else if( !detectorType.compare( "HARRIS" ) )
{
fd = new GoodFeaturesToTrackDetector( 1000/*maxCorners*/, 0.01/*qualityLevel*/, 1./*minDistance*/,
3/*int _blockSize*/, true/*useHarrisDetector*/, 0.04/*k*/ );
GoodFeaturesToTrackDetector::Params params;
params.useHarrisDetector = true;
fd = new GoodFeaturesToTrackDetector(params);
}
return fd;
}
......
......@@ -71,11 +71,23 @@ Mat windowedMatchingMask( const vector<KeyPoint>& keypoints1, const vector<KeyPo
/****************************************************************************************\
* DescriptorMatcher *
\****************************************************************************************/
void DescriptorMatcher::DescriptorCollection::set( const vector<Mat>& descCollection )
DescriptorMatcher::DescriptorCollection::DescriptorCollection()
{}
DescriptorMatcher::DescriptorCollection::DescriptorCollection( const DescriptorCollection& collection )
{
mergedDescriptors = collection.mergedDescriptors.clone();
copy( collection.startIdxs.begin(), collection.startIdxs.begin(), startIdxs.begin() );
}
DescriptorMatcher::DescriptorCollection::~DescriptorCollection()
{}
void DescriptorMatcher::DescriptorCollection::set( const vector<Mat>& descriptors )
{
clear();
size_t imageCount = descCollection.size();
size_t imageCount = descriptors.size();
CV_Assert( imageCount > 0 );
startIdxs.resize( imageCount );
......@@ -86,35 +98,35 @@ void DescriptorMatcher::DescriptorCollection::set( const vector<Mat>& descCollec
for( size_t i = 1; i < imageCount; i++ )
{
int s = 0;
if( !descCollection[i-1].empty() )
if( !descriptors[i-1].empty() )
{
dim = descCollection[i-1].cols;
type = descCollection[i-1].type();
s = descCollection[i-1].rows;
dim = descriptors[i-1].cols;
type = descriptors[i-1].type();
s = descriptors[i-1].rows;
}
startIdxs[i] = startIdxs[i-1] + s;
}
if( imageCount == 1 )
{
if( descCollection[0].empty() ) return;
if( descriptors[0].empty() ) return;
dim = descCollection[0].cols;
type = descCollection[0].type();
dim = descriptors[0].cols;
type = descriptors[0].type();
}
assert( dim > 0 );
int count = startIdxs[imageCount-1] + descCollection[imageCount-1].rows;
int count = startIdxs[imageCount-1] + descriptors[imageCount-1].rows;
if( count > 0 )
{
dmatrix.create( count, dim, type );
mergedDescriptors.create( count, dim, type );
for( size_t i = 0; i < imageCount; i++ )
{
if( !descCollection[i].empty() )
if( !descriptors[i].empty() )
{
CV_Assert( descCollection[i].cols == dim && descCollection[i].type() == type );
Mat m = dmatrix.rowRange( startIdxs[i], startIdxs[i] + descCollection[i].rows );
descCollection[i].copyTo(m);
CV_Assert( descriptors[i].cols == dim && descriptors[i].type() == type );
Mat m = mergedDescriptors.rowRange( startIdxs[i], startIdxs[i] + descriptors[i].rows );
descriptors[i].copyTo(m);
}
}
}
......@@ -123,7 +135,7 @@ void DescriptorMatcher::DescriptorCollection::set( const vector<Mat>& descCollec
void DescriptorMatcher::DescriptorCollection::clear()
{
startIdxs.clear();
dmatrix.release();
mergedDescriptors.release();
}
const Mat DescriptorMatcher::DescriptorCollection::getDescriptor( int imgIdx, int localDescIdx ) const
......@@ -135,10 +147,15 @@ const Mat DescriptorMatcher::DescriptorCollection::getDescriptor( int imgIdx, in
return getDescriptor( globalIdx );
}
const Mat& DescriptorMatcher::DescriptorCollection::getDescriptors() const
{
return mergedDescriptors;
}
const Mat DescriptorMatcher::DescriptorCollection::getDescriptor( int globalDescIdx ) const
{
CV_Assert( globalDescIdx < size() );
return dmatrix.row( globalDescIdx );
return mergedDescriptors.row( globalDescIdx );
}
void DescriptorMatcher::DescriptorCollection::getLocalIdx( int globalDescIdx, int& imgIdx, int& localDescIdx ) const
......@@ -157,6 +174,11 @@ void DescriptorMatcher::DescriptorCollection::getLocalIdx( int globalDescIdx, in
localDescIdx = globalDescIdx - startIdxs[imgIdx];
}
int DescriptorMatcher::DescriptorCollection::size() const
{
return mergedDescriptors.rows;
}
/*
* DescriptorMatcher
*/
......@@ -172,9 +194,17 @@ void convertMatches( const vector<vector<DMatch> >& knnMatches, vector<DMatch>&
}
}
void DescriptorMatcher::add( const vector<Mat>& descCollection )
DescriptorMatcher::~DescriptorMatcher()
{}
void DescriptorMatcher::add( const vector<Mat>& descriptors )
{
trainDescCollection.insert( trainDescCollection.end(), descriptors.begin(), descriptors.end() );
}
const vector<Mat>& DescriptorMatcher::getTrainDescriptors() const
{
trainDescCollection.insert( trainDescCollection.end(), descCollection.begin(), descCollection.end() );
return trainDescCollection;
}
void DescriptorMatcher::clear()
......@@ -182,67 +212,134 @@ void DescriptorMatcher::clear()
trainDescCollection.clear();
}
void DescriptorMatcher::match( const Mat& queryDescs, const Mat& trainDescs, vector<DMatch>& matches, const Mat& mask ) const
bool DescriptorMatcher::empty() const
{
return trainDescCollection.size() == 0;
}
void DescriptorMatcher::train()
{}
void DescriptorMatcher::match( const Mat& queryDescriptors, const Mat& trainDescriptors, vector<DMatch>& matches, const Mat& mask ) const
{
Ptr<DescriptorMatcher> tempMatcher = cloneWithoutData();
tempMatcher->add( vector<Mat>(1, trainDescs) );
tempMatcher->match( queryDescs, matches, vector<Mat>(1, mask) );
Ptr<DescriptorMatcher> tempMatcher = clone(true);
tempMatcher->add( vector<Mat>(1, trainDescriptors) );
tempMatcher->match( queryDescriptors, matches, vector<Mat>(1, mask) );
}
void DescriptorMatcher::knnMatch( const Mat& queryDescs, const Mat& trainDescs, vector<vector<DMatch> >& matches, int knn,
void DescriptorMatcher::knnMatch( const Mat& queryDescriptors, const Mat& trainDescriptors, vector<vector<DMatch> >& matches, int knn,
const Mat& mask, bool compactResult ) const
{
Ptr<DescriptorMatcher> tempMatcher = cloneWithoutData();
tempMatcher->add( vector<Mat>(1, trainDescs) );
tempMatcher->knnMatch( queryDescs, matches, knn, vector<Mat>(1, mask), compactResult );
Ptr<DescriptorMatcher> tempMatcher = clone(true);
tempMatcher->add( vector<Mat>(1, trainDescriptors) );
tempMatcher->knnMatch( queryDescriptors, matches, knn, vector<Mat>(1, mask), compactResult );
}
void DescriptorMatcher::radiusMatch( const Mat& queryDescs, const Mat& trainDescs, vector<vector<DMatch> >& matches, float maxDistance,
void DescriptorMatcher::radiusMatch( const Mat& queryDescriptors, const Mat& trainDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
const Mat& mask, bool compactResult ) const
{
Ptr<DescriptorMatcher> tempMatcher = cloneWithoutData();
tempMatcher->add( vector<Mat>(1, trainDescs) );
tempMatcher->radiusMatch( queryDescs, matches, maxDistance, vector<Mat>(1, mask), compactResult );
Ptr<DescriptorMatcher> tempMatcher = clone(true);
tempMatcher->add( vector<Mat>(1, trainDescriptors) );
tempMatcher->radiusMatch( queryDescriptors, matches, maxDistance, vector<Mat>(1, mask), compactResult );
}
void DescriptorMatcher::match( const Mat& queryDescs, vector<DMatch>& matches, const vector<Mat>& masks )
void DescriptorMatcher::match( const Mat& queryDescriptors, vector<DMatch>& matches, const vector<Mat>& masks )
{
vector<vector<DMatch> > knnMatches;
knnMatch( queryDescs, knnMatches, 1, masks, true /*compactResult*/ );
knnMatch( queryDescriptors, knnMatches, 1, masks, true /*compactResult*/ );
convertMatches( knnMatches, matches );
}
void DescriptorMatcher::knnMatch( const Mat& queryDescs, vector<vector<DMatch> >& matches, int knn,
void DescriptorMatcher::checkMasks( const vector<Mat>& masks, int queryDescriptorsCount ) const
{
if( isMaskSupported() && !masks.empty() )
{
// Check masks
size_t imageCount = trainDescCollection.size();
CV_Assert( masks.size() == imageCount );
for( size_t i = 0; i < imageCount; i++ )
{
if( !masks[i].empty() && !trainDescCollection[i].empty() )
{
CV_Assert( masks[i].rows == queryDescriptorsCount &&
masks[i].cols == trainDescCollection[i].rows &&
masks[i].type() == CV_8UC1 );
}
}
}
}
void DescriptorMatcher::knnMatch( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int knn,
const vector<Mat>& masks, bool compactResult )
{
matches.empty();
if( empty() || queryDescriptors.empty() )
return;
CV_Assert( knn > 0 );
checkMasks( masks, queryDescriptors.rows );
train();
knnMatchImpl( queryDescs, matches, knn, masks, compactResult );
knnMatchImpl( queryDescriptors, matches, knn, masks, compactResult );
}
void DescriptorMatcher::radiusMatch( const Mat& queryDescs, vector<vector<DMatch> >& matches, float maxDistance,
void DescriptorMatcher::radiusMatch( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks, bool compactResult )
{
matches.empty();
if( empty() || queryDescriptors.empty() )
return;
CV_Assert( maxDistance > std::numeric_limits<float>::epsilon() );
checkMasks( masks, queryDescriptors.rows );
train();
radiusMatchImpl( queryDescs, matches, maxDistance, masks, compactResult );
radiusMatchImpl( queryDescriptors, matches, maxDistance, masks, compactResult );
}
void DescriptorMatcher::read( const FileNode& )
{}
void DescriptorMatcher::write( FileStorage& ) const
{}
bool DescriptorMatcher::isPossibleMatch( const Mat& mask, int queryIdx, int trainIdx )
{
return mask.empty() || mask.at<uchar>(queryIdx, trainIdx);
}
bool DescriptorMatcher::isMaskedOut( const vector<Mat>& masks, int queryIdx )
{
size_t outCount = 0;
for( size_t i = 0; i < masks.size(); i++ )
{
if( !masks[i].empty() && (countNonZero(masks[i].row(queryIdx)) == 0) )
outCount++;
}
return !masks.empty() && outCount == masks.size() ;
}
template<>
void BruteForceMatcher<L2<float> >::knnMatchImpl( const Mat& queryDescs, vector<vector<DMatch> >& matches, int knn,
const vector<Mat>& masks, bool compactResult )
void BruteForceMatcher<L2<float> >::knnMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int knn,
const vector<Mat>& masks, bool compactResult )
{
#ifndef HAVE_EIGEN2
bfKnnMatchImpl( *this, queryDescs, matches, knn, masks, compactResult );
commonKnnMatchImpl( *this, queryDescriptors, matches, knn, masks, compactResult );
#else
CV_Assert( queryDescs.type() == CV_32FC1 || queryDescs.empty() );
CV_Assert( queryDescriptors.type() == CV_32FC1 || queryDescriptors.empty() );
CV_Assert( masks.empty() || masks.size() == trainDescCollection.size() );
matches.reserve(queryDescs.rows);
matches.reserve(queryDescriptors.rows);
size_t imgCount = trainDescCollection.size();
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> e_query_t;
vector<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> > e_trainCollection(trainDescCollection.size());
vector<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> > e_trainNorms2(trainDescCollection.size());
cv2eigen( queryDescs.t(), e_query_t);
cv2eigen( queryDescriptors.t(), e_query_t);
for( size_t i = 0; i < trainDescCollection.size(); i++ )
{
cv2eigen( trainDescCollection[i], e_trainCollection[i] );
......@@ -251,7 +348,7 @@ void BruteForceMatcher<L2<float> >::knnMatchImpl( const Mat& queryDescs, vector<
vector<Eigen::Matrix<float, Eigen::Dynamic, 1> > e_allDists( imgCount ); // distances between one query descriptor and all train descriptors
for( int qIdx = 0; qIdx < queryDescs.rows; qIdx++ )
for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ )
{
if( maskedOut( masks, qIdx ) )
{
......@@ -265,10 +362,10 @@ void BruteForceMatcher<L2<float> >::knnMatchImpl( const Mat& queryDescs, vector<
for( size_t iIdx = 0; iIdx < imgCount; iIdx++ )
{
CV_Assert( masks.empty() || masks[iIdx].empty() ||
( masks[iIdx].rows == queryDescs.rows && masks[iIdx].cols == trainDescCollection[iIdx].rows &&
( masks[iIdx].rows == queryDescriptors.rows && masks[iIdx].cols == trainDescCollection[iIdx].rows &&
masks[iIdx].type() == CV_8UC1 ) );
CV_Assert( trainDescCollection[iIdx].type() == CV_32FC1 || trainDescCollection[iIdx].empty() );
CV_Assert( queryDescs.cols == trainDescCollection[iIdx].cols );
CV_Assert( queryDescriptors.cols == trainDescCollection[iIdx].cols );
e_allDists[iIdx] = e_trainCollection[iIdx] *e_query_t.col(qIdx);
e_allDists[iIdx] -= e_trainNorms2[iIdx];
......@@ -315,22 +412,22 @@ void BruteForceMatcher<L2<float> >::knnMatchImpl( const Mat& queryDescs, vector<
}
template<>
void BruteForceMatcher<L2<float> >::radiusMatchImpl( const Mat& queryDescs, vector<vector<DMatch> >& matches, float maxDistance,
void BruteForceMatcher<L2<float> >::radiusMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks, bool compactResult )
{
#ifndef HAVE_EIGEN2
bfRadiusMatchImpl( *this, queryDescs, matches, maxDistance, masks, compactResult );
commonRadiusMatchImpl( *this, queryDescriptors, matches, maxDistance, masks, compactResult );
#else
CV_Assert( queryDescs.type() == CV_32FC1 || queryDescs.empty() );
CV_Assert( queryDescriptors.type() == CV_32FC1 || queryDescriptors.empty() );
CV_Assert( masks.empty() || masks.size() == trainDescCollection.size() );
matches.reserve(queryDescs.rows);
matches.reserve(queryDescriptors.rows);
size_t imgCount = trainDescCollection.size();
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> e_query_t;
vector<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> > e_trainCollection(trainDescCollection.size());
vector<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> > e_trainNorms2(trainDescCollection.size());
cv2eigen( queryDescs.t(), e_query_t);
cv2eigen( queryDescriptors.t(), e_query_t);
for( size_t i = 0; i < trainDescCollection.size(); i++ )
{
cv2eigen( trainDescCollection[i], e_trainCollection[i] );
......@@ -339,7 +436,7 @@ void BruteForceMatcher<L2<float> >::radiusMatchImpl( const Mat& queryDescs, vect
vector<Eigen::Matrix<float, Eigen::Dynamic, 1> > e_allDists( imgCount ); // distances between one query descriptor and all train descriptors
for( int qIdx = 0; qIdx < queryDescs.rows; qIdx++ )
for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ )
{
if( maskedOut( masks, qIdx ) )
{
......@@ -353,10 +450,10 @@ void BruteForceMatcher<L2<float> >::radiusMatchImpl( const Mat& queryDescs, vect
for( size_t iIdx = 0; iIdx < imgCount; iIdx++ )
{
CV_Assert( masks.empty() || masks[iIdx].empty() ||
( masks[iIdx].rows == queryDescs.rows && masks[iIdx].cols == trainDescCollection[iIdx].rows &&
( masks[iIdx].rows == queryDescriptors.rows && masks[iIdx].cols == trainDescCollection[iIdx].rows &&
masks[iIdx].type() == CV_8UC1 ) );
CV_Assert( trainDescCollection[iIdx].type() == CV_32FC1 || trainDescCollection[iIdx].empty() );
CV_Assert( queryDescs.cols == trainDescCollection[iIdx].cols );
CV_Assert( queryDescriptors.cols == trainDescCollection[iIdx].cols );
e_allDists[iIdx] = e_trainCollection[iIdx] *e_query_t.col(qIdx);
e_allDists[iIdx] -= e_trainNorms2[iIdx];
......@@ -393,12 +490,12 @@ FlannBasedMatcher::FlannBasedMatcher( const Ptr<flann::IndexParams>& _indexParam
CV_Assert( !_searchParams.empty() );
}
void FlannBasedMatcher::add( const vector<Mat>& descCollection )
void FlannBasedMatcher::add( const vector<Mat>& descriptors )
{
DescriptorMatcher::add( descCollection );
for( size_t i = 0; i < descCollection.size(); i++ )
DescriptorMatcher::add( descriptors );
for( size_t i = 0; i < descriptors.size(); i++ )
{
addedDescCount += descCollection[i].rows;
addedDescCount += descriptors[i].rows;
}
}
......@@ -421,6 +518,27 @@ void FlannBasedMatcher::train()
}
}
bool FlannBasedMatcher::isMaskSupported() const
{
return false;
}
Ptr<DescriptorMatcher> FlannBasedMatcher::clone( bool emptyTrainData ) const
{
FlannBasedMatcher* matcher = new FlannBasedMatcher(indexParams, searchParams);
if( !emptyTrainData )
{
CV_Error( CV_StsNotImplemented, "deep clone functionality is not implemented, because "
"Flann::Index has not copy constructor or clone method ");
//matcher->flannIndex;
matcher->addedDescCount = addedDescCount;
matcher->mergedDescriptors = DescriptorCollection( mergedDescriptors );
transform( trainDescCollection.begin(), trainDescCollection.end(),
matcher->trainDescCollection.begin(), clone_op );
}
return matcher;
}
void FlannBasedMatcher::convertToDMatches( const DescriptorCollection& collection, const Mat& indices, const Mat& dists,
vector<vector<DMatch> >& matches )
{
......@@ -440,28 +558,28 @@ void FlannBasedMatcher::convertToDMatches( const DescriptorCollection& collectio
}
}
void FlannBasedMatcher::knnMatchImpl( const Mat& queryDescs, vector<vector<DMatch> >& matches, int knn,
void FlannBasedMatcher::knnMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int knn,
const vector<Mat>& /*masks*/, bool /*compactResult*/ )
{
Mat indices( queryDescs.rows, knn, CV_32SC1 );
Mat dists( queryDescs.rows, knn, CV_32FC1);
flannIndex->knnSearch( queryDescs, indices, dists, knn, *searchParams );
Mat indices( queryDescriptors.rows, knn, CV_32SC1 );
Mat dists( queryDescriptors.rows, knn, CV_32FC1);
flannIndex->knnSearch( queryDescriptors, indices, dists, knn, *searchParams );
convertToDMatches( mergedDescriptors, indices, dists, matches );
}
void FlannBasedMatcher::radiusMatchImpl( const Mat& queryDescs, vector<vector<DMatch> >& matches, float maxDistance,
void FlannBasedMatcher::radiusMatchImpl( const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& /*masks*/, bool /*compactResult*/ )
{
const int count = mergedDescriptors.size(); // TODO do count as param?
Mat indices( queryDescs.rows, count, CV_32SC1, Scalar::all(-1) );
Mat dists( queryDescs.rows, count, CV_32FC1, Scalar::all(-1) );
for( int qIdx = 0; qIdx < queryDescs.rows; qIdx++ )
Mat indices( queryDescriptors.rows, count, CV_32SC1, Scalar::all(-1) );
Mat dists( queryDescriptors.rows, count, CV_32FC1, Scalar::all(-1) );
for( int qIdx = 0; qIdx < queryDescriptors.rows; qIdx++ )
{
Mat queryDescsRow = queryDescs.row(qIdx);
Mat queryDescriptorsRow = queryDescriptors.row(qIdx);
Mat indicesRow = indices.row(qIdx);
Mat distsRow = dists.row(qIdx);
flannIndex->radiusSearch( queryDescsRow, indicesRow, distsRow, maxDistance*maxDistance, *searchParams );
flannIndex->radiusSearch( queryDescriptorsRow, indicesRow, distsRow, maxDistance*maxDistance, *searchParams );
}
convertToDMatches( mergedDescriptors, indices, dists, matches );
......@@ -507,6 +625,22 @@ Ptr<DescriptorMatcher> createDescriptorMatcher( const string& descriptorMatcherT
/*
* KeyPointCollection
*/
GenericDescriptorMatcher::KeyPointCollection::KeyPointCollection() : pointCount(0)
{}
GenericDescriptorMatcher::KeyPointCollection::KeyPointCollection( const KeyPointCollection& collection )
{
pointCount = collection.pointCount;
transform( collection.images.begin(), collection.images.end(), images.begin(), clone_op );
keypoints.resize( collection.keypoints.size() );
for( size_t i = 0; i < keypoints.size(); i++ )
copy( collection.keypoints[i].begin(), collection.keypoints[i].end(), keypoints[i].begin() );
copy( collection.startIndices.begin(), collection.startIndices.end(), startIndices.begin() );
}
void GenericDescriptorMatcher::KeyPointCollection::add( const vector<Mat>& _images,
const vector<vector<KeyPoint> >& _points )
{
......@@ -514,9 +648,9 @@ void GenericDescriptorMatcher::KeyPointCollection::add( const vector<Mat>& _imag
CV_Assert( _images.size() == _points.size() );
images.insert( images.end(), _images.begin(), _images.end() );
points.insert( points.end(), _points.begin(), _points.end() );
keypoints.insert( keypoints.end(), _points.begin(), _points.end() );
for( size_t i = 0; i < _points.size(); i++ )
size += _points[i].size();
pointCount += _points[i].size();
size_t prevSize = startIndices.size(), addSize = _images.size();
startIndices.resize( prevSize + addSize );
......@@ -524,37 +658,58 @@ void GenericDescriptorMatcher::KeyPointCollection::add( const vector<Mat>& _imag
if( prevSize == 0 )
startIndices[prevSize] = 0; //first
else
startIndices[prevSize] = startIndices[prevSize-1] + points[prevSize-1].size();
startIndices[prevSize] = startIndices[prevSize-1] + keypoints[prevSize-1].size();
for( size_t i = prevSize + 1; i < prevSize + addSize; i++ )
{
startIndices[i] = startIndices[i - 1] + points[i - 1].size();
startIndices[i] = startIndices[i - 1] + keypoints[i - 1].size();
}
}
void GenericDescriptorMatcher::KeyPointCollection::clear()
{
points.clear();
keypoints.clear();
}
size_t GenericDescriptorMatcher::KeyPointCollection::keypointCount() const
{
return pointCount;
}
size_t GenericDescriptorMatcher::KeyPointCollection::imageCount() const
{
return images.size();
}
const vector<vector<KeyPoint> >& GenericDescriptorMatcher::KeyPointCollection::getKeypoints() const
{
return keypoints;
}
const vector<KeyPoint>& GenericDescriptorMatcher::KeyPointCollection::getKeypoints( int imgIdx ) const
{
CV_Assert( imgIdx < (int)imageCount() );
return keypoints[imgIdx];
}
const KeyPoint& GenericDescriptorMatcher::KeyPointCollection::getKeyPoint( int imgIdx, int localPointIdx ) const
{
CV_Assert( imgIdx < (int)images.size() );
CV_Assert( localPointIdx < (int)points[imgIdx].size() );
return points[imgIdx][localPointIdx];
CV_Assert( localPointIdx < (int)keypoints[imgIdx].size() );
return keypoints[imgIdx][localPointIdx];
}
const KeyPoint& GenericDescriptorMatcher::KeyPointCollection::getKeyPoint( int globalPointIdx ) const
{
int imgIdx, localPointIdx;
getLocalIdx( globalPointIdx, imgIdx, localPointIdx );
return points[imgIdx][localPointIdx];
return keypoints[imgIdx][localPointIdx];
}
void GenericDescriptorMatcher::KeyPointCollection::getLocalIdx( int globalPointIdx, int& imgIdx, int& localPointIdx ) const
{
imgIdx = -1;
CV_Assert( globalPointIdx < (int)pointCount() );
CV_Assert( globalPointIdx < (int)keypointCount() );
for( size_t i = 1; i < startIndices.size(); i++ )
{
if( globalPointIdx < startIndices[i] )
......@@ -567,20 +722,50 @@ void GenericDescriptorMatcher::KeyPointCollection::getLocalIdx( int globalPointI
localPointIdx = globalPointIdx - startIndices[imgIdx];
}
const vector<Mat>& GenericDescriptorMatcher::KeyPointCollection::getImages() const
{
return images;
}
const Mat& GenericDescriptorMatcher::KeyPointCollection::getImage( int imgIdx ) const
{
CV_Assert( imgIdx < (int)imageCount() );
return images[imgIdx];
}
/*
* GenericDescriptorMatcher
*/
GenericDescriptorMatcher::GenericDescriptorMatcher()
{}
GenericDescriptorMatcher::~GenericDescriptorMatcher()
{}
void GenericDescriptorMatcher::add( const vector<Mat>& imgCollection,
vector<vector<KeyPoint> >& pointCollection )
{
trainPointCollection.add( imgCollection, pointCollection );
}
const vector<Mat>& GenericDescriptorMatcher::getTrainImages() const
{
return trainPointCollection.getImages();
}
const vector<vector<KeyPoint> >& GenericDescriptorMatcher::getTrainKeypoints() const
{
return trainPointCollection.getKeypoints();
}
void GenericDescriptorMatcher::clear()
{
trainPointCollection.clear();
}
void GenericDescriptorMatcher::train()
{}
void GenericDescriptorMatcher::classify( const Mat& queryImage, vector<KeyPoint>& queryPoints,
const Mat& trainImage, vector<KeyPoint>& trainPoints ) const
{
......@@ -606,7 +791,7 @@ void GenericDescriptorMatcher::match( const Mat& queryImg, vector<KeyPoint>& que
const Mat& trainImg, vector<KeyPoint>& trainPoints,
vector<DMatch>& matches, const Mat& mask ) const
{
Ptr<GenericDescriptorMatcher> tempMatcher = createEmptyMatcherCopy();
Ptr<GenericDescriptorMatcher> tempMatcher = clone( true );
vector<vector<KeyPoint> > vecTrainPoints(1, trainPoints);
tempMatcher->add( vector<Mat>(1, trainImg), vecTrainPoints );
tempMatcher->match( queryImg, queryPoints, matches, vector<Mat>(1, mask) );
......@@ -617,7 +802,7 @@ void GenericDescriptorMatcher::knnMatch( const Mat& queryImg, vector<KeyPoint>&
const Mat& trainImg, vector<KeyPoint>& trainPoints,
vector<vector<DMatch> >& matches, int knn, const Mat& mask, bool compactResult ) const
{
Ptr<GenericDescriptorMatcher> tempMatcher = createEmptyMatcherCopy();
Ptr<GenericDescriptorMatcher> tempMatcher = clone( true );
vector<vector<KeyPoint> > vecTrainPoints(1, trainPoints);
tempMatcher->add( vector<Mat>(1, trainImg), vecTrainPoints );
tempMatcher->knnMatch( queryImg, queryPoints, matches, knn, vector<Mat>(1, mask), compactResult );
......@@ -629,7 +814,7 @@ void GenericDescriptorMatcher::radiusMatch( const Mat& queryImg, vector<KeyPoint
vector<vector<DMatch> >& matches, float maxDistance,
const Mat& mask, bool compactResult ) const
{
Ptr<GenericDescriptorMatcher> tempMatcher = createEmptyMatcherCopy();
Ptr<GenericDescriptorMatcher> tempMatcher = clone( true );
vector<vector<KeyPoint> > vecTrainPoints(1, trainPoints);
tempMatcher->add( vector<Mat>(1, trainImg), vecTrainPoints );
tempMatcher->radiusMatch( queryImg, queryPoints, matches, maxDistance, vector<Mat>(1, mask), compactResult );
......@@ -660,9 +845,25 @@ void GenericDescriptorMatcher::radiusMatch( const Mat& queryImg, vector<KeyPoint
radiusMatchImpl( queryImg, queryPoints, matches, maxDistance, masks, compactResult );
}
void GenericDescriptorMatcher::read( const FileNode& )
{}
void GenericDescriptorMatcher::write( FileStorage& ) const
{}
/****************************************************************************************\
* OneWayDescriptorMatcher *
\****************************************************************************************/
OneWayDescriptorMatcher::Params::Params( int _poseCount, Size _patchSize, string _pcaFilename,
string _trainPath, string _trainImagesList,
float _minScale, float _maxScale, float _stepScale ) :
poseCount(_poseCount), patchSize(_patchSize), pcaFilename(_pcaFilename),
trainPath(_trainPath), trainImagesList(_trainImagesList),
minScale(_minScale), maxScale(_maxScale), stepScale(_stepScale)
{}
OneWayDescriptorMatcher::OneWayDescriptorMatcher( const Params& _params)
{
initialize(_params);
......@@ -691,13 +892,13 @@ void OneWayDescriptorMatcher::clear()
void OneWayDescriptorMatcher::train()
{
if( base.empty() || prevTrainCount < (int)trainPointCollection.pointCount() )
if( base.empty() || prevTrainCount < (int)trainPointCollection.keypointCount() )
{
base = new OneWayDescriptorObject( params.patchSize, params.poseCount, params.pcaFilename,
params.trainPath, params.trainImagesList, params.minScale, params.maxScale, params.stepScale );
base->Allocate( trainPointCollection.pointCount() );
prevTrainCount = trainPointCollection.pointCount();
base->Allocate( trainPointCollection.keypointCount() );
prevTrainCount = trainPointCollection.keypointCount();
const vector<vector<KeyPoint> >& points = trainPointCollection.getKeypoints();
int count = 0;
......@@ -714,6 +915,11 @@ void OneWayDescriptorMatcher::train()
}
}
bool OneWayDescriptorMatcher::isMaskSupported()
{
return false;
}
void OneWayDescriptorMatcher::knnMatchImpl( const Mat& queryImg, vector<KeyPoint>& queryPoints,
vector<vector<DMatch> >& matches, int knn,
const vector<Mat>& /*masks*/, bool /*compactResult*/ )
......@@ -763,6 +969,23 @@ void OneWayDescriptorMatcher::write( FileStorage& fs ) const
base->Write (fs);
}
Ptr<GenericDescriptorMatcher> OneWayDescriptorMatcher::clone( bool emptyTrainData ) const
{
OneWayDescriptorMatcher* matcher = new OneWayDescriptorMatcher( params );
if( !emptyTrainData )
{
CV_Error( CV_StsNotImplemented, "deep clone dunctionality is not implemented, because "
"OneWayDescriptorBase has not copy constructor or clone method ");
//matcher->base;
matcher->params = params;
matcher->prevTrainCount = prevTrainCount;
matcher->trainPointCollection = trainPointCollection;
}
return matcher;
}
/****************************************************************************************\
* FernDescriptorMatcher *
\****************************************************************************************/
......@@ -805,7 +1028,7 @@ void FernDescriptorMatcher::clear()
void FernDescriptorMatcher::train()
{
if( classifier.empty() || prevTrainCount < (int)trainPointCollection.pointCount() )
if( classifier.empty() || prevTrainCount < (int)trainPointCollection.keypointCount() )
{
assert( params.filename.empty() );
......@@ -819,6 +1042,11 @@ void FernDescriptorMatcher::train()
}
}
bool FernDescriptorMatcher::isMaskSupported()
{
return false;
}
void FernDescriptorMatcher::calcBestProbAndMatchIdx( const Mat& image, const Point2f& pt,
float& bestProb, int& bestMatchIdx, vector<float>& signature )
{
......@@ -921,16 +1149,42 @@ void FernDescriptorMatcher::write( FileStorage& fs ) const
// classifier->write(fs);
}
Ptr<GenericDescriptorMatcher> FernDescriptorMatcher::clone( bool emptyTrainData ) const
{
FernDescriptorMatcher* matcher = new FernDescriptorMatcher( params );
if( !emptyTrainData )
{
CV_Error( CV_StsNotImplemented, "deep clone dunctionality is not implemented, because "
"FernClassifier has not copy constructor or clone method ");
//matcher->classifier;
matcher->params = params;
matcher->prevTrainCount = prevTrainCount;
matcher->trainPointCollection = trainPointCollection;
}
return matcher;
}
/****************************************************************************************\
* VectorDescriptorMatcher *
* VectorDescriptorMatcher *
\****************************************************************************************/
VectorDescriptorMatcher::VectorDescriptorMatcher( const Ptr<DescriptorExtractor>& _extractor,
const Ptr<DescriptorMatcher>& _matcher )
: extractor( _extractor ), matcher( _matcher )
{
CV_Assert( !extractor.empty() && !matcher.empty() );
}
VectorDescriptorMatcher::~VectorDescriptorMatcher()
{}
void VectorDescriptorMatcher::add( const vector<Mat>& imgCollection,
vector<vector<KeyPoint> >& pointCollection )
{
vector<Mat> descCollection;
extractor->compute( imgCollection, pointCollection, descCollection );
vector<Mat> descriptors;
extractor->compute( imgCollection, pointCollection, descriptors );
matcher->add( descCollection );
matcher->add( descriptors );
trainPointCollection.add( imgCollection, pointCollection );
}
......@@ -947,28 +1201,33 @@ void VectorDescriptorMatcher::train()
matcher->train();
}
bool VectorDescriptorMatcher::isMaskSupported()
{
return matcher->isMaskSupported();
}
void VectorDescriptorMatcher::knnMatchImpl( const Mat& queryImg, vector<KeyPoint>& queryPoints,
vector<vector<DMatch> >& matches, int knn,
const vector<Mat>& masks, bool compactResult )
{
Mat queryDescs;
extractor->compute( queryImg, queryPoints, queryDescs );
matcher->knnMatch( queryDescs, matches, knn, masks, compactResult );
Mat queryDescriptors;
extractor->compute( queryImg, queryPoints, queryDescriptors );
matcher->knnMatch( queryDescriptors, matches, knn, masks, compactResult );
}
void VectorDescriptorMatcher::radiusMatchImpl( const Mat& queryImg, vector<KeyPoint>& queryPoints,
vector<vector<DMatch> >& matches, float maxDistance,
const vector<Mat>& masks, bool compactResult )
{
Mat queryDescs;
extractor->compute( queryImg, queryPoints, queryDescs );
matcher->radiusMatch( queryDescs, matches, maxDistance, masks, compactResult );
Mat queryDescriptors;
extractor->compute( queryImg, queryPoints, queryDescriptors );
matcher->radiusMatch( queryDescriptors, matches, maxDistance, masks, compactResult );
}
void VectorDescriptorMatcher::read( const FileNode& fn )
{
GenericDescriptorMatcher::read(fn);
extractor->read (fn);
extractor->read(fn);
}
void VectorDescriptorMatcher::write (FileStorage& fs) const
......@@ -977,6 +1236,12 @@ void VectorDescriptorMatcher::write (FileStorage& fs) const
extractor->write (fs);
}
Ptr<GenericDescriptorMatcher> VectorDescriptorMatcher::clone( bool emptyTrainData ) const
{
// TODO clone extractor
return new VectorDescriptorMatcher( extractor, matcher->clone(emptyTrainData) );
}
/*
* Factory function for GenericDescriptorMatch creating
*/
......
......@@ -7,132 +7,228 @@
using namespace cv;
using namespace std;
void maskMatchesByTrainImgIdx( const vector<DMatch>& matches, int trainImgIdx, vector<char>& mask );
void readTrainFilenames( const string& filename, string& dirName, vector<string>& trainFilenames );
/*
* This is a sample on matching descriptors detected on one image to descriptors detected in image set.
* So we have one query image and several train images. For each keypoint descriptor of query image
* the one nearest train descriptor is found the entire collection of train images. To visualize the result
* of matching we save images, each of which combines query and train image with matches between them (if they exist).
* Match is drawn as line between corresponding points. Count of all matches is equel to count of
* query keypoints, so we have the same count of lines in all set of result images (but not for each result
* (train) image).
*/
const string defaultDetectorType = "SURF";
const string defaultDescriptorType = "SURF";
const string defaultMatcherType = "FlannBased";
const string defaultQueryImageName = "../../opencv/samples/cpp/matching_to_many_images/query.png";
const string defaultFileWithTrainImages = "../../opencv/samples/cpp/matching_to_many_images/train/trainImages.txt";
const string defaultDirToSaveResImages = "../../opencv/samples/cpp/matching_to_many_images/results";
int main(int argc, char** argv)
void maskMatchesByTrainImgIdx( const vector<DMatch>& matches, int trainImgIdx, vector<char>& mask )
{
mask.resize( matches.size() );
fill( mask.begin(), mask.end(), 0 );
for( size_t i = 0; i < matches.size(); i++ )
{
if( matches[i].imgIdx == trainImgIdx )
mask[i] = 1;
}
}
void readTrainFilenames( const string& filename, string& dirName, vector<string>& trainFilenames )
{
Mat queryImg;
vector<KeyPoint> queryPoints;
Mat queryDescs;
const char dlmtr = '/';
vector<Mat> trainImgCollection;
vector<vector<KeyPoint> > trainPointCollection;
vector<Mat> trainDescCollection;
trainFilenames.clear();
vector<DMatch> matches;
ifstream file( filename.c_str() );
if ( !file.is_open() )
return;
if( argc != 7 )
size_t pos = filename.rfind(dlmtr);
dirName = pos == string::npos ? "" : filename.substr(0, pos) + dlmtr;
while( !file.eof() )
{
cout << "Format:" << endl;
cout << argv[0] << "[detectorType] [descriptorType] [matcherType] [queryImage] [fileWithTrainImages] [dirToSaveResImages]" << endl;
return -1;
string str; getline( file, str );
if( str.empty() ) break;
trainFilenames.push_back(str);
}
file.close();
}
cout << "< 1.) Creating feature detector, descriptor extractor and descriptor matcher ..." << endl;
Ptr<FeatureDetector> detector = createFeatureDetector( argv[1] );
Ptr<DescriptorExtractor> descriptorExtractor = createDescriptorExtractor( argv[2] );
Ptr<DescriptorMatcher> descriptorMatcher = createDescriptorMatcher( argv[3] );
bool createDetectorDescriptorMatcher( const string& detectorType, const string& descriptorType, const string& matcherType,
Ptr<FeatureDetector>& featureDetector,
Ptr<DescriptorExtractor>& descriptorExtractor,
Ptr<DescriptorMatcher>& descriptorMatcher )
{
cout << "< Creating feature detector, descriptor extractor and descriptor matcher ..." << endl;
featureDetector = createFeatureDetector( detectorType );
descriptorExtractor = createDescriptorExtractor( descriptorType );
descriptorMatcher = createDescriptorMatcher( matcherType );
cout << ">" << endl;
if( detector.empty() || descriptorExtractor.empty() || descriptorMatcher.empty() )
{
bool isCreated = !( featureDetector.empty() || descriptorExtractor.empty() || descriptorMatcher.empty() );
if( !isCreated )
cout << "Can not create feature detector or descriptor exstractor or descriptor matcher of given types." << endl << ">" << endl;
return -1;
}
cout << "< 2.) Reading the images..." << endl;
queryImg = imread( argv[4], CV_LOAD_IMAGE_GRAYSCALE);
if( queryImg.empty() )
return isCreated;
}
bool readImages( const string& queryImageName, const string& trainFilename,
Mat& queryImage, vector <Mat>& trainImages, vector<string>& trainImageNames )
{
cout << "< Reading the images..." << endl;
queryImage = imread( queryImageName, CV_LOAD_IMAGE_GRAYSCALE);
if( queryImage.empty() )
{
cout << "Query image can not be read." << endl << ">" << endl;
return -1;
return false;
}
string trainDirName;
vector<string> trainFilenames;
vector<int> usedTrainImgIdxs;
readTrainFilenames( argv[5], trainDirName, trainFilenames );
if( trainFilenames.empty() )
readTrainFilenames( trainFilename, trainDirName, trainImageNames );
if( trainImageNames.empty() )
{
cout << "Train image filenames can not be read." << endl << ">" << endl;
return -1;
return false;
}
for( size_t i = 0; i < trainFilenames.size(); i++ )
int readImageCount = 0;
for( size_t i = 0; i < trainImageNames.size(); i++ )
{
Mat img = imread( trainDirName + trainFilenames[i], CV_LOAD_IMAGE_GRAYSCALE );
if( img.empty() ) cout << "Train image " << trainDirName + trainFilenames[i] << " can not be read." << endl;
trainImgCollection.push_back( img );
usedTrainImgIdxs.push_back( i );
string filename = trainDirName + trainImageNames[i];
Mat img = imread( filename, CV_LOAD_IMAGE_GRAYSCALE );
if( img.empty() )
cout << "Train image " << filename << " can not be read." << endl;
else
readImageCount++;
trainImages.push_back( img );
}
if( trainImgCollection.empty() )
if( !readImageCount )
{
cout << "All train images can not be read." << endl << ">" << endl;
return -1;
return false;
}
else
cout << trainImgCollection.size() << " train images were read." << endl;
cout << readImageCount << " train images were read." << endl;
cout << ">" << endl;
cout << endl << "< 3.) Extracting keypoints from images..." << endl;
detector->detect( queryImg, queryPoints );
detector->detect( trainImgCollection, trainPointCollection );
return true;
}
void detectKeypoints( const Mat& queryImage, vector<KeyPoint>& queryKeypoints,
const vector<Mat>& trainImages, vector<vector<KeyPoint> >& trainKeypoints,
Ptr<FeatureDetector>& featureDetector )
{
cout << endl << "< Extracting keypoints from images..." << endl;
featureDetector->detect( queryImage, queryKeypoints );
featureDetector->detect( trainImages, trainKeypoints );
cout << ">" << endl;
}
cout << "< 4.) Computing descriptors for keypoints..." << endl;
descriptorExtractor->compute( queryImg, queryPoints, queryDescs );
descriptorExtractor->compute( trainImgCollection, trainPointCollection, trainDescCollection );
void computeDescriptors( const Mat& queryImage, vector<KeyPoint>& queryKeypoints, Mat& queryDescriptors,
const vector<Mat>& trainImages, vector<vector<KeyPoint> >& trainKeypoints, vector<Mat>& trainDescriptors,
Ptr<DescriptorExtractor>& descriptorExtractor )
{
cout << "< Computing descriptors for keypoints..." << endl;
descriptorExtractor->compute( queryImage, queryKeypoints, queryDescriptors );
descriptorExtractor->compute( trainImages, trainKeypoints, trainDescriptors );
cout << ">" << endl;
}
cout << "< 5.) Set train descriptors collection in the matcher and match query descriptors to them..." << endl;
descriptorMatcher->add( trainDescCollection );
descriptorMatcher->match( queryDescs, matches );
CV_Assert( queryPoints.size() == matches.size() );
void matchDescriptors( const Mat& queryDescriptors, const vector<Mat>& trainDescriptors,
vector<DMatch>& matches, Ptr<DescriptorMatcher>& descriptorMatcher )
{
cout << "< Set train descriptors collection in the matcher and match query descriptors to them..." << endl;
descriptorMatcher->add( trainDescriptors );
descriptorMatcher->match( queryDescriptors, matches );
CV_Assert( queryDescriptors.rows == (int)matches.size() || matches.empty() );
cout << ">" << endl;
}
cout << "< 6.) Save results..." << endl;
void saveResultImages( const Mat& queryImage, const vector<KeyPoint>& queryKeypoints,
const vector<Mat>& trainImages, const vector<vector<KeyPoint> >& trainKeypoints,
const vector<DMatch>& matches, const vector<string>& trainImagesNames, const string& resultDir )
{
cout << "< Save results..." << endl;
Mat drawImg;
vector<char> mask;
for( size_t i = 0; i < trainImgCollection.size(); i++ )
for( size_t i = 0; i < trainImages.size(); i++ )
{
maskMatchesByTrainImgIdx( matches, i, mask );
drawMatches( queryImg, queryPoints, trainImgCollection[i], trainPointCollection[i],
matches, drawImg, Scalar::all(-1), Scalar::all(-1), mask );
imwrite( string(argv[6]) + "/res_" + trainFilenames[usedTrainImgIdxs[i]] + ".png", drawImg );
if( !trainImages[i].empty() )
{
maskMatchesByTrainImgIdx( matches, i, mask );
drawMatches( queryImage, queryKeypoints, trainImages[i], trainKeypoints[i],
matches, drawImg, Scalar::all(-1), Scalar::all(-1), mask );
string filename = resultDir + "/res_" + trainImagesNames[i];
if( !imwrite( filename, drawImg ) )
cout << "Image " << filename << " can not be saved (may be because directory " << resultDir << " does not exist)." << endl;
}
}
cout << ">" << endl;
return 0;
}
void maskMatchesByTrainImgIdx( const vector<DMatch>& matches, int trainImgIdx, vector<char>& mask )
void printPrompt( const string& applName )
{
mask.resize( matches.size() );
fill( mask.begin(), mask.end(), 0 );
for( size_t i = 0; i < matches.size(); i++ )
{
if( matches[i].imgIdx == trainImgIdx )
mask[i] = 1;
}
cout << endl << "Format:" << endl;
cout << applName << " [detectorType] [descriptorType] [matcherType] [queryImage] [fileWithTrainImages] [dirToSaveResImages]" << endl;
cout << endl << "Example:" << endl
<< defaultDetectorType << " " << defaultDescriptorType << " " << defaultMatcherType << " "
<< defaultQueryImageName << " " << defaultFileWithTrainImages << " " << defaultDirToSaveResImages << endl;
}
void readTrainFilenames( const string& filename, string& dirName, vector<string>& trainFilenames )
int main(int argc, char** argv)
{
const char dlmtr = '/';
string detectorType = defaultDetectorType;
string descriptorType = defaultDescriptorType;
string matcherType = defaultMatcherType;
string queryImageName = defaultQueryImageName;
string fileWithTrainImages = defaultFileWithTrainImages;
string dirToSaveResImages = defaultDirToSaveResImages;
if( argc != 7 && argc != 1 )
{
printPrompt( argv[0] );
return -1;
}
trainFilenames.clear();
if( argc != 1 )
{
detectorType = argv[1]; descriptorType = argv[2]; matcherType = argv[3];
queryImageName = argv[4]; fileWithTrainImages = argv[5];
dirToSaveResImages = argv[6];
}
ifstream file( filename.c_str() );
if ( !file.is_open() )
return;
Ptr<FeatureDetector> featureDetector;
Ptr<DescriptorExtractor> descriptorExtractor;
Ptr<DescriptorMatcher> descriptorMatcher;
if( !createDetectorDescriptorMatcher( detectorType, descriptorType, matcherType, featureDetector, descriptorExtractor, descriptorMatcher ) )
{
printPrompt( argv[0] );
return -1;
}
size_t pos = filename.rfind(dlmtr);
dirName = pos == string::npos ? "" : filename.substr(0, pos) + dlmtr;
while( !file.eof() )
Mat queryImage;
vector<Mat> trainImages;
vector<string> trainImagesNames;
if( !readImages( queryImageName, fileWithTrainImages, queryImage, trainImages, trainImagesNames ) )
{
string str; getline( file, str );
if( str.empty() ) break;
trainFilenames.push_back(str);
printPrompt( argv[0] );
return -1;
}
file.close();
vector<KeyPoint> queryKeypoints;
vector<vector<KeyPoint> > trainKeypoints;
detectKeypoints( queryImage, queryKeypoints, trainImages, trainKeypoints, featureDetector );
Mat queryDescriptors;
vector<Mat> trainDescriptors;
computeDescriptors( queryImage, queryKeypoints, queryDescriptors,
trainImages, trainKeypoints, trainDescriptors,
descriptorExtractor );
vector<DMatch> matches;
matchDescriptors( queryDescriptors, trainDescriptors, matches, descriptorMatcher );
saveResultImages( queryImage, queryKeypoints, trainImages, trainKeypoints,
matches, trainImagesNames, dirToSaveResImages );
return 0;
}
......@@ -60,7 +60,7 @@ public:
CvTest( testName, "cv::FeatureDetector::detect"), fdetector(_fdetector) {}
protected:
virtual void run( int start_from )
virtual void run( int /*start_from*/ )
{
const float maxPtDif = 1.f;
const float maxSizeDif = 1.f;
......@@ -112,7 +112,7 @@ protected:
for( size_t c = 0; c < calcKeypoints.size(); c++ )
{
progress = update_progress( progress, v*calcKeypoints.size() + c, progressCount, 0 );
float curDist = norm( calcKeypoints[c].pt - validKeypoints[v].pt );
float curDist = (float)norm( calcKeypoints[c].pt - validKeypoints[v].pt );
if( curDist < minDist )
{
minDist = curDist;
......@@ -434,7 +434,7 @@ int CV_DescriptorMatcherTest::testMatch( const Mat& query, const Mat& train )
for( size_t i = 0; i < matches.size(); i++ )
{
DMatch match = matches[i];
int shift = dmatcher->supportMask() ? 1 : 0;
int shift = dmatcher->isMaskSupported() ? 1 : 0;
{
if( i < queryDescCount/2 )
{
......@@ -533,7 +533,7 @@ int CV_DescriptorMatcherTest::testKnnMatch( const Mat& query, const Mat& train )
else
{
int badCount = 0;
int shift = dmatcher->supportMask() ? 1 : 0;
int shift = dmatcher->isMaskSupported() ? 1 : 0;
for( size_t i = 0; i < matches.size(); i++ )
{
if( (int)matches[i].size() != knn )
......@@ -641,8 +641,8 @@ int CV_DescriptorMatcherTest::testRadiusMatch( const Mat& query, const Mat& trai
res = curRes != CvTS::OK ? curRes : res;
int badCount = 0;
int shift = dmatcher->supportMask() ? 1 : 0;
int needMatchCount = dmatcher->supportMask() ? n-1 : n;
int shift = dmatcher->isMaskSupported() ? 1 : 0;
int needMatchCount = dmatcher->isMaskSupported() ? n-1 : n;
for( size_t i = 0; i < matches.size(); i++ )
{
if( (int)matches[i].size() != needMatchCount )
......@@ -741,6 +741,6 @@ CV_CalonderDescriptorExtractorTest<float> floatCalonderTest( "descriptor-calonde
* Matchers
*/
CV_DescriptorMatcherTest bruteForceMatcherTest( "descriptor-matcher-brute-force",
new BruteForceMatcher<L2<float> >, 0.01 );
new BruteForceMatcher<L2<float> >, 0.01f );
CV_DescriptorMatcherTest flannBasedMatcherTest( "descriptor-matcher-flann-based",
new FlannBasedMatcher, 0.04 );
new FlannBasedMatcher, 0.04f );
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment