diff --git a/modules/features2d/include/opencv2/features2d/features2d.hpp b/modules/features2d/include/opencv2/features2d/features2d.hpp index d5243e0f88..0c686bfd1c 100644 --- a/modules/features2d/include/opencv2/features2d/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d/features2d.hpp @@ -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& keypoints, const Mat& mask=Mat() ) const = 0; + void detect( const Mat& image, vector& 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& imageCollection, vector >& pointCollection, const vector& masks=vector() ) const; + void detect( const vector& images, vector >& keypoints, const vector& masks=vector() ) 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& 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& keypoints, const Mat& mask=Mat() ) const; - // todo read/write + 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 + protected: - float initFeatureScale; - int featureScaleLevels; - float featureScaleMul; + virtual void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const; - int initXyStep; - int initImgBound; - - bool varyXyStepWithScale; - bool varyImgBoundWithScale; + Params params; }; /* @@ -1397,13 +1418,12 @@ public: */ GridAdaptedFeatureDetector( const Ptr& detector, int maxTotalKeypoints, int gridRows=4, int gridCols=4 ); - virtual void detect( const Mat& image, vector& 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& keypoints, const Mat& mask=Mat() ) const; + Ptr detector; int maxTotalKeypoints; int gridRows; @@ -1418,13 +1438,12 @@ class CV_EXPORTS PyramidAdaptedFeatureDetector : public FeatureDetector { public: PyramidAdaptedFeatureDetector( const Ptr& detector, int levels=2 ); - virtual void detect( const Mat& image, vector& 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& keypoints, const Mat& mask=Mat() ) const; + Ptr detector; int levels; }; @@ -1450,38 +1469,39 @@ CV_EXPORTS Ptr 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& keypoints, Mat& descriptors ) const = 0; + void compute( const Mat& image, vector& 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& imageCollection, vector >& pointCollection, vector& descCollection ) const; + void compute( const vector& images, vector >& keypoints, vector& 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& 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& 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& 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& 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& 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& 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& 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::type; } protected: + virtual void computeImpl( const Mat& image, vector& keypoints, Mat& descriptors ) const; + RTreeClassifier classifier_; static const int BORDER_SIZE = 16; }; @@ -1556,23 +1579,24 @@ CalonderDescriptorExtractor::CalonderDescriptorExtractor(const std::string& c } template -void CalonderDescriptorExtractor::compute( const cv::Mat& image, +void CalonderDescriptorExtractor::computeImpl( const cv::Mat& image, std::vector& 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::type); + /// @todo Check 16-byte aligned + descriptors.create(keypoints.size(), classifier_.classes(), cv::DataType::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(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(i)); + } } template @@ -1595,18 +1619,18 @@ void CalonderDescriptorExtractor::write( FileStorage& ) const class CV_EXPORTS OpponentColorDescriptorExtractor : public DescriptorExtractor { public: - OpponentColorDescriptorExtractor( const Ptr& dextractor ); - - virtual void compute( const Mat& image, vector& keypoints, Mat& descriptors ) const; + OpponentColorDescriptorExtractor( const Ptr& 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 dextractor; + virtual void computeImpl( const Mat& image, vector& keypoints, Mat& descriptors ) const; + + Ptr descriptorExtractor; }; /* @@ -1615,99 +1639,25 @@ 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& 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; + virtual void computeImpl(const Mat& image, std::vector& keypoints, Mat& descriptors) const; - int bytes_; - typedef void(*PixelTestFn)(const Mat&, const std::vector&, 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& keypoints, Mat& descriptors); - static void pixelTests32(const Mat& sum, const std::vector& keypoints, Mat& descriptors); - static void pixelTests64(const Mat& sum, const std::vector& keypoints, Mat& descriptors); + typedef void(*PixelTestFn)(const Mat&, const std::vector&, Mat&); + int bytes_; + PixelTestFn test_fn_; }; -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 (img_y + HALF_KERNEL + 1, img_x + HALF_KERNEL + 1) - sum.at (img_y + HALF_KERNEL + 1, - img_x - HALF_KERNEL) - - sum.at (img_y - HALF_KERNEL, img_x + HALF_KERNEL + 1) + sum.at (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); -}; - -#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 -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 (a + i); - unsigned long b2 = *reinterpret_cast (b + i); - result += __builtin_popcountl(a2 ^ b2); - } - return result; - } -}; -#else -typedef HammingLUT Hamming; -#endif - CV_EXPORTS Ptr 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 (a + i); + unsigned long b2 = *reinterpret_cast (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& descCollection ); + virtual void add( const vector& descriptors ); /* - * Get descriptor collection. + * Get train descriptors collection. */ - const vector& getTrainDescCollection() const { return trainDescCollection; } + const vector& 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& 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 >& matches, int knn, + void match( const Mat& queryDescriptors, const Mat& trainDescriptors, + vector& 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 >& 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 >& 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 >& 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& matches, + void match( const Mat& queryDescriptors, vector& matches, const vector& masks=vector() ); - void knnMatch( const Mat& queryDescs, vector >& matches, int knn, + void knnMatch( const Mat& queryDescriptors, vector >& matches, int k, + const vector& masks=vector(), bool compactResult=false ); + void radiusMatch( const Mat& queryDescriptors, vector >& matches, float maxDistance, const vector& masks=vector(), bool compactResult=false ); - void radiusMatch( const Mat& queryDescs, vector >& matches, float maxDistance, - const vector& masks=vector(), 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 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& descCollection ); + // Vector of matrices "descriptors" will be merged to one matrix "mergedDescriptors" here. + void set( const vector& 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 startIdxs; }; - // create matcher clone with current parameters but with empty data - virtual Ptr cloneWithoutData() const = 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 >& matches, int k, + const vector& masks=vector(), bool compactResult=false ) = 0; + virtual void radiusMatchImpl( const Mat& queryDescriptors, vector >& matches, float maxDistance, + const vector& masks=vector(), bool compactResult=false ) = 0; - virtual void knnMatchImpl( const Mat& queryDescs, vector >& matches, int knn, - const vector& masks, bool compactResult ) = 0; - virtual void radiusMatchImpl( const Mat& queryDescs, vector >& matches, float maxDistance, - const vector& masks, bool compactResult ) = 0; + static bool isPossibleMatch( const Mat& mask, int queryIdx, int trainIdx ); + static bool isMaskedOut( const vector& masks, int queryIdx ); + static Mat clone_op( Mat m ) { return m.clone(); } + void checkMasks( const vector& masks, int queryDescriptorsCount ) const; - static bool possibleMatch( const Mat& mask, int index_1, int index_2 ) - { - return mask.empty() || mask.at(index_1, index_2); - } - - static bool maskedOut( const vector& 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++; - } - - return !masks.empty() && outCount == masks.size() ; - } - + // Collection of descriptors from train images. vector 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 clone( bool emptyTrainData=false ) const; protected: - virtual Ptr cloneWithoutData() const { return new BruteForceMatcher(distance); } + virtual void knnMatchImpl( const Mat& queryDescriptors, vector >& matches, int k, + const vector& masks=vector(), bool compactResult=false ); + virtual void radiusMatchImpl( const Mat& queryDescriptors, vector >& matches, float maxDistance, + const vector& masks=vector(), bool compactResult=false ); - virtual void knnMatchImpl( const Mat& queryDescs, vector >& matches, int knn, - const vector& masks, bool compactResult ); - virtual void radiusMatchImpl( const Mat& queryDescs, vector >& matches, float maxDistance, - const vector& 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& matcher, - const Mat& queryDescs, vector >& matches, int knn, - const vector& masks, bool compactResult ); - static void bfRadiusMatchImpl( BruteForceMatcher& matcher, - const Mat& queryDescs, vector >& matches, float maxDistance, - const vector& masks, bool compactResult ); + static void commonKnnMatchImpl( BruteForceMatcher& matcher, + const Mat& queryDescriptors, vector >& matches, int k, + const vector& masks, bool compactResult ); + static void commonRadiusMatchImpl( BruteForceMatcher& matcher, + const Mat& queryDescriptors, vector >& matches, float maxDistance, + const vector& masks, bool compactResult ); }; template -void BruteForceMatcher::knnMatchImpl( const Mat& queryDescs, vector >& matches, int knn, +Ptr BruteForceMatcher::clone( bool emptyTrainData ) const +{ + BruteForceMatcher* matcher = new BruteForceMatcher(distance); + if( !emptyTrainData ) + { + transform( trainDescCollection.begin(), trainDescCollection.end(), + matcher->trainDescCollection.begin(), clone_op ); + } + return matcher; +} + +template +void BruteForceMatcher::knnMatchImpl( const Mat& queryDescriptors, vector >& matches, int k, const vector& masks, bool compactResult ) { - bfKnnMatchImpl( *this, queryDescs, matches, knn, masks, compactResult ); + commonKnnMatchImpl( *this, queryDescriptors, matches, k, masks, compactResult ); } template -void BruteForceMatcher::radiusMatchImpl( const Mat& queryDescs, vector >& matches, float maxDistance, - const vector& masks, bool compactResult ) +void BruteForceMatcher::radiusMatchImpl( const Mat& queryDescriptors, vector >& matches, + float maxDistance, const vector& masks, bool compactResult ) { - bfRadiusMatchImpl( *this, queryDescs, matches, maxDistance, masks, compactResult ); + commonRadiusMatchImpl( *this, queryDescriptors, matches, maxDistance, masks, compactResult ); } template -inline void BruteForceMatcher::bfKnnMatchImpl( BruteForceMatcher& matcher, - const Mat& queryDescs, vector >& matches, int knn, +inline void BruteForceMatcher::commonKnnMatchImpl( BruteForceMatcher& matcher, + const Mat& queryDescriptors, vector >& matches, int knn, const vector& masks, bool compactResult ) { typedef typename Distance::ValueType ValueType; typedef typename Distance::ResultType DistanceType; - CV_Assert( DataType::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::type == queryDescriptors.type() ); + + int dimension = queryDescriptors.cols; + matches.reserve(queryDescriptors.rows); size_t imgCount = matcher.trainDescCollection.size(); vector allDists( imgCount ); // distances between one query descriptor and all train descriptors for( size_t i = 0; i < imgCount; i++ ) - allDists[i] = Mat( 1, matcher.trainDescCollection[i].rows, DataType::type ); + if( matcher.trainDescCollection[i].rows ) + allDists[i] = Mat( 1, matcher.trainDescCollection[i].rows, DataType::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() ); @@ -1994,17 +2024,15 @@ inline void BruteForceMatcher::bfKnnMatchImpl( BruteForceMatcher::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::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::bfKnnMatchImpl( BruteForceMatcher() ); vector >::reverse_iterator curMatches = matches.rbegin(); for( int k = 0; k < knn; k++ ) { DMatch bestMatch; - bestMatch.distance = std::numeric_limits::max(); + bestMatch.distance = std::numeric_limits::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::bfKnnMatchImpl( BruteForceMatcher -inline void BruteForceMatcher::bfRadiusMatchImpl( BruteForceMatcher& matcher, - const Mat& queryDescs, vector >& matches, float maxDistance, +inline void BruteForceMatcher::commonRadiusMatchImpl( BruteForceMatcher& matcher, + const Mat& queryDescriptors, vector >& matches, float maxDistance, const vector& masks, bool compactResult ) { typedef typename Distance::ValueType ValueType; typedef typename Distance::ResultType DistanceType; - CV_Assert( DataType::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::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() ); @@ -2067,23 +2098,21 @@ inline void BruteForceMatcher::bfRadiusMatchImpl( BruteForceMatcher >::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::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::bfRadiusMatchImpl( BruteForceMatcher -void BruteForceMatcher >::knnMatchImpl( const Mat& queryDescs, vector >& matches, int knn, +void BruteForceMatcher >::knnMatchImpl( const Mat& queryDescriptors, vector >& matches, int k, const vector& masks, bool compactResult ); template<> -void BruteForceMatcher >::radiusMatchImpl( const Mat& queryDescs, vector >& matches, float maxDistance, - const vector& masks, bool compactResult ); +void BruteForceMatcher >::radiusMatchImpl( const Mat& queryDescriptors, vector >& matches, + float maxDistance, const vector& masks, bool compactResult ); /* * Flann based matcher @@ -2108,26 +2137,27 @@ void BruteForceMatcher >::radiusMatchImpl( const Mat& queryDescs, vect class CV_EXPORTS FlannBasedMatcher : public DescriptorMatcher { public: - FlannBasedMatcher( const Ptr& _indexParams=new flann::KDTreeIndexParams(), - const Ptr& _searchParams=new flann::SearchParams() ); + FlannBasedMatcher( const Ptr& indexParams=new flann::KDTreeIndexParams(), + const Ptr& searchParams=new flann::SearchParams() ); - virtual void add( const vector& descCollection ); + virtual void add( const vector& descriptors ); virtual void clear(); virtual void train(); - virtual bool supportMask() { return false; } + virtual bool isMaskSupported() const; + + virtual Ptr clone( bool emptyTrainData=false ) const; + protected: - virtual Ptr cloneWithoutData() const { return new FlannBasedMatcher(indexParams, searchParams); } - - // masks is ignored (unsupported) - virtual void knnMatchImpl( const Mat& queryDescs, vector >& matches, int knn, - const vector& masks, bool compactResult ); - virtual void radiusMatchImpl( const Mat& queryDescs, vector >& matches, float maxDistance, - const vector& masks, bool compactResult ); - - static void convertToDMatches( const DescriptorCollection& collection, const Mat& indices, const Mat& dists, + static void convertToDMatches( const DescriptorCollection& descriptors, + const Mat& indices, const Mat& distances, vector >& matches ); + virtual void knnMatchImpl( const Mat& queryDescriptors, vector >& matches, int k, + const vector& masks=vector(), bool compactResult=false ); + virtual void radiusMatchImpl( const Mat& queryDescriptors, vector >& matches, float maxDistance, + const vector& masks=vector(), bool compactResult=false ); + Ptr indexParams; Ptr searchParams; Ptr 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& imgCollection, - vector >& pointCollection ); + virtual void add( const vector& images, + vector >& keypoints ); - const vector& getTrainImgCollection() const { return trainPointCollection.getImages(); } - const vector >& getTrainPointCollection() const { return trainPointCollection.getKeypoints(); } + const vector& getTrainImages() const; + const vector >& 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& queryPoints, - const Mat& trainImage, vector& trainPoints ) const; + virtual void classify( const Mat& queryImage, vector& queryKeypoints, + const Mat& trainImage, vector& trainKeypoints ) const; // Classify keypoints from query image under train image collection. - virtual void classify( const Mat& queryImage, vector& queryPoints ); + virtual void classify( const Mat& queryImage, vector& 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& queryPoints, - const Mat& trainImg, vector& trainPoints, + void match( const Mat& queryImage, vector& queryKeypoints, + const Mat& trainImage, vector& trainKeypoints, vector& 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& queryPoints, - const Mat& trainImg, vector& trainPoints, - vector >& matches, int knn, const Mat& mask=Mat(), bool compactResult=false ) const; + void knnMatch( const Mat& queryImage, vector& queryKeypoints, + const Mat& trainImage, vector& trainKeypoints, + vector >& 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& queryPoints, - const Mat& trainImg, vector& trainPoints, - vector >& matches, float maxDistance, const Mat& mask=Mat(), bool compactResult=false ) const; + void radiusMatch( const Mat& queryImage, vector& queryKeypoints, + const Mat& trainImage, vector& trainKeypoints, + vector >& 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& queryPoints, + void match( const Mat& queryImage, vector& queryKeypoints, vector& matches, const vector& masks=vector() ); - void knnMatch( const Mat& queryImg, vector& queryPoints, - vector >& matches, int knn, const vector& masks=vector(), bool compactResult=false ); - void radiusMatch( const Mat& queryImg, vector& queryPoints, - vector >& matches, float maxDistance, const vector& masks=vector(), bool compactResult=false ); + void knnMatch( const Mat& queryImage, vector& queryKeypoints, + vector >& matches, int k, + const vector& masks=vector(), bool compactResult=false ); + void radiusMatch( const Mat& queryImage, vector& queryKeypoints, + vector >& matches, float maxDistance, + const vector& masks=vector(), 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 clone( bool emptyTrainData=false ) const = 0; protected: - virtual Ptr createEmptyMatcherCopy() const = 0; - - virtual void knnMatchImpl( const Mat& queryImg, vector& queryPoints, - vector >& matches, int knn, + // 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& queryKeypoints, + vector >& matches, int k, const vector& masks, bool compactResult ) = 0; - virtual void radiusMatchImpl( const Mat& queryImg, vector& queryPoints, + virtual void radiusMatchImpl( const Mat& queryImage, vector& queryKeypoints, vector >& matches, float maxDistance, const vector& masks, bool compactResult ) = 0; /* @@ -2234,32 +2289,34 @@ protected: class CV_EXPORTS KeyPointCollection { public: - KeyPointCollection() : size(0) {} - void add( const vector& _images, const vector >& _points ); + KeyPointCollection(); + KeyPointCollection( const KeyPointCollection& collection ); + void add( const vector& images, const vector >& 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 >& getKeypoints() const { return points; } - const vector& getKeypoints( int imgIdx ) const { CV_Assert( imgIdx < (int)imageCount() ); return points[imgIdx]; } + const vector >& getKeypoints() const; + const vector& 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& getImages() const { return images; } - const Mat& getImage( int imgIdx ) const { CV_Assert( imgIdx < (int)imageCount() ); return images[imgIdx]; } + const vector& getImages() const; + const Mat& getImage( int imgIdx ) const; protected: - int size; + int pointCount; vector images; - vector > points; - - // global indices of the first points in each image, - // startIndices.size() = points.size() + vector > keypoints; + // global indices of the first points in each image, startIndices.size() = keypoints.size() vector 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& _base=Ptr() ); + void initialize( const Params& params, const Ptr& base=Ptr() ); // 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 createEmptyMatcherCopy() const { return new OneWayDescriptorMatcher( params ); } + virtual Ptr 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& queryPoints, - vector >& matches, int knn, + virtual void knnMatchImpl( const Mat& queryImage, vector& queryKeypoints, + vector >& matches, int k, const vector& masks, bool compactResult ); - virtual void radiusMatchImpl( const Mat& queryImg, vector& queryPoints, + virtual void radiusMatchImpl( const Mat& queryImage, vector& queryKeypoints, vector >& matches, float maxDistance, const vector& 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 createEmptyMatcherCopy() const { return new FernDescriptorMatcher( params ); } + virtual Ptr clone( bool emptyTrainData=false ) const; - virtual void knnMatchImpl( const Mat& queryImg, vector& queryPoints, - vector >& matches, int knn, +protected: + virtual void knnMatchImpl( const Mat& queryImage, vector& queryKeypoints, + vector >& matches, int k, const vector& masks, bool compactResult ); - virtual void radiusMatchImpl( const Mat& queryImg, vector& queryPoints, + virtual void radiusMatchImpl( const Mat& queryImage, vector& queryKeypoints, vector >& matches, float maxDistance, const vector& masks, bool compactResult ); @@ -2425,9 +2474,8 @@ typedef VectorDescriptorMatcher VectorDescriptorMatch; class CV_EXPORTS VectorDescriptorMatcher : public GenericDescriptorMatcher { public: - VectorDescriptorMatcher( const Ptr& _extractor, const Ptr& _matcher ) - : extractor( _extractor ), matcher( _matcher ) { CV_Assert( !extractor.empty() && !matcher.empty() ); } - virtual ~VectorDescriptorMatcher() {} + VectorDescriptorMatcher( const Ptr& extractor, const Ptr& matcher ); + virtual ~VectorDescriptorMatcher(); virtual void add( const vector& imgCollection, vector >& 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 createEmptyMatcherCopy() const { return new VectorDescriptorMatcher(extractor, matcher); } + virtual Ptr clone( bool emptyTrainData=false ) const; - virtual void knnMatchImpl( const Mat& queryImg, vector& queryPoints, - vector >& matches, int knn, +protected: + virtual void knnMatchImpl( const Mat& queryImage, vector& queryKeypoints, + vector >& matches, int k, const vector& masks, bool compactResult ); - virtual void radiusMatchImpl( const Mat& queryImg, vector& queryPoints, + virtual void radiusMatchImpl( const Mat& queryImage, vector& queryKeypoints, vector >& matches, float maxDistance, const vector& 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& getDescriptors() const { return descriptors; } - int descripotorsCount() const { return descriptors.empty() ? 0 : size; } + const vector& 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& dextractor, const Ptr& 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& keypoints, Mat& imgDescriptor, - vector >* 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 >* pointIdxsOfClusters=0, Mat* descriptors=0 ); + // compute() is not constant because DescriptorMatcher::match is not constant + + int descriptorSize() const; + int descriptorType() const; protected: Mat vocabulary; diff --git a/modules/features2d/src/bagofwords.cpp b/modules/features2d/src/bagofwords.cpp index dc056cc92a..51daa892c6 100755 --- a/modules/features2d/src/bagofwords.cpp +++ b/modules/features2d/src/bagofwords.cpp @@ -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& 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 Ptrclear(); @@ -111,6 +133,11 @@ void BOWImgDescriptorExtractor::setVocabulary( const Mat& _vocabulary ) dmatcher->add( vector(1, vocabulary) ); } +const Mat& BOWImgDescriptorExtractor::getVocabulary() const +{ + return vocabulary; +} + void BOWImgDescriptorExtractor::compute( const Mat& image, vector& keypoints, Mat& imgDescriptor, vector >* pointIdxsOfClusters, Mat* _descriptors ) { @@ -153,4 +180,14 @@ void BOWImgDescriptorExtractor::compute( const Mat& image, vector& key imgDescriptor /= descriptors.rows; } +int BOWImgDescriptorExtractor::descriptorSize() const +{ + return vocabulary.empty() ? 0 : vocabulary.rows; +} + +int BOWImgDescriptorExtractor::descriptorType() const +{ + return CV_32FC1; +} + } diff --git a/modules/features2d/src/brief.cpp b/modules/features2d/src/brief.cpp index e43efb3eaa..bc2978588c 100644 --- a/modules/features2d/src/brief.cpp +++ b/modules/features2d/src/brief.cpp @@ -44,78 +44,101 @@ #include #include +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; + + int img_y = (int)(pt.pt.y + 0.5) + y; + int img_x = (int)(pt.pt.x + 0.5) + x; + return sum.at(img_y + HALF_KERNEL + 1, img_x + HALF_KERNEL + 1) + - sum.at(img_y + HALF_KERNEL + 1, img_x - HALF_KERNEL) + - sum.at(img_y - HALF_KERNEL, img_x + HALF_KERNEL + 1) + + sum.at(img_y - HALF_KERNEL, img_x - HALF_KERNEL); +} + +void pixelTests16(const Mat& sum, const std::vector& keypoints, Mat& descriptors) +{ + for (int i = 0; i < (int)keypoints.size(); ++i) + { + uchar* desc = descriptors.ptr(i); + const KeyPoint& pt = keypoints[i]; +#include "generated_16.i" + } +} + +void pixelTests32(const Mat& sum, const std::vector& keypoints, Mat& descriptors) +{ + for (int i = 0; i < (int)keypoints.size(); ++i) + { + uchar* desc = descriptors.ptr(i); + const KeyPoint& pt = keypoints[i]; + +#include "generated_32.i" + } +} + +void pixelTests64(const Mat& sum, const std::vector& keypoints, Mat& descriptors) +{ + for (int i = 0; i < (int)keypoints.size(); ++i) + { + uchar* desc = descriptors.ptr(i); + const KeyPoint& pt = keypoints[i]; + +#include "generated_64.i" + } +} + namespace cv { BriefDescriptorExtractor::BriefDescriptorExtractor(int bytes) : - bytes_(bytes), test_fn_(NULL) + 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"); - } + 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::compute(const Mat& image, std::vector& keypoints, Mat& descriptors) const +int BriefDescriptorExtractor::descriptorSize() const { - // 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); - - //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); + return bytes_; } -void BriefDescriptorExtractor::pixelTests16(const Mat& sum, const std::vector& keypoints, Mat& descriptors) +int BriefDescriptorExtractor::descriptorType() const { - for (int i = 0; i < (int)keypoints.size(); ++i) - { - uchar* desc = descriptors.ptr(i); - const KeyPoint& pt = keypoints[i]; - -#include "generated_16.i" - } + return CV_8UC1; } -void BriefDescriptorExtractor::pixelTests32(const Mat& sum, const std::vector& keypoints, Mat& descriptors) +void BriefDescriptorExtractor::computeImpl(const Mat& image, std::vector& 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_32.i" - } -} + ///TODO allow the user to pass in a precomputed integral image + //if(image.type() == CV_32S) + // sum = image; + //else -void BriefDescriptorExtractor::pixelTests64(const Mat& sum, const std::vector& keypoints, Mat& descriptors) -{ - for (int i = 0; i < (int)keypoints.size(); ++i) - { - uchar* desc = descriptors.ptr(i); - const KeyPoint& pt = keypoints[i]; + integral(image, sum, CV_32S); -#include "generated_64.i" - } + //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 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 diff --git a/modules/features2d/src/descriptors.cpp b/modules/features2d/src/descriptors.cpp index 934d7b6e66..be27de48eb 100644 --- a/modules/features2d/src/descriptors.cpp +++ b/modules/features2d/src/descriptors.cpp @@ -67,6 +67,21 @@ struct RoiPredicate float minX, minY, maxX, maxY; }; +DescriptorExtractor::~DescriptorExtractor() +{} + +void DescriptorExtractor::compute( const Mat& image, vector& 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& imageCollection, vector >& pointCollection, vector& descCollection ) const { descCollection.resize( imageCollection.size() ); @@ -74,27 +89,42 @@ void DescriptorExtractor::compute( const vector& imageCollection, vector& 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& keypoints, - Mat& descriptors) const +void SiftDescriptorExtractor::computeImpl( const Mat& image, + vector& 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& keypoints, - Mat& descriptors) const +void SurfDescriptorExtractor::computeImpl( const Mat& image, + vector& keypoints, + Mat& descriptors) const { // Compute descriptors for given keypoints vector _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& _dextractor ) : - dextractor(_dextractor) +OpponentColorDescriptorExtractor::OpponentColorDescriptorExtractor( const Ptr& _descriptorExtractor ) : + descriptorExtractor(_descriptorExtractor) {} void convertBGRImageToOpponentColorSpace( const Mat& bgrImage, vector& opponentChannels ) @@ -246,33 +296,42 @@ void convertBGRImageToOpponentColorSpace( const Mat& bgrImage, vector& oppo } } -void OpponentColorDescriptorExtractor::compute( const Mat& bgrImage, vector& keypoints, Mat& descriptors ) const +void OpponentColorDescriptorExtractor::computeImpl( const Mat& bgrImage, vector& keypoints, Mat& descriptors ) const { vector 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(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 * \****************************************************************************************/ diff --git a/modules/features2d/src/detectors.cpp b/modules/features2d/src/detectors.cpp index 3dd999eae7..2fb086fc68 100644 --- a/modules/features2d/src/detectors.cpp +++ b/modules/features2d/src/detectors.cpp @@ -61,6 +61,21 @@ struct MaskPredicate const Mat& mask; }; +FeatureDetector::~FeatureDetector() +{} + +void FeatureDetector::detect( const Mat& image, vector& 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& imageCollection, vector >& pointCollection, const vector& masks ) const { pointCollection.resize( imageCollection.size() ); @@ -76,6 +91,12 @@ void FeatureDetector::removeInvalidPoints( const Mat& mask, vector& 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& keypoints, const Mat& mask ) const +void FastFeatureDetector::detectImpl( const Mat& image, vector& 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& 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& 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& keypoints, const Mat& mask) const { Mat grayImage = image; if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); vector 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::const_iterator corner_it = corners.begin(); vector::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& keypoints, const Mat& mask ) const +void MserFeatureDetector::detectImpl( const Mat& image, vector& keypoints, const Mat& mask ) const { vector > msers; mser(image, msers, mask); - keypoints.clear(); vector >::const_iterator contour_it = msers.begin(); for( ; contour_it != msers.end(); ++contour_it ) { @@ -220,6 +259,12 @@ void MserFeatureDetector::detect( const Mat& image, vector& 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& keypoints, const Mat& mask ) const +void StarFeatureDetector::detectImpl( const Mat& image, vector& 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& 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& keypoints, const Mat& mask ) const +void SiftFeatureDetector::detectImpl( const Mat& image, vector& 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& keypoints, const Mat& mask ) const +void SurfFeatureDetector::detectImpl( const Mat& image, vector& 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& keypoints, /* * DenseFeatureDetector */ -void DenseFeatureDetector::detect( const Mat& image, vector& 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) +{} - float curScale = initFeatureScale; - int curStep = initXyStep; - int curBound = initImgBound; - for( int curLevel = 0; curLevel < featureScaleLevels; curLevel++ ) +DenseFeatureDetector::DenseFeatureDetector(const DenseFeatureDetector::Params &_params) : params(_params) +{} + +void DenseFeatureDetector::detectImpl( const Mat& image, vector& 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& keypoints } } - curScale = curScale * featureScaleMul; - if( varyXyStepWithScale ) curStep = static_cast( curStep * featureScaleMul + 0.5f ); - if( varyImgBoundWithScale ) curBound = static_cast( curBound * featureScaleMul + 0.5f ); + curScale = curScale * params.featureScaleMul; + if( params.varyXyStepWithScale ) curStep = static_cast( curStep * params.featureScaleMul + 0.5f ); + if( params.varyImgBoundWithScale ) curBound = static_cast( curBound * params.featureScaleMul + 0.5f ); } removeInvalidPoints( mask, keypoints ); @@ -391,9 +453,8 @@ void keepStrongest( int N, vector& keypoints ) } } -void GridAdaptedFeatureDetector::detect( const Mat& image, vector& keypoints, const Mat& mask ) const +void GridAdaptedFeatureDetector::detectImpl( const Mat& image, vector& keypoints, const Mat& mask ) const { - keypoints.clear(); keypoints.reserve(maxTotalKeypoints); int maxPerCell = maxTotalKeypoints / (gridRows * gridCols); @@ -430,7 +491,7 @@ PyramidAdaptedFeatureDetector::PyramidAdaptedFeatureDetector( const Ptr& keypoints, const Mat& mask ) const +void PyramidAdaptedFeatureDetector::detectImpl( const Mat& image, vector& keypoints, const Mat& mask ) const { Mat src = image; for( int l = 0, multiplier = 1; l <= levels; ++l, multiplier *= 2 ) @@ -463,12 +524,11 @@ Ptr 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 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; } diff --git a/modules/features2d/src/matchers.cpp b/modules/features2d/src/matchers.cpp index 3f374d479f..3be8febd84 100755 --- a/modules/features2d/src/matchers.cpp +++ b/modules/features2d/src/matchers.cpp @@ -71,11 +71,23 @@ Mat windowedMatchingMask( const vector& keypoints1, const vector& 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& 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& 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& 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 >& knnMatches, vector& } } -void DescriptorMatcher::add( const vector& descCollection ) +DescriptorMatcher::~DescriptorMatcher() +{} + +void DescriptorMatcher::add( const vector& descriptors ) { - trainDescCollection.insert( trainDescCollection.end(), descCollection.begin(), descCollection.end() ); + trainDescCollection.insert( trainDescCollection.end(), descriptors.begin(), descriptors.end() ); +} + +const vector& DescriptorMatcher::getTrainDescriptors() const +{ + return trainDescCollection; } void DescriptorMatcher::clear() @@ -182,67 +212,134 @@ void DescriptorMatcher::clear() trainDescCollection.clear(); } -void DescriptorMatcher::match( const Mat& queryDescs, const Mat& trainDescs, vector& matches, const Mat& mask ) const +bool DescriptorMatcher::empty() const { - Ptr tempMatcher = cloneWithoutData(); - tempMatcher->add( vector(1, trainDescs) ); - tempMatcher->match( queryDescs, matches, vector(1, mask) ); + return trainDescCollection.size() == 0; } -void DescriptorMatcher::knnMatch( const Mat& queryDescs, const Mat& trainDescs, vector >& matches, int knn, +void DescriptorMatcher::train() +{} + +void DescriptorMatcher::match( const Mat& queryDescriptors, const Mat& trainDescriptors, vector& matches, const Mat& mask ) const +{ + Ptr tempMatcher = clone(true); + tempMatcher->add( vector(1, trainDescriptors) ); + tempMatcher->match( queryDescriptors, matches, vector(1, mask) ); +} + +void DescriptorMatcher::knnMatch( const Mat& queryDescriptors, const Mat& trainDescriptors, vector >& matches, int knn, const Mat& mask, bool compactResult ) const { - Ptr tempMatcher = cloneWithoutData(); - tempMatcher->add( vector(1, trainDescs) ); - tempMatcher->knnMatch( queryDescs, matches, knn, vector(1, mask), compactResult ); + Ptr tempMatcher = clone(true); + tempMatcher->add( vector(1, trainDescriptors) ); + tempMatcher->knnMatch( queryDescriptors, matches, knn, vector(1, mask), compactResult ); } -void DescriptorMatcher::radiusMatch( const Mat& queryDescs, const Mat& trainDescs, vector >& matches, float maxDistance, +void DescriptorMatcher::radiusMatch( const Mat& queryDescriptors, const Mat& trainDescriptors, vector >& matches, float maxDistance, const Mat& mask, bool compactResult ) const { - Ptr tempMatcher = cloneWithoutData(); - tempMatcher->add( vector(1, trainDescs) ); - tempMatcher->radiusMatch( queryDescs, matches, maxDistance, vector(1, mask), compactResult ); + Ptr tempMatcher = clone(true); + tempMatcher->add( vector(1, trainDescriptors) ); + tempMatcher->radiusMatch( queryDescriptors, matches, maxDistance, vector(1, mask), compactResult ); } -void DescriptorMatcher::match( const Mat& queryDescs, vector& matches, const vector& masks ) +void DescriptorMatcher::match( const Mat& queryDescriptors, vector& matches, const vector& masks ) { vector > 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 >& matches, int knn, +void DescriptorMatcher::checkMasks( const vector& 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 >& matches, int knn, const vector& 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 >& matches, float maxDistance, +void DescriptorMatcher::radiusMatch( const Mat& queryDescriptors, vector >& matches, float maxDistance, const vector& masks, bool compactResult ) { + matches.empty(); + if( empty() || queryDescriptors.empty() ) + return; + + CV_Assert( maxDistance > std::numeric_limits::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(queryIdx, trainIdx); +} + +bool DescriptorMatcher::isMaskedOut( const vector& 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 >::knnMatchImpl( const Mat& queryDescs, vector >& matches, int knn, - const vector& masks, bool compactResult ) +void BruteForceMatcher >::knnMatchImpl( const Mat& queryDescriptors, vector >& matches, int knn, + const vector& 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 e_query_t; vector > e_trainCollection(trainDescCollection.size()); vector > 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 >::knnMatchImpl( const Mat& queryDescs, vector< vector > 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 >::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 >::knnMatchImpl( const Mat& queryDescs, vector< } template<> -void BruteForceMatcher >::radiusMatchImpl( const Mat& queryDescs, vector >& matches, float maxDistance, +void BruteForceMatcher >::radiusMatchImpl( const Mat& queryDescriptors, vector >& matches, float maxDistance, const vector& 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 e_query_t; vector > e_trainCollection(trainDescCollection.size()); vector > 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 >::radiusMatchImpl( const Mat& queryDescs, vect vector > 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 >::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& _indexParam CV_Assert( !_searchParams.empty() ); } -void FlannBasedMatcher::add( const vector& descCollection ) +void FlannBasedMatcher::add( const vector& 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 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 >& matches ) { @@ -440,28 +558,28 @@ void FlannBasedMatcher::convertToDMatches( const DescriptorCollection& collectio } } -void FlannBasedMatcher::knnMatchImpl( const Mat& queryDescs, vector >& matches, int knn, +void FlannBasedMatcher::knnMatchImpl( const Mat& queryDescriptors, vector >& matches, int knn, const vector& /*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 >& matches, float maxDistance, +void FlannBasedMatcher::radiusMatchImpl( const Mat& queryDescriptors, vector >& matches, float maxDistance, const vector& /*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 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& _images, const vector >& _points ) { @@ -514,9 +648,9 @@ void GenericDescriptorMatcher::KeyPointCollection::add( const vector& _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& _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 >& GenericDescriptorMatcher::KeyPointCollection::getKeypoints() const +{ + return keypoints; +} + +const vector& 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& 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& imgCollection, vector >& pointCollection ) { trainPointCollection.add( imgCollection, pointCollection ); } +const vector& GenericDescriptorMatcher::getTrainImages() const +{ + return trainPointCollection.getImages(); +} + +const vector >& GenericDescriptorMatcher::getTrainKeypoints() const +{ + return trainPointCollection.getKeypoints(); +} + void GenericDescriptorMatcher::clear() { trainPointCollection.clear(); } +void GenericDescriptorMatcher::train() +{} + void GenericDescriptorMatcher::classify( const Mat& queryImage, vector& queryPoints, const Mat& trainImage, vector& trainPoints ) const { @@ -606,7 +791,7 @@ void GenericDescriptorMatcher::match( const Mat& queryImg, vector& que const Mat& trainImg, vector& trainPoints, vector& matches, const Mat& mask ) const { - Ptr tempMatcher = createEmptyMatcherCopy(); + Ptr tempMatcher = clone( true ); vector > vecTrainPoints(1, trainPoints); tempMatcher->add( vector(1, trainImg), vecTrainPoints ); tempMatcher->match( queryImg, queryPoints, matches, vector(1, mask) ); @@ -617,7 +802,7 @@ void GenericDescriptorMatcher::knnMatch( const Mat& queryImg, vector& const Mat& trainImg, vector& trainPoints, vector >& matches, int knn, const Mat& mask, bool compactResult ) const { - Ptr tempMatcher = createEmptyMatcherCopy(); + Ptr tempMatcher = clone( true ); vector > vecTrainPoints(1, trainPoints); tempMatcher->add( vector(1, trainImg), vecTrainPoints ); tempMatcher->knnMatch( queryImg, queryPoints, matches, knn, vector(1, mask), compactResult ); @@ -629,7 +814,7 @@ void GenericDescriptorMatcher::radiusMatch( const Mat& queryImg, vector >& matches, float maxDistance, const Mat& mask, bool compactResult ) const { - Ptr tempMatcher = createEmptyMatcherCopy(); + Ptr tempMatcher = clone( true ); vector > vecTrainPoints(1, trainPoints); tempMatcher->add( vector(1, trainImg), vecTrainPoints ); tempMatcher->radiusMatch( queryImg, queryPoints, matches, maxDistance, vector(1, mask), compactResult ); @@ -660,9 +845,25 @@ void GenericDescriptorMatcher::radiusMatch( const Mat& queryImg, vectorAllocate( trainPointCollection.pointCount() ); - prevTrainCount = trainPointCollection.pointCount(); + base->Allocate( trainPointCollection.keypointCount() ); + prevTrainCount = trainPointCollection.keypointCount(); const vector >& 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& queryPoints, vector >& matches, int knn, const vector& /*masks*/, bool /*compactResult*/ ) @@ -763,6 +969,23 @@ void OneWayDescriptorMatcher::write( FileStorage& fs ) const base->Write (fs); } +Ptr 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& signature ) { @@ -921,16 +1149,42 @@ void FernDescriptorMatcher::write( FileStorage& fs ) const // classifier->write(fs); } +Ptr 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& _extractor, + const Ptr& _matcher ) + : extractor( _extractor ), matcher( _matcher ) +{ + CV_Assert( !extractor.empty() && !matcher.empty() ); +} + +VectorDescriptorMatcher::~VectorDescriptorMatcher() +{} + void VectorDescriptorMatcher::add( const vector& imgCollection, vector >& pointCollection ) { - vector descCollection; - extractor->compute( imgCollection, pointCollection, descCollection ); + vector 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& queryPoints, vector >& matches, int knn, const vector& 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& queryPoints, vector >& matches, float maxDistance, const vector& 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 VectorDescriptorMatcher::clone( bool emptyTrainData ) const +{ + // TODO clone extractor + return new VectorDescriptorMatcher( extractor, matcher->clone(emptyTrainData) ); +} + /* * Factory function for GenericDescriptorMatch creating */ diff --git a/samples/cpp/matching_to_many_images.cpp b/samples/cpp/matching_to_many_images.cpp index f235fdd1dd..69da1e134f 100644 --- a/samples/cpp/matching_to_many_images.cpp +++ b/samples/cpp/matching_to_many_images.cpp @@ -7,103 +7,22 @@ using namespace cv; using namespace std; -void maskMatchesByTrainImgIdx( const vector& matches, int trainImgIdx, vector& mask ); -void readTrainFilenames( const string& filename, string& dirName, vector& trainFilenames ); - -int main(int argc, char** argv) -{ - Mat queryImg; - vector queryPoints; - Mat queryDescs; - - vector trainImgCollection; - vector > trainPointCollection; - vector trainDescCollection; - - vector matches; - - if( argc != 7 ) - { - cout << "Format:" << endl; - cout << argv[0] << "[detectorType] [descriptorType] [matcherType] [queryImage] [fileWithTrainImages] [dirToSaveResImages]" << endl; - return -1; - } - - cout << "< 1.) Creating feature detector, descriptor extractor and descriptor matcher ..." << endl; - Ptr detector = createFeatureDetector( argv[1] ); - Ptr descriptorExtractor = createDescriptorExtractor( argv[2] ); - Ptr descriptorMatcher = createDescriptorMatcher( argv[3] ); - cout << ">" << endl; - if( detector.empty() || descriptorExtractor.empty() || descriptorMatcher.empty() ) - { - 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() ) - { - cout << "Query image can not be read." << endl << ">" << endl; - return -1; - } - string trainDirName; - vector trainFilenames; - vector usedTrainImgIdxs; - readTrainFilenames( argv[5], trainDirName, trainFilenames ); - if( trainFilenames.empty() ) - { - cout << "Train image filenames can not be read." << endl << ">" << endl; - return -1; - } - for( size_t i = 0; i < trainFilenames.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 ); - } - if( trainImgCollection.empty() ) - { - cout << "All train images can not be read." << endl << ">" << endl; - return -1; - } - else - cout << trainImgCollection.size() << " train images were read." << endl; - cout << ">" << endl; - - cout << endl << "< 3.) Extracting keypoints from images..." << endl; - detector->detect( queryImg, queryPoints ); - detector->detect( trainImgCollection, trainPointCollection ); - cout << ">" << endl; - - cout << "< 4.) Computing descriptors for keypoints..." << endl; - descriptorExtractor->compute( queryImg, queryPoints, queryDescs ); - descriptorExtractor->compute( trainImgCollection, trainPointCollection, trainDescCollection ); - 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() ); - cout << ">" << endl; - - cout << "< 6.) Save results..." << endl; - Mat drawImg; - vector mask; - for( size_t i = 0; i < trainImgCollection.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 ); - } - cout << ">" << endl; - - return 0; -} +/* + * 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"; void maskMatchesByTrainImgIdx( const vector& matches, int trainImgIdx, vector& mask ) { @@ -136,3 +55,180 @@ void readTrainFilenames( const string& filename, string& dirName, vector } file.close(); } + +bool createDetectorDescriptorMatcher( const string& detectorType, const string& descriptorType, const string& matcherType, + Ptr& featureDetector, + Ptr& descriptorExtractor, + Ptr& descriptorMatcher ) +{ + cout << "< Creating feature detector, descriptor extractor and descriptor matcher ..." << endl; + featureDetector = createFeatureDetector( detectorType ); + descriptorExtractor = createDescriptorExtractor( descriptorType ); + descriptorMatcher = createDescriptorMatcher( matcherType ); + cout << ">" << endl; + + 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 isCreated; +} + +bool readImages( const string& queryImageName, const string& trainFilename, + Mat& queryImage, vector & trainImages, vector& 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 false; + } + string trainDirName; + readTrainFilenames( trainFilename, trainDirName, trainImageNames ); + if( trainImageNames.empty() ) + { + cout << "Train image filenames can not be read." << endl << ">" << endl; + return false; + } + int readImageCount = 0; + for( size_t i = 0; i < trainImageNames.size(); 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( !readImageCount ) + { + cout << "All train images can not be read." << endl << ">" << endl; + return false; + } + else + cout << readImageCount << " train images were read." << endl; + cout << ">" << endl; + + return true; +} + +void detectKeypoints( const Mat& queryImage, vector& queryKeypoints, + const vector& trainImages, vector >& trainKeypoints, + Ptr& featureDetector ) +{ + cout << endl << "< Extracting keypoints from images..." << endl; + featureDetector->detect( queryImage, queryKeypoints ); + featureDetector->detect( trainImages, trainKeypoints ); + cout << ">" << endl; +} + +void computeDescriptors( const Mat& queryImage, vector& queryKeypoints, Mat& queryDescriptors, + const vector& trainImages, vector >& trainKeypoints, vector& trainDescriptors, + Ptr& descriptorExtractor ) +{ + cout << "< Computing descriptors for keypoints..." << endl; + descriptorExtractor->compute( queryImage, queryKeypoints, queryDescriptors ); + descriptorExtractor->compute( trainImages, trainKeypoints, trainDescriptors ); + cout << ">" << endl; +} + +void matchDescriptors( const Mat& queryDescriptors, const vector& trainDescriptors, + vector& matches, Ptr& 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; +} + +void saveResultImages( const Mat& queryImage, const vector& queryKeypoints, + const vector& trainImages, const vector >& trainKeypoints, + const vector& matches, const vector& trainImagesNames, const string& resultDir ) +{ + cout << "< Save results..." << endl; + Mat drawImg; + vector mask; + for( size_t i = 0; i < trainImages.size(); i++ ) + { + 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; +} + +void printPrompt( const string& applName ) +{ + cout << endl << "Format:" << endl; + cout << applName << " [detectorType] [descriptorType] [matcherType] [queryImage] [fileWithTrainImages] [dirToSaveResImages]" << endl; + cout << endl << "Example:" << endl + << defaultDetectorType << " " << defaultDescriptorType << " " << defaultMatcherType << " " + << defaultQueryImageName << " " << defaultFileWithTrainImages << " " << defaultDirToSaveResImages << endl; +} + +int main(int argc, char** argv) +{ + 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; + } + + if( argc != 1 ) + { + detectorType = argv[1]; descriptorType = argv[2]; matcherType = argv[3]; + queryImageName = argv[4]; fileWithTrainImages = argv[5]; + dirToSaveResImages = argv[6]; + } + + Ptr featureDetector; + Ptr descriptorExtractor; + Ptr descriptorMatcher; + if( !createDetectorDescriptorMatcher( detectorType, descriptorType, matcherType, featureDetector, descriptorExtractor, descriptorMatcher ) ) + { + printPrompt( argv[0] ); + return -1; + } + + Mat queryImage; + vector trainImages; + vector trainImagesNames; + if( !readImages( queryImageName, fileWithTrainImages, queryImage, trainImages, trainImagesNames ) ) + { + printPrompt( argv[0] ); + return -1; + } + + vector queryKeypoints; + vector > trainKeypoints; + detectKeypoints( queryImage, queryKeypoints, trainImages, trainKeypoints, featureDetector ); + + Mat queryDescriptors; + vector trainDescriptors; + computeDescriptors( queryImage, queryKeypoints, queryDescriptors, + trainImages, trainKeypoints, trainDescriptors, + descriptorExtractor ); + + vector matches; + matchDescriptors( queryDescriptors, trainDescriptors, matches, descriptorMatcher ); + + saveResultImages( queryImage, queryKeypoints, trainImages, trainKeypoints, + matches, trainImagesNames, dirToSaveResImages ); + return 0; +} diff --git a/samples/cpp/matching_to_many_images/query.png b/samples/cpp/matching_to_many_images/query.png new file mode 100755 index 0000000000..9aa1199ee6 Binary files /dev/null and b/samples/cpp/matching_to_many_images/query.png differ diff --git a/samples/cpp/matching_to_many_images/train/1.png b/samples/cpp/matching_to_many_images/train/1.png new file mode 100755 index 0000000000..c52e19ea51 Binary files /dev/null and b/samples/cpp/matching_to_many_images/train/1.png differ diff --git a/samples/cpp/matching_to_many_images/train/2.png b/samples/cpp/matching_to_many_images/train/2.png new file mode 100755 index 0000000000..7b1189cf06 Binary files /dev/null and b/samples/cpp/matching_to_many_images/train/2.png differ diff --git a/samples/cpp/matching_to_many_images/train/3.png b/samples/cpp/matching_to_many_images/train/3.png new file mode 100755 index 0000000000..dc2389aa75 Binary files /dev/null and b/samples/cpp/matching_to_many_images/train/3.png differ diff --git a/samples/cpp/matching_to_many_images/train/trainImages.txt b/samples/cpp/matching_to_many_images/train/trainImages.txt new file mode 100755 index 0000000000..b37663950f --- /dev/null +++ b/samples/cpp/matching_to_many_images/train/trainImages.txt @@ -0,0 +1,3 @@ +1.png +2.png +3.png diff --git a/tests/cv/src/afeatures2d.cpp b/tests/cv/src/afeatures2d.cpp index 4b36c4e9cd..d8a1cb5f9f 100644 --- a/tests/cv/src/afeatures2d.cpp +++ b/tests/cv/src/afeatures2d.cpp @@ -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 floatCalonderTest( "descriptor-calonde * Matchers */ CV_DescriptorMatcherTest bruteForceMatcherTest( "descriptor-matcher-brute-force", - new BruteForceMatcher >, 0.01 ); + new BruteForceMatcher >, 0.01f ); CV_DescriptorMatcherTest flannBasedMatcherTest( "descriptor-matcher-flann-based", - new FlannBasedMatcher, 0.04 ); + new FlannBasedMatcher, 0.04f );