Home | History | Annotate | Download | only in src
      1 /*M///////////////////////////////////////////////////////////////////////////////////////
      2 //
      3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
      4 //
      5 //  By downloading, copying, installing or using the software you agree to this license.
      6 //  If you do not agree to this license, do not download, install,
      7 //  copy or use the software.
      8 //
      9 //
     10 //                           License Agreement
     11 //                For Open Source Computer Vision Library
     12 //
     13 // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
     14 // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
     15 // Third party copyrights are property of their respective owners.
     16 //
     17 // Redistribution and use in source and binary forms, with or without modification,
     18 // are permitted provided that the following conditions are met:
     19 //
     20 //   * Redistribution's of source code must retain the above copyright notice,
     21 //     this list of conditions and the following disclaimer.
     22 //
     23 //   * Redistribution's in binary form must reproduce the above copyright notice,
     24 //     this list of conditions and the following disclaimer in the documentation
     25 //     and/or other materials provided with the distribution.
     26 //
     27 //   * The name of the copyright holders may not be used to endorse or promote products
     28 //     derived from this software without specific prior written permission.
     29 //
     30 // This software is provided by the copyright holders and contributors "as is" and
     31 // any express or implied warranties, including, but not limited to, the implied
     32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
     33 // In no event shall the Intel Corporation or contributors be liable for any direct,
     34 // indirect, incidental, special, exemplary, or consequential damages
     35 // (including, but not limited to, procurement of substitute goods or services;
     36 // loss of use, data, or profits; or business interruption) however caused
     37 // and on any theory of liability, whether in contract, strict liability,
     38 // or tort (including negligence or otherwise) arising in any way out of
     39 // the use of this software, even if advised of the possibility of such damage.
     40 //
     41 //M*/
     42 
     43 #include "precomp.hpp"
     44 #include <iterator>
     45 #include <limits>
     46 
     47 //#define DEBUG_BLOB_DETECTOR
     48 
     49 #ifdef DEBUG_BLOB_DETECTOR
     50 #  include "opencv2/opencv_modules.hpp"
     51 #  ifdef HAVE_OPENCV_HIGHGUI
     52 #    include "opencv2/highgui.hpp"
     53 #  else
     54 #    undef DEBUG_BLOB_DETECTOR
     55 #  endif
     56 #endif
     57 
     58 namespace cv
     59 {
     60 
     61 class CV_EXPORTS_W SimpleBlobDetectorImpl : public SimpleBlobDetector
     62 {
     63 public:
     64 
     65   explicit SimpleBlobDetectorImpl(const SimpleBlobDetector::Params &parameters = SimpleBlobDetector::Params());
     66 
     67   virtual void read( const FileNode& fn );
     68   virtual void write( FileStorage& fs ) const;
     69 
     70 protected:
     71   struct CV_EXPORTS Center
     72   {
     73       Point2d location;
     74       double radius;
     75       double confidence;
     76   };
     77 
     78   virtual void detect( InputArray image, std::vector<KeyPoint>& keypoints, InputArray mask=noArray() );
     79   virtual void findBlobs(InputArray image, InputArray binaryImage, std::vector<Center> &centers) const;
     80 
     81   Params params;
     82 };
     83 
     84 /*
     85 *  SimpleBlobDetector
     86 */
     87 SimpleBlobDetector::Params::Params()
     88 {
     89     thresholdStep = 10;
     90     minThreshold = 50;
     91     maxThreshold = 220;
     92     minRepeatability = 2;
     93     minDistBetweenBlobs = 10;
     94 
     95     filterByColor = true;
     96     blobColor = 0;
     97 
     98     filterByArea = true;
     99     minArea = 25;
    100     maxArea = 5000;
    101 
    102     filterByCircularity = false;
    103     minCircularity = 0.8f;
    104     maxCircularity = std::numeric_limits<float>::max();
    105 
    106     filterByInertia = true;
    107     //minInertiaRatio = 0.6;
    108     minInertiaRatio = 0.1f;
    109     maxInertiaRatio = std::numeric_limits<float>::max();
    110 
    111     filterByConvexity = true;
    112     //minConvexity = 0.8;
    113     minConvexity = 0.95f;
    114     maxConvexity = std::numeric_limits<float>::max();
    115 }
    116 
    117 void SimpleBlobDetector::Params::read(const cv::FileNode& fn )
    118 {
    119     thresholdStep = fn["thresholdStep"];
    120     minThreshold = fn["minThreshold"];
    121     maxThreshold = fn["maxThreshold"];
    122 
    123     minRepeatability = (size_t)(int)fn["minRepeatability"];
    124     minDistBetweenBlobs = fn["minDistBetweenBlobs"];
    125 
    126     filterByColor = (int)fn["filterByColor"] != 0 ? true : false;
    127     blobColor = (uchar)(int)fn["blobColor"];
    128 
    129     filterByArea = (int)fn["filterByArea"] != 0 ? true : false;
    130     minArea = fn["minArea"];
    131     maxArea = fn["maxArea"];
    132 
    133     filterByCircularity = (int)fn["filterByCircularity"] != 0 ? true : false;
    134     minCircularity = fn["minCircularity"];
    135     maxCircularity = fn["maxCircularity"];
    136 
    137     filterByInertia = (int)fn["filterByInertia"] != 0 ? true : false;
    138     minInertiaRatio = fn["minInertiaRatio"];
    139     maxInertiaRatio = fn["maxInertiaRatio"];
    140 
    141     filterByConvexity = (int)fn["filterByConvexity"] != 0 ? true : false;
    142     minConvexity = fn["minConvexity"];
    143     maxConvexity = fn["maxConvexity"];
    144 }
    145 
    146 void SimpleBlobDetector::Params::write(cv::FileStorage& fs) const
    147 {
    148     fs << "thresholdStep" << thresholdStep;
    149     fs << "minThreshold" << minThreshold;
    150     fs << "maxThreshold" << maxThreshold;
    151 
    152     fs << "minRepeatability" << (int)minRepeatability;
    153     fs << "minDistBetweenBlobs" << minDistBetweenBlobs;
    154 
    155     fs << "filterByColor" << (int)filterByColor;
    156     fs << "blobColor" << (int)blobColor;
    157 
    158     fs << "filterByArea" << (int)filterByArea;
    159     fs << "minArea" << minArea;
    160     fs << "maxArea" << maxArea;
    161 
    162     fs << "filterByCircularity" << (int)filterByCircularity;
    163     fs << "minCircularity" << minCircularity;
    164     fs << "maxCircularity" << maxCircularity;
    165 
    166     fs << "filterByInertia" << (int)filterByInertia;
    167     fs << "minInertiaRatio" << minInertiaRatio;
    168     fs << "maxInertiaRatio" << maxInertiaRatio;
    169 
    170     fs << "filterByConvexity" << (int)filterByConvexity;
    171     fs << "minConvexity" << minConvexity;
    172     fs << "maxConvexity" << maxConvexity;
    173 }
    174 
    175 SimpleBlobDetectorImpl::SimpleBlobDetectorImpl(const SimpleBlobDetector::Params &parameters) :
    176 params(parameters)
    177 {
    178 }
    179 
    180 void SimpleBlobDetectorImpl::read( const cv::FileNode& fn )
    181 {
    182     params.read(fn);
    183 }
    184 
    185 void SimpleBlobDetectorImpl::write( cv::FileStorage& fs ) const
    186 {
    187     params.write(fs);
    188 }
    189 
    190 void SimpleBlobDetectorImpl::findBlobs(InputArray _image, InputArray _binaryImage, std::vector<Center> &centers) const
    191 {
    192     Mat image = _image.getMat(), binaryImage = _binaryImage.getMat();
    193     (void)image;
    194     centers.clear();
    195 
    196     std::vector < std::vector<Point> > contours;
    197     Mat tmpBinaryImage = binaryImage.clone();
    198     findContours(tmpBinaryImage, contours, RETR_LIST, CHAIN_APPROX_NONE);
    199 
    200 #ifdef DEBUG_BLOB_DETECTOR
    201     //  Mat keypointsImage;
    202     //  cvtColor( binaryImage, keypointsImage, CV_GRAY2RGB );
    203     //
    204     //  Mat contoursImage;
    205     //  cvtColor( binaryImage, contoursImage, CV_GRAY2RGB );
    206     //  drawContours( contoursImage, contours, -1, Scalar(0,255,0) );
    207     //  imshow("contours", contoursImage );
    208 #endif
    209 
    210     for (size_t contourIdx = 0; contourIdx < contours.size(); contourIdx++)
    211     {
    212         Center center;
    213         center.confidence = 1;
    214         Moments moms = moments(Mat(contours[contourIdx]));
    215         if (params.filterByArea)
    216         {
    217             double area = moms.m00;
    218             if (area < params.minArea || area >= params.maxArea)
    219                 continue;
    220         }
    221 
    222         if (params.filterByCircularity)
    223         {
    224             double area = moms.m00;
    225             double perimeter = arcLength(Mat(contours[contourIdx]), true);
    226             double ratio = 4 * CV_PI * area / (perimeter * perimeter);
    227             if (ratio < params.minCircularity || ratio >= params.maxCircularity)
    228                 continue;
    229         }
    230 
    231         if (params.filterByInertia)
    232         {
    233             double denominator = std::sqrt(std::pow(2 * moms.mu11, 2) + std::pow(moms.mu20 - moms.mu02, 2));
    234             const double eps = 1e-2;
    235             double ratio;
    236             if (denominator > eps)
    237             {
    238                 double cosmin = (moms.mu20 - moms.mu02) / denominator;
    239                 double sinmin = 2 * moms.mu11 / denominator;
    240                 double cosmax = -cosmin;
    241                 double sinmax = -sinmin;
    242 
    243                 double imin = 0.5 * (moms.mu20 + moms.mu02) - 0.5 * (moms.mu20 - moms.mu02) * cosmin - moms.mu11 * sinmin;
    244                 double imax = 0.5 * (moms.mu20 + moms.mu02) - 0.5 * (moms.mu20 - moms.mu02) * cosmax - moms.mu11 * sinmax;
    245                 ratio = imin / imax;
    246             }
    247             else
    248             {
    249                 ratio = 1;
    250             }
    251 
    252             if (ratio < params.minInertiaRatio || ratio >= params.maxInertiaRatio)
    253                 continue;
    254 
    255             center.confidence = ratio * ratio;
    256         }
    257 
    258         if (params.filterByConvexity)
    259         {
    260             std::vector < Point > hull;
    261             convexHull(Mat(contours[contourIdx]), hull);
    262             double area = contourArea(Mat(contours[contourIdx]));
    263             double hullArea = contourArea(Mat(hull));
    264             double ratio = area / hullArea;
    265             if (ratio < params.minConvexity || ratio >= params.maxConvexity)
    266                 continue;
    267         }
    268 
    269         if(moms.m00 == 0.0)
    270             continue;
    271         center.location = Point2d(moms.m10 / moms.m00, moms.m01 / moms.m00);
    272 
    273         if (params.filterByColor)
    274         {
    275             if (binaryImage.at<uchar> (cvRound(center.location.y), cvRound(center.location.x)) != params.blobColor)
    276                 continue;
    277         }
    278 
    279         //compute blob radius
    280         {
    281             std::vector<double> dists;
    282             for (size_t pointIdx = 0; pointIdx < contours[contourIdx].size(); pointIdx++)
    283             {
    284                 Point2d pt = contours[contourIdx][pointIdx];
    285                 dists.push_back(norm(center.location - pt));
    286             }
    287             std::sort(dists.begin(), dists.end());
    288             center.radius = (dists[(dists.size() - 1) / 2] + dists[dists.size() / 2]) / 2.;
    289         }
    290 
    291         centers.push_back(center);
    292 
    293 
    294 #ifdef DEBUG_BLOB_DETECTOR
    295         //    circle( keypointsImage, center.location, 1, Scalar(0,0,255), 1 );
    296 #endif
    297     }
    298 #ifdef DEBUG_BLOB_DETECTOR
    299     //  imshow("bk", keypointsImage );
    300     //  waitKey();
    301 #endif
    302 }
    303 
    304 void SimpleBlobDetectorImpl::detect(InputArray image, std::vector<cv::KeyPoint>& keypoints, InputArray)
    305 {
    306     //TODO: support mask
    307     keypoints.clear();
    308     Mat grayscaleImage;
    309     if (image.channels() == 3)
    310         cvtColor(image, grayscaleImage, COLOR_BGR2GRAY);
    311     else
    312         grayscaleImage = image.getMat();
    313 
    314     std::vector < std::vector<Center> > centers;
    315     for (double thresh = params.minThreshold; thresh < params.maxThreshold; thresh += params.thresholdStep)
    316     {
    317         Mat binarizedImage;
    318         threshold(grayscaleImage, binarizedImage, thresh, 255, THRESH_BINARY);
    319 
    320         std::vector < Center > curCenters;
    321         findBlobs(grayscaleImage, binarizedImage, curCenters);
    322         std::vector < std::vector<Center> > newCenters;
    323         for (size_t i = 0; i < curCenters.size(); i++)
    324         {
    325             bool isNew = true;
    326             for (size_t j = 0; j < centers.size(); j++)
    327             {
    328                 double dist = norm(centers[j][ centers[j].size() / 2 ].location - curCenters[i].location);
    329                 isNew = dist >= params.minDistBetweenBlobs && dist >= centers[j][ centers[j].size() / 2 ].radius && dist >= curCenters[i].radius;
    330                 if (!isNew)
    331                 {
    332                     centers[j].push_back(curCenters[i]);
    333 
    334                     size_t k = centers[j].size() - 1;
    335                     while( k > 0 && centers[j][k].radius < centers[j][k-1].radius )
    336                     {
    337                         centers[j][k] = centers[j][k-1];
    338                         k--;
    339                     }
    340                     centers[j][k] = curCenters[i];
    341 
    342                     break;
    343                 }
    344             }
    345             if (isNew)
    346                 newCenters.push_back(std::vector<Center> (1, curCenters[i]));
    347         }
    348         std::copy(newCenters.begin(), newCenters.end(), std::back_inserter(centers));
    349     }
    350 
    351     for (size_t i = 0; i < centers.size(); i++)
    352     {
    353         if (centers[i].size() < params.minRepeatability)
    354             continue;
    355         Point2d sumPoint(0, 0);
    356         double normalizer = 0;
    357         for (size_t j = 0; j < centers[i].size(); j++)
    358         {
    359             sumPoint += centers[i][j].confidence * centers[i][j].location;
    360             normalizer += centers[i][j].confidence;
    361         }
    362         sumPoint *= (1. / normalizer);
    363         KeyPoint kpt(sumPoint, (float)(centers[i][centers[i].size() / 2].radius) * 2.0f);
    364         keypoints.push_back(kpt);
    365     }
    366 }
    367 
    368 Ptr<SimpleBlobDetector> SimpleBlobDetector::create(const SimpleBlobDetector::Params& params)
    369 {
    370     return makePtr<SimpleBlobDetectorImpl>(params);
    371 }
    372 
    373 }
    374