From 66c116ec6ad77a470b2380ff9369dbe659c6f67d Mon Sep 17 00:00:00 2001
From: Maria Dimashova <no@email>
Date: Thu, 5 May 2011 10:10:46 +0000
Subject: [PATCH] fixed #1044

---
 .../include/opencv2/features2d/features2d.hpp | 14 ++--
 modules/features2d/src/detectors.cpp          | 28 ++------
 modules/features2d/src/keypoint.cpp           | 21 ++++++
 modules/features2d/src/sift.cpp               | 69 ++++++++++++++++---
 4 files changed, 91 insertions(+), 41 deletions(-)

diff --git a/modules/features2d/include/opencv2/features2d/features2d.hpp b/modules/features2d/include/opencv2/features2d/features2d.hpp
index 66ffea98e..31a14d035 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<KeyPoint>& 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<KeyPoint>& keypoints, float minSize, float maxSize=std::numeric_limits<float>::max() );
+    /*
+     * Remove keypoints from some image by mask for pixels of this image.
+     */
+    static void runByPixelsMask( vector<KeyPoint>& keypoints, const Mat& mask );
 };
 
 /*!
@@ -1243,13 +1247,7 @@ public:
     static Ptr<FeatureDetector> create( const string& detectorType );
 
 protected:
-	virtual void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const = 0;
-    /*
-     * Remove keypoints that are not in the mask.
-     * Helper function, useful when wrapping a library call for keypoint detection that
-     * does not support a mask argument.
-     */
-    static void removeInvalidPoints( const Mat& mask, vector<KeyPoint>& keypoints );
+    virtual void detectImpl( const Mat& image, vector<KeyPoint>& 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 d5a0f8374..552adf394 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<uchar>( (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<Mat>& imageCollection, vector<vector<K
         detect( imageCollection[i], pointCollection[i], masks.empty() ? Mat() : masks[i] );
 }
 
-void FeatureDetector::removeInvalidPoints( const Mat& mask, vector<KeyPoint>& keypoints )
-{
-    if( mask.empty() )
-        return;
-
-    keypoints.erase(remove_if(keypoints.begin(), keypoints.end(), MaskPredicate(mask)), keypoints.end());
-};
-
 void FeatureDetector::read( const FileNode& )
 {}
 
@@ -179,7 +160,7 @@ void FastFeatureDetector::detectImpl( const Mat& image, vector<KeyPoint>& 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<KeyPoint>& 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<KeyPoint>& keypo
         if( params.varyImgBoundWithScale ) curBound = static_cast<int>( curBound * params.featureScaleMul + 0.5f );
     }
 
-    removeInvalidPoints( mask, keypoints );
+    KeyPointsFilter::runByPixelsMask( keypoints, mask );
 }
 
 /*
@@ -566,7 +547,7 @@ void PyramidAdaptedFeatureDetector::detectImpl( const Mat& image, vector<KeyPoin
     {
         // Detect on current level of the pyramid
         vector<KeyPoint> new_pts;
-        detector->detect(src, new_pts);
+        detector->detect( src, new_pts, mask );
         for( vector<KeyPoint>::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, vector<KeyPoin
             it->size *= 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 88305914c..6f76026a5 100644
--- a/modules/features2d/src/keypoint.cpp
+++ b/modules/features2d/src/keypoint.cpp
@@ -200,4 +200,25 @@ void KeyPointsFilter::runByKeypointSize( vector<KeyPoint>& keypoints, float minS
                      keypoints.end() );
 }
 
+class MaskPredicate
+{
+public:
+    MaskPredicate( const Mat& _mask ) : mask(_mask) {}
+    bool operator() (const KeyPoint& key_pt) const
+    {
+        return mask.at<uchar>( (int)(key_pt.pt.y + 0.5f), (int)(key_pt.pt.x + 0.5f) ) == 0;
+    }
+
+private:
+    const Mat mask;
+};
+
+void KeyPointsFilter::runByPixelsMask( vector<KeyPoint>& 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 f97f1faa6..fca4de70c 100644
--- a/modules/features2d/src/sift.cpp
+++ b/modules/features2d/src/sift.cpp
@@ -2194,16 +2194,49 @@ static void removeDuplicatedKeypoints(vector<KeyPoint>& keypoints)
     keypoints.resize(j);
 }
 
-
 // detectors
-void SIFT::operator()(const Mat& img, const Mat& mask,
+void SIFT::operator()(const Mat& image, const Mat& mask,
                       vector<KeyPoint>& 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<Point> 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<uchar>(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<KeyPoint>::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<KeyPoint>& 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,