2010-12-21 17:24:36 +08:00
/*M///////////////////////////////////////////////////////////////////////////////////////
2011-06-12 01:24:09 +08:00
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
2010-12-21 17:24:36 +08:00
2010-12-27 17:15:08 +08:00
# include "precomp.hpp"
2011-02-10 06:45:45 +08:00
# include <iterator>
2013-05-07 10:45:11 +08:00
# include <limits>
2010-12-21 17:24:36 +08:00
2021-10-06 20:05:45 +08:00
# include <opencv2/core/utils/logger.hpp>
2019-04-16 02:59:51 +08:00
// Requires CMake flag: DEBUG_opencv_features2d=ON
2011-02-07 15:57:32 +08:00
//#define DEBUG_BLOB_DETECTOR
# ifdef DEBUG_BLOB_DETECTOR
2019-04-16 02:59:51 +08:00
# include "opencv2/highgui.hpp"
2011-02-07 15:57:32 +08:00
# endif
2014-10-14 03:01:45 +08:00
namespace cv
{
2022-10-08 00:07:51 +08:00
// TODO: To be removed in 5.x branch
const std : : vector < std : : vector < cv : : Point > > & SimpleBlobDetector : : getBlobContours ( ) const
{
CV_Error ( Error : : StsNotImplemented , " Method SimpleBlobDetector::getBlobContours() is not implemented " ) ;
}
2014-10-14 03:01:45 +08:00
class CV_EXPORTS_W SimpleBlobDetectorImpl : public SimpleBlobDetector
{
public :
explicit SimpleBlobDetectorImpl ( const SimpleBlobDetector : : Params & parameters = SimpleBlobDetector : : Params ( ) ) ;
2018-03-15 21:16:54 +08:00
virtual void read ( const FileNode & fn ) CV_OVERRIDE ;
virtual void write ( FileStorage & fs ) const CV_OVERRIDE ;
2014-10-14 03:01:45 +08:00
2022-12-21 21:03:00 +08:00
void setParams ( const SimpleBlobDetector : : Params & _params ) CV_OVERRIDE {
SimpleBlobDetectorImpl : : validateParameters ( _params ) ;
params = _params ;
}
SimpleBlobDetector : : Params getParams ( ) const CV_OVERRIDE { return params ; }
static void validateParameters ( const SimpleBlobDetector : : Params & p )
{
if ( p . thresholdStep < = 0 )
CV_Error ( Error : : StsBadArg , " thresholdStep>0 " ) ;
2022-12-26 17:07:00 +08:00
if ( p . minThreshold > p . maxThreshold | | p . minThreshold < 0 )
CV_Error ( Error : : StsBadArg , " 0<=minThreshold<=maxThreshold " ) ;
2022-12-21 21:03:00 +08:00
if ( p . minDistBetweenBlobs < = 0 )
CV_Error ( Error : : StsBadArg , " minDistBetweenBlobs>0 " ) ;
if ( p . minArea > p . maxArea | | p . minArea < = 0 )
CV_Error ( Error : : StsBadArg , " 0<minArea<=maxArea " ) ;
if ( p . minCircularity > p . maxCircularity | | p . minCircularity < = 0 )
CV_Error ( Error : : StsBadArg , " 0<minCircularity<=maxCircularity " ) ;
if ( p . minInertiaRatio > p . maxInertiaRatio | | p . minInertiaRatio < = 0 )
CV_Error ( Error : : StsBadArg , " 0<minInertiaRatio<=maxInertiaRatio " ) ;
if ( p . minConvexity > p . maxConvexity | | p . minConvexity < = 0 )
CV_Error ( Error : : StsBadArg , " 0<minConvexity<=maxConvexity " ) ;
}
2014-10-14 03:01:45 +08:00
protected :
struct CV_EXPORTS Center
{
Point2d location ;
double radius ;
double confidence ;
} ;
2018-03-15 21:16:54 +08:00
virtual void detect ( InputArray image , std : : vector < KeyPoint > & keypoints , InputArray mask = noArray ( ) ) CV_OVERRIDE ;
2022-10-08 00:07:51 +08:00
virtual void findBlobs ( InputArray image , InputArray binaryImage , std : : vector < Center > & centers ,
std : : vector < std : : vector < Point > > & contours , std : : vector < Moments > & moments ) const ;
virtual const std : : vector < std : : vector < Point > > & getBlobContours ( ) const CV_OVERRIDE ;
2014-10-14 03:01:45 +08:00
Params params ;
2022-10-08 00:07:51 +08:00
std : : vector < std : : vector < Point > > blobContours ;
2014-10-14 03:01:45 +08:00
} ;
2010-12-21 17:24:36 +08:00
2010-12-27 17:15:08 +08:00
/*
2011-06-12 01:24:09 +08:00
* SimpleBlobDetector
*/
2010-12-27 17:15:08 +08:00
SimpleBlobDetector : : Params : : Params ( )
2010-12-21 17:24:36 +08:00
{
2012-10-17 15:12:04 +08:00
thresholdStep = 10 ;
minThreshold = 50 ;
maxThreshold = 220 ;
minRepeatability = 2 ;
minDistBetweenBlobs = 10 ;
filterByColor = true ;
blobColor = 0 ;
filterByArea = true ;
minArea = 25 ;
maxArea = 5000 ;
filterByCircularity = false ;
minCircularity = 0.8f ;
maxCircularity = std : : numeric_limits < float > : : max ( ) ;
filterByInertia = true ;
//minInertiaRatio = 0.6;
minInertiaRatio = 0.1f ;
maxInertiaRatio = std : : numeric_limits < float > : : max ( ) ;
filterByConvexity = true ;
//minConvexity = 0.8;
minConvexity = 0.95f ;
maxConvexity = std : : numeric_limits < float > : : max ( ) ;
2022-10-08 00:07:51 +08:00
collectContours = false ;
2010-12-21 17:24:36 +08:00
}
2011-08-10 17:17:37 +08:00
void SimpleBlobDetector : : Params : : read ( const cv : : FileNode & fn )
{
thresholdStep = fn [ " thresholdStep " ] ;
minThreshold = fn [ " minThreshold " ] ;
maxThreshold = fn [ " maxThreshold " ] ;
minRepeatability = ( size_t ) ( int ) fn [ " minRepeatability " ] ;
minDistBetweenBlobs = fn [ " minDistBetweenBlobs " ] ;
filterByColor = ( int ) fn [ " filterByColor " ] ! = 0 ? true : false ;
blobColor = ( uchar ) ( int ) fn [ " blobColor " ] ;
filterByArea = ( int ) fn [ " filterByArea " ] ! = 0 ? true : false ;
minArea = fn [ " minArea " ] ;
maxArea = fn [ " maxArea " ] ;
filterByCircularity = ( int ) fn [ " filterByCircularity " ] ! = 0 ? true : false ;
minCircularity = fn [ " minCircularity " ] ;
maxCircularity = fn [ " maxCircularity " ] ;
filterByInertia = ( int ) fn [ " filterByInertia " ] ! = 0 ? true : false ;
minInertiaRatio = fn [ " minInertiaRatio " ] ;
maxInertiaRatio = fn [ " maxInertiaRatio " ] ;
filterByConvexity = ( int ) fn [ " filterByConvexity " ] ! = 0 ? true : false ;
minConvexity = fn [ " minConvexity " ] ;
maxConvexity = fn [ " maxConvexity " ] ;
2022-10-08 00:07:51 +08:00
collectContours = ( int ) fn [ " collectContours " ] ! = 0 ? true : false ;
2011-08-10 17:17:37 +08:00
}
void SimpleBlobDetector : : Params : : write ( cv : : FileStorage & fs ) const
{
fs < < " thresholdStep " < < thresholdStep ;
fs < < " minThreshold " < < minThreshold ;
fs < < " maxThreshold " < < maxThreshold ;
fs < < " minRepeatability " < < ( int ) minRepeatability ;
fs < < " minDistBetweenBlobs " < < minDistBetweenBlobs ;
fs < < " filterByColor " < < ( int ) filterByColor ;
fs < < " blobColor " < < ( int ) blobColor ;
fs < < " filterByArea " < < ( int ) filterByArea ;
fs < < " minArea " < < minArea ;
fs < < " maxArea " < < maxArea ;
fs < < " filterByCircularity " < < ( int ) filterByCircularity ;
fs < < " minCircularity " < < minCircularity ;
fs < < " maxCircularity " < < maxCircularity ;
fs < < " filterByInertia " < < ( int ) filterByInertia ;
fs < < " minInertiaRatio " < < minInertiaRatio ;
fs < < " maxInertiaRatio " < < maxInertiaRatio ;
fs < < " filterByConvexity " < < ( int ) filterByConvexity ;
fs < < " minConvexity " < < minConvexity ;
fs < < " maxConvexity " < < maxConvexity ;
2022-10-08 00:07:51 +08:00
fs < < " collectContours " < < ( int ) collectContours ;
2011-08-10 17:17:37 +08:00
}
2014-10-16 02:49:17 +08:00
SimpleBlobDetectorImpl : : SimpleBlobDetectorImpl ( const SimpleBlobDetector : : Params & parameters ) :
2011-06-12 01:24:09 +08:00
params ( parameters )
2010-12-21 17:24:36 +08:00
{
}
2014-10-16 02:49:17 +08:00
void SimpleBlobDetectorImpl : : read ( const cv : : FileNode & fn )
2011-08-10 17:17:37 +08:00
{
2022-12-21 21:03:00 +08:00
SimpleBlobDetector : : Params rp ;
rp . read ( fn ) ;
SimpleBlobDetectorImpl : : validateParameters ( rp ) ;
params = rp ;
2011-08-10 17:17:37 +08:00
}
2014-10-16 02:49:17 +08:00
void SimpleBlobDetectorImpl : : write ( cv : : FileStorage & fs ) const
2011-08-10 17:17:37 +08:00
{
2016-03-23 06:19:42 +08:00
writeFormat ( fs ) ;
2011-08-10 17:17:37 +08:00
params . write ( fs ) ;
}
2022-10-08 00:07:51 +08:00
void SimpleBlobDetectorImpl : : findBlobs ( InputArray _image , InputArray _binaryImage , std : : vector < Center > & centers ,
std : : vector < std : : vector < Point > > & contoursOut , std : : vector < Moments > & momentss ) const
2010-12-21 17:24:36 +08:00
{
2018-09-14 05:35:26 +08:00
CV_INSTRUMENT_REGION ( ) ;
2016-08-18 14:53:00 +08:00
2014-02-04 20:34:18 +08:00
Mat image = _image . getMat ( ) , binaryImage = _binaryImage . getMat ( ) ;
2018-09-07 19:33:52 +08:00
CV_UNUSED ( image ) ;
2012-10-17 15:12:04 +08:00
centers . clear ( ) ;
2022-10-08 00:07:51 +08:00
contoursOut . clear ( ) ;
momentss . clear ( ) ;
2010-12-21 17:24:36 +08:00
2013-02-25 00:14:01 +08:00
std : : vector < std : : vector < Point > > contours ;
2019-01-12 01:31:55 +08:00
findContours ( binaryImage , contours , RETR_LIST , CHAIN_APPROX_NONE ) ;
2010-12-21 17:24:36 +08:00
2011-02-07 15:57:32 +08:00
# ifdef DEBUG_BLOB_DETECTOR
2019-04-16 02:59:51 +08:00
Mat keypointsImage ;
cvtColor ( binaryImage , keypointsImage , COLOR_GRAY2RGB ) ;
Mat contoursImage ;
cvtColor ( binaryImage , contoursImage , COLOR_GRAY2RGB ) ;
drawContours ( contoursImage , contours , - 1 , Scalar ( 0 , 255 , 0 ) ) ;
imshow ( " contours " , contoursImage ) ;
2011-02-07 15:57:32 +08:00
# endif
2010-12-21 17:24:36 +08:00
2012-10-17 15:12:04 +08:00
for ( size_t contourIdx = 0 ; contourIdx < contours . size ( ) ; contourIdx + + )
{
Center center ;
center . confidence = 1 ;
2019-01-12 01:31:55 +08:00
Moments moms = moments ( contours [ contourIdx ] ) ;
2012-10-17 15:12:04 +08:00
if ( params . filterByArea )
{
double area = moms . m00 ;
if ( area < params . minArea | | area > = params . maxArea )
continue ;
}
if ( params . filterByCircularity )
{
double area = moms . m00 ;
2019-01-12 01:31:55 +08:00
double perimeter = arcLength ( contours [ contourIdx ] , true ) ;
2012-10-17 15:12:04 +08:00
double ratio = 4 * CV_PI * area / ( perimeter * perimeter ) ;
if ( ratio < params . minCircularity | | ratio > = params . maxCircularity )
continue ;
}
if ( params . filterByInertia )
{
2013-02-25 00:14:01 +08:00
double denominator = std : : sqrt ( std : : pow ( 2 * moms . mu11 , 2 ) + std : : pow ( moms . mu20 - moms . mu02 , 2 ) ) ;
2012-10-17 15:12:04 +08:00
const double eps = 1e-2 ;
double ratio ;
if ( denominator > eps )
{
double cosmin = ( moms . mu20 - moms . mu02 ) / denominator ;
double sinmin = 2 * moms . mu11 / denominator ;
double cosmax = - cosmin ;
double sinmax = - sinmin ;
double imin = 0.5 * ( moms . mu20 + moms . mu02 ) - 0.5 * ( moms . mu20 - moms . mu02 ) * cosmin - moms . mu11 * sinmin ;
double imax = 0.5 * ( moms . mu20 + moms . mu02 ) - 0.5 * ( moms . mu20 - moms . mu02 ) * cosmax - moms . mu11 * sinmax ;
ratio = imin / imax ;
}
else
{
ratio = 1 ;
}
if ( ratio < params . minInertiaRatio | | ratio > = params . maxInertiaRatio )
continue ;
center . confidence = ratio * ratio ;
}
if ( params . filterByConvexity )
{
2013-02-25 00:14:01 +08:00
std : : vector < Point > hull ;
2019-01-12 01:31:55 +08:00
convexHull ( contours [ contourIdx ] , hull ) ;
2020-06-23 20:39:55 +08:00
double area = moms . m00 ;
2019-01-12 01:31:55 +08:00
double hullArea = contourArea ( hull ) ;
2018-07-17 21:14:54 +08:00
if ( fabs ( hullArea ) < DBL_EPSILON )
continue ;
2012-10-17 15:12:04 +08:00
double ratio = area / hullArea ;
if ( ratio < params . minConvexity | | ratio > = params . maxConvexity )
continue ;
}
2013-04-10 19:54:14 +08:00
2015-05-07 17:52:06 +08:00
if ( moms . m00 = = 0.0 )
continue ;
2012-10-17 15:12:04 +08:00
center . location = Point2d ( moms . m10 / moms . m00 , moms . m01 / moms . m00 ) ;
if ( params . filterByColor )
{
if ( binaryImage . at < uchar > ( cvRound ( center . location . y ) , cvRound ( center . location . x ) ) ! = params . blobColor )
continue ;
}
//compute blob radius
{
2013-02-25 00:14:01 +08:00
std : : vector < double > dists ;
2012-10-17 15:12:04 +08:00
for ( size_t pointIdx = 0 ; pointIdx < contours [ contourIdx ] . size ( ) ; pointIdx + + )
{
Point2d pt = contours [ contourIdx ] [ pointIdx ] ;
dists . push_back ( norm ( center . location - pt ) ) ;
}
std : : sort ( dists . begin ( ) , dists . end ( ) ) ;
center . radius = ( dists [ ( dists . size ( ) - 1 ) / 2 ] + dists [ dists . size ( ) / 2 ] ) / 2. ;
}
2013-03-31 18:43:40 +08:00
centers . push_back ( center ) ;
2022-10-08 00:07:51 +08:00
if ( params . collectContours )
{
contoursOut . push_back ( contours [ contourIdx ] ) ;
momentss . push_back ( moms ) ;
}
2010-12-21 17:24:36 +08:00
2011-02-07 15:57:32 +08:00
# ifdef DEBUG_BLOB_DETECTOR
2019-04-16 02:59:51 +08:00
circle ( keypointsImage , center . location , 1 , Scalar ( 0 , 0 , 255 ) , 1 ) ;
2011-02-07 15:57:32 +08:00
# endif
2012-10-17 15:12:04 +08:00
}
2011-02-07 15:57:32 +08:00
# ifdef DEBUG_BLOB_DETECTOR
2019-04-16 02:59:51 +08:00
imshow ( " bk " , keypointsImage ) ;
waitKey ( ) ;
2011-02-07 15:57:32 +08:00
# endif
2010-12-21 17:24:36 +08:00
}
2018-01-25 20:34:12 +08:00
void SimpleBlobDetectorImpl : : detect ( InputArray image , std : : vector < cv : : KeyPoint > & keypoints , InputArray mask )
2010-12-21 17:24:36 +08:00
{
2018-09-14 05:35:26 +08:00
CV_INSTRUMENT_REGION ( ) ;
2016-08-18 14:53:00 +08:00
2012-10-17 15:12:04 +08:00
keypoints . clear ( ) ;
2022-10-08 00:07:51 +08:00
blobContours . clear ( ) ;
2018-07-17 21:14:54 +08:00
CV_Assert ( params . minRepeatability ! = 0 ) ;
2012-10-17 15:12:04 +08:00
Mat grayscaleImage ;
2018-01-07 13:30:40 +08:00
if ( image . channels ( ) = = 3 | | image . channels ( ) = = 4 )
2013-04-10 19:54:14 +08:00
cvtColor ( image , grayscaleImage , COLOR_BGR2GRAY ) ;
2012-10-17 15:12:04 +08:00
else
2014-01-24 23:39:05 +08:00
grayscaleImage = image . getMat ( ) ;
2012-10-17 15:12:04 +08:00
2015-12-08 15:24:54 +08:00
if ( grayscaleImage . type ( ) ! = CV_8UC1 ) {
CV_Error ( Error : : StsUnsupportedFormat , " Blob detector only supports 8-bit images! " ) ;
2014-09-04 22:17:51 +08:00
}
2015-12-08 15:24:54 +08:00
2021-10-06 20:05:45 +08:00
CV_CheckGT ( params . thresholdStep , 0.0f , " " ) ;
if ( params . minThreshold + params . thresholdStep > = params . maxThreshold )
{
// https://github.com/opencv/opencv/issues/6667
CV_LOG_ONCE_INFO ( NULL , " SimpleBlobDetector: params.minDistBetweenBlobs is ignored for case with single threshold " ) ;
#if 0 // OpenCV 5.0
CV_CheckEQ ( params . minRepeatability , 1u , " Incompatible parameters for case with single threshold " ) ;
# else
if ( params . minRepeatability ! = 1 )
CV_LOG_WARNING ( NULL , " SimpleBlobDetector: params.minRepeatability= " < < params . minRepeatability < < " is incompatible for case with single threshold. Empty result is expected. " ) ;
# endif
}
2013-02-25 00:14:01 +08:00
std : : vector < std : : vector < Center > > centers ;
2022-10-08 00:07:51 +08:00
std : : vector < Moments > momentss ;
2012-10-17 15:12:04 +08:00
for ( double thresh = params . minThreshold ; thresh < params . maxThreshold ; thresh + = params . thresholdStep )
{
Mat binarizedImage ;
threshold ( grayscaleImage , binarizedImage , thresh , 255 , THRESH_BINARY ) ;
2010-12-21 17:24:36 +08:00
2013-02-25 00:14:01 +08:00
std : : vector < Center > curCenters ;
2022-10-08 00:07:51 +08:00
std : : vector < std : : vector < Point > > curContours ;
std : : vector < Moments > curMomentss ;
findBlobs ( grayscaleImage , binarizedImage , curCenters , curContours , curMomentss ) ;
2013-02-25 00:14:01 +08:00
std : : vector < std : : vector < Center > > newCenters ;
2022-10-08 00:07:51 +08:00
std : : vector < std : : vector < Point > > newContours ;
std : : vector < Moments > newMomentss ;
2012-10-17 15:12:04 +08:00
for ( size_t i = 0 ; i < curCenters . size ( ) ; i + + )
{
bool isNew = true ;
for ( size_t j = 0 ; j < centers . size ( ) ; j + + )
{
2021-10-06 20:05:45 +08:00
double dist = norm ( centers [ j ] [ centers [ j ] . size ( ) / 2 ] . location - curCenters [ i ] . location ) ;
2012-10-17 15:12:04 +08:00
isNew = dist > = params . minDistBetweenBlobs & & dist > = centers [ j ] [ centers [ j ] . size ( ) / 2 ] . radius & & dist > = curCenters [ i ] . radius ;
if ( ! isNew )
{
centers [ j ] . push_back ( curCenters [ i ] ) ;
size_t k = centers [ j ] . size ( ) - 1 ;
2019-07-01 23:48:48 +08:00
while ( k > 0 & & curCenters [ i ] . radius < centers [ j ] [ k - 1 ] . radius )
2012-10-17 15:12:04 +08:00
{
centers [ j ] [ k ] = centers [ j ] [ k - 1 ] ;
k - - ;
}
2022-10-08 00:07:51 +08:00
if ( params . collectContours )
{
if ( curCenters [ i ] . confidence > centers [ j ] [ k ] . confidence
| | ( curCenters [ i ] . confidence = = centers [ j ] [ k ] . confidence & & curMomentss [ i ] . m00 > momentss [ j ] . m00 ) )
{
blobContours [ j ] = curContours [ i ] ;
momentss [ j ] = curMomentss [ i ] ;
}
}
2012-10-17 15:12:04 +08:00
centers [ j ] [ k ] = curCenters [ i ] ;
break ;
}
}
if ( isNew )
2022-10-08 00:07:51 +08:00
{
2013-02-25 00:14:01 +08:00
newCenters . push_back ( std : : vector < Center > ( 1 , curCenters [ i ] ) ) ;
2022-10-08 00:07:51 +08:00
if ( params . collectContours )
{
newContours . push_back ( curContours [ i ] ) ;
newMomentss . push_back ( curMomentss [ i ] ) ;
}
}
2012-10-17 15:12:04 +08:00
}
std : : copy ( newCenters . begin ( ) , newCenters . end ( ) , std : : back_inserter ( centers ) ) ;
2022-10-08 00:07:51 +08:00
if ( params . collectContours )
{
std : : copy ( newContours . begin ( ) , newContours . end ( ) , std : : back_inserter ( blobContours ) ) ;
std : : copy ( newMomentss . begin ( ) , newMomentss . end ( ) , std : : back_inserter ( momentss ) ) ;
}
2012-10-17 15:12:04 +08:00
}
for ( size_t i = 0 ; i < centers . size ( ) ; i + + )
{
if ( centers [ i ] . size ( ) < params . minRepeatability )
continue ;
Point2d sumPoint ( 0 , 0 ) ;
double normalizer = 0 ;
for ( size_t j = 0 ; j < centers [ i ] . size ( ) ; j + + )
{
sumPoint + = centers [ i ] [ j ] . confidence * centers [ i ] [ j ] . location ;
normalizer + = centers [ i ] [ j ] . confidence ;
}
sumPoint * = ( 1. / normalizer ) ;
2014-02-13 00:59:04 +08:00
KeyPoint kpt ( sumPoint , ( float ) ( centers [ i ] [ centers [ i ] . size ( ) / 2 ] . radius ) * 2.0f ) ;
2012-10-17 15:12:04 +08:00
keypoints . push_back ( kpt ) ;
}
2018-01-25 20:34:12 +08:00
if ( ! mask . empty ( ) )
{
2022-10-08 00:07:51 +08:00
if ( params . collectContours )
{
KeyPointsFilter : : runByPixelsMask2VectorPoint ( keypoints , blobContours , mask . getMat ( ) ) ;
}
else
{
KeyPointsFilter : : runByPixelsMask ( keypoints , mask . getMat ( ) ) ;
}
2018-01-25 20:34:12 +08:00
}
2010-12-21 17:24:36 +08:00
}
2014-10-16 02:49:17 +08:00
2022-10-08 00:07:51 +08:00
const std : : vector < std : : vector < Point > > & SimpleBlobDetectorImpl : : getBlobContours ( ) const {
return blobContours ;
}
2014-10-16 02:49:17 +08:00
Ptr < SimpleBlobDetector > SimpleBlobDetector : : create ( const SimpleBlobDetector : : Params & params )
{
2022-12-21 21:03:00 +08:00
SimpleBlobDetectorImpl : : validateParameters ( params ) ;
2014-10-16 02:49:17 +08:00
return makePtr < SimpleBlobDetectorImpl > ( params ) ;
}
2017-09-14 12:54:05 +08:00
String SimpleBlobDetector : : getDefaultName ( ) const
{
return ( Feature2D : : getDefaultName ( ) + " .SimpleBlobDetector " ) ;
}
2014-10-16 02:49:17 +08:00
}