diff --git a/modules/features2d/include/opencv2/features2d/features2d.hpp b/modules/features2d/include/opencv2/features2d/features2d.hpp index 66ffea98e8..31a14d035f 100644 --- a/modules/features2d/include/opencv2/features2d/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d/features2d.hpp @@ -266,7 +266,7 @@ CV_EXPORTS void read(const FileNode& node, CV_OUT vector& keypoints); /* * A class filters a vector of keypoints. * Because now it is difficult to provide a convenient interface for all usage scenarios of the keypoints filter class, - * it has only 2 needed by now static methods. + * it has only 3 needed by now static methods. */ class CV_EXPORTS KeyPointsFilter { @@ -281,6 +281,10 @@ public: * Remove keypoints of sizes out of range. */ static void runByKeypointSize( vector& keypoints, float minSize, float maxSize=std::numeric_limits::max() ); + /* + * Remove keypoints from some image by mask for pixels of this image. + */ + static void runByPixelsMask( vector& keypoints, const Mat& mask ); }; /*! @@ -1243,13 +1247,7 @@ public: static Ptr create( const string& detectorType ); 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. - */ - static void removeInvalidPoints( const Mat& mask, vector& keypoints ); + virtual void detectImpl( const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const = 0; }; class CV_EXPORTS FastFeatureDetector : public FeatureDetector diff --git a/modules/features2d/src/detectors.cpp b/modules/features2d/src/detectors.cpp index d5a0f83743..552adf3948 100644 --- a/modules/features2d/src/detectors.cpp +++ b/modules/features2d/src/detectors.cpp @@ -48,17 +48,6 @@ namespace cv /* * FeatureDetector */ -class MaskPredicate -{ -public: - MaskPredicate( const Mat& _mask ) : mask(_mask) {} - bool operator() (const KeyPoint& key_pt) const - { - return mask.at( (int)(key_pt.pt.y + 0.5f), (int)(key_pt.pt.x + 0.5f) ) == 0; - } -private: - const Mat mask; -}; FeatureDetector::~FeatureDetector() {} @@ -82,14 +71,6 @@ void FeatureDetector::detect(const vector& imageCollection, vector& keypoints ) -{ - if( mask.empty() ) - return; - - keypoints.erase(remove_if(keypoints.begin(), keypoints.end(), MaskPredicate(mask)), keypoints.end()); -}; - void FeatureDetector::read( const FileNode& ) {} @@ -179,7 +160,7 @@ void FastFeatureDetector::detectImpl( const Mat& image, vector& keypoi Mat grayImage = image; if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); FAST( grayImage, keypoints, threshold, nonmaxSuppression ); - removeInvalidPoints( mask, keypoints ); + KeyPointsFilter::runByPixelsMask( keypoints, mask ); } /* @@ -360,7 +341,7 @@ void StarFeatureDetector::detectImpl( const Mat& image, vector& keypoi if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); star(grayImage, keypoints); - removeInvalidPoints(mask, keypoints); + KeyPointsFilter::runByPixelsMask( keypoints, mask ); } /* @@ -482,7 +463,7 @@ void DenseFeatureDetector::detectImpl( const Mat& image, vector& keypo if( params.varyImgBoundWithScale ) curBound = static_cast( curBound * params.featureScaleMul + 0.5f ); } - removeInvalidPoints( mask, keypoints ); + KeyPointsFilter::runByPixelsMask( keypoints, mask ); } /* @@ -566,7 +547,7 @@ void PyramidAdaptedFeatureDetector::detectImpl( const Mat& image, vector new_pts; - detector->detect(src, new_pts); + detector->detect( src, new_pts, mask ); for( vector::iterator it = new_pts.begin(), end = new_pts.end(); it != end; ++it) { it->pt.x *= multiplier; @@ -574,7 +555,6 @@ void PyramidAdaptedFeatureDetector::detectImpl( const Mat& image, vectorsize *= multiplier; it->octave = l; } - removeInvalidPoints( mask, new_pts ); keypoints.insert( keypoints.end(), new_pts.begin(), new_pts.end() ); // Downsample diff --git a/modules/features2d/src/keypoint.cpp b/modules/features2d/src/keypoint.cpp index 88305914c5..6f76026a5a 100644 --- a/modules/features2d/src/keypoint.cpp +++ b/modules/features2d/src/keypoint.cpp @@ -200,4 +200,25 @@ void KeyPointsFilter::runByKeypointSize( vector& keypoints, float minS keypoints.end() ); } +class MaskPredicate +{ +public: + MaskPredicate( const Mat& _mask ) : mask(_mask) {} + bool operator() (const KeyPoint& key_pt) const + { + return mask.at( (int)(key_pt.pt.y + 0.5f), (int)(key_pt.pt.x + 0.5f) ) == 0; + } + +private: + const Mat mask; +}; + +void KeyPointsFilter::runByPixelsMask( vector& keypoints, const Mat& mask ) +{ + if( mask.empty() ) + return; + + keypoints.erase(remove_if(keypoints.begin(), keypoints.end(), MaskPredicate(mask)), keypoints.end()); +} + } diff --git a/modules/features2d/src/sift.cpp b/modules/features2d/src/sift.cpp index f97f1faa61..fca4de70c2 100644 --- a/modules/features2d/src/sift.cpp +++ b/modules/features2d/src/sift.cpp @@ -2194,16 +2194,49 @@ static void removeDuplicatedKeypoints(vector& keypoints) keypoints.resize(j); } - // detectors -void SIFT::operator()(const Mat& img, const Mat& mask, +void SIFT::operator()(const Mat& image, const Mat& mask, vector& keypoints) const { - if( img.empty() || img.type() != CV_8UC1 ) - CV_Error( CV_StsBadArg, "img is empty or has incorrect type" ); + if( image.empty() || image.type() != CV_8UC1 ) + CV_Error( CV_StsBadArg, "image is empty or has incorrect type (!=CV_8UC1)" ); + + if( !mask.empty() && mask.type() != CV_8UC1 ) + CV_Error( CV_StsBadArg, "mask has incorrect type (!=CV_8UC1)" ); + + Mat subImage, subMask; + Rect brect( 0, 0, image.cols, image.rows ); + if( mask.empty() ) + { + subImage = image; + } + else + { + vector points; + points.reserve( image.rows * image.cols ); + for( int y = 0; y < mask.rows; y++ ) + { + for( int x = 0; x < mask.cols; x++ ) + { + if( mask.at(y,x) ) + points.push_back( cv::Point(x,y) ); + } + } + brect = cv::boundingRect( points ); + + if( brect.x == 0 && brect.y == 0 && brect.width == mask.cols && brect.height == mask.rows ) + { + subImage = image; + } + else + { + subImage = image( brect ); + subMask = mask( brect ); + } + } Mat fimg; - img.convertTo(fimg, CV_32FC1, 1.0/255.0); + subImage.convertTo( fimg, CV_32FC1, 1.0/255.0 ); const double sigman = .5 ; const double sigma0 = 1.6 * powf(2.0f, 1.0f / commParams.nOctaveLayers) ; @@ -2225,7 +2258,20 @@ void SIFT::operator()(const Mat& img, const Mat& mask, keypoints.push_back( vlKeypointToOcv(vlsift, *iter, angleVal*a_180divPI) ); } } + removeDuplicatedKeypoints(keypoints); + + if( !subMask.empty() ) + { + // filter points by subMask and convert the points coordinates from subImage size to image size + KeyPointsFilter::runByPixelsMask( keypoints, subMask ); + int dx = brect.x, dy = brect.y; + for( vector::iterator it = keypoints.begin(); it != keypoints.end(); ++it ) + { + it->pt.x += dx; + it->pt.y += dy; + } + } } struct InvalidKeypoint @@ -2234,16 +2280,16 @@ struct InvalidKeypoint }; // descriptors -void SIFT::operator()(const Mat& img, const Mat& mask, +void SIFT::operator()(const Mat& image, const Mat& mask, vector& keypoints, Mat& descriptors, bool useProvidedKeypoints) const { - if( img.empty() || img.type() != CV_8UC1 ) + if( image.empty() || image.type() != CV_8UC1 ) CV_Error( CV_StsBadArg, "img is empty or has incorrect type" ); Mat fimg; - img.convertTo(fimg, CV_32FC1, 1.0/255.0); + image.convertTo(fimg, CV_32FC1, 1.0/255.0); const double sigman = .5 ; const double sigma0 = 1.6 * powf(2.0f, 1.0f / commParams.nOctaveLayers) ; @@ -2251,7 +2297,12 @@ void SIFT::operator()(const Mat& img, const Mat& mask, const double a_PIdiv180 = CV_PI/180.; if( !useProvidedKeypoints ) - (*this)(img, mask, keypoints); + (*this)(image, mask, keypoints); + else + { + // filter keypoints by mask + KeyPointsFilter::runByPixelsMask( keypoints, mask ); + } VL::Sift vlsift((float*)fimg.data, fimg.cols, fimg.rows, sigman, sigma0, commParams.nOctaves, commParams.nOctaveLayers,