Path: blob/master/modules/features2d/src/blobdetector.cpp
16337 views
/*M///////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// License Agreement10// For Open Source Computer Vision Library11//12// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.13// Copyright (C) 2009, Willow Garage Inc., all rights reserved.14// Third party copyrights are property of their respective owners.15//16// Redistribution and use in source and binary forms, with or without modification,17// are permitted provided that the following conditions are met:18//19// * Redistribution's of source code must retain the above copyright notice,20// this list of conditions and the following disclaimer.21//22// * Redistribution's in binary form must reproduce the above copyright notice,23// this list of conditions and the following disclaimer in the documentation24// and/or other materials provided with the distribution.25//26// * The name of the copyright holders may not be used to endorse or promote products27// derived from this software without specific prior written permission.28//29// This software is provided by the copyright holders and contributors "as is" and30// any express or implied warranties, including, but not limited to, the implied31// warranties of merchantability and fitness for a particular purpose are disclaimed.32// In no event shall the Intel Corporation or contributors be liable for any direct,33// indirect, incidental, special, exemplary, or consequential damages34// (including, but not limited to, procurement of substitute goods or services;35// loss of use, data, or profits; or business interruption) however caused36// and on any theory of liability, whether in contract, strict liability,37// or tort (including negligence or otherwise) arising in any way out of38// the use of this software, even if advised of the possibility of such damage.39//40//M*/4142#include "precomp.hpp"43#include <iterator>44#include <limits>4546//#define DEBUG_BLOB_DETECTOR4748#ifdef DEBUG_BLOB_DETECTOR49# include "opencv2/opencv_modules.hpp"50# ifdef HAVE_OPENCV_HIGHGUI51# include "opencv2/highgui.hpp"52# else53# undef DEBUG_BLOB_DETECTOR54# endif55#endif5657namespace cv58{5960class CV_EXPORTS_W SimpleBlobDetectorImpl : public SimpleBlobDetector61{62public:6364explicit SimpleBlobDetectorImpl(const SimpleBlobDetector::Params ¶meters = SimpleBlobDetector::Params());6566virtual void read( const FileNode& fn ) CV_OVERRIDE;67virtual void write( FileStorage& fs ) const CV_OVERRIDE;6869protected:70struct CV_EXPORTS Center71{72Point2d location;73double radius;74double confidence;75};7677virtual void detect( InputArray image, std::vector<KeyPoint>& keypoints, InputArray mask=noArray() ) CV_OVERRIDE;78virtual void findBlobs(InputArray image, InputArray binaryImage, std::vector<Center> ¢ers) const;7980Params params;81};8283/*84* SimpleBlobDetector85*/86SimpleBlobDetector::Params::Params()87{88thresholdStep = 10;89minThreshold = 50;90maxThreshold = 220;91minRepeatability = 2;92minDistBetweenBlobs = 10;9394filterByColor = true;95blobColor = 0;9697filterByArea = true;98minArea = 25;99maxArea = 5000;100101filterByCircularity = false;102minCircularity = 0.8f;103maxCircularity = std::numeric_limits<float>::max();104105filterByInertia = true;106//minInertiaRatio = 0.6;107minInertiaRatio = 0.1f;108maxInertiaRatio = std::numeric_limits<float>::max();109110filterByConvexity = true;111//minConvexity = 0.8;112minConvexity = 0.95f;113maxConvexity = std::numeric_limits<float>::max();114}115116void SimpleBlobDetector::Params::read(const cv::FileNode& fn )117{118thresholdStep = fn["thresholdStep"];119minThreshold = fn["minThreshold"];120maxThreshold = fn["maxThreshold"];121122minRepeatability = (size_t)(int)fn["minRepeatability"];123minDistBetweenBlobs = fn["minDistBetweenBlobs"];124125filterByColor = (int)fn["filterByColor"] != 0 ? true : false;126blobColor = (uchar)(int)fn["blobColor"];127128filterByArea = (int)fn["filterByArea"] != 0 ? true : false;129minArea = fn["minArea"];130maxArea = fn["maxArea"];131132filterByCircularity = (int)fn["filterByCircularity"] != 0 ? true : false;133minCircularity = fn["minCircularity"];134maxCircularity = fn["maxCircularity"];135136filterByInertia = (int)fn["filterByInertia"] != 0 ? true : false;137minInertiaRatio = fn["minInertiaRatio"];138maxInertiaRatio = fn["maxInertiaRatio"];139140filterByConvexity = (int)fn["filterByConvexity"] != 0 ? true : false;141minConvexity = fn["minConvexity"];142maxConvexity = fn["maxConvexity"];143}144145void SimpleBlobDetector::Params::write(cv::FileStorage& fs) const146{147fs << "thresholdStep" << thresholdStep;148fs << "minThreshold" << minThreshold;149fs << "maxThreshold" << maxThreshold;150151fs << "minRepeatability" << (int)minRepeatability;152fs << "minDistBetweenBlobs" << minDistBetweenBlobs;153154fs << "filterByColor" << (int)filterByColor;155fs << "blobColor" << (int)blobColor;156157fs << "filterByArea" << (int)filterByArea;158fs << "minArea" << minArea;159fs << "maxArea" << maxArea;160161fs << "filterByCircularity" << (int)filterByCircularity;162fs << "minCircularity" << minCircularity;163fs << "maxCircularity" << maxCircularity;164165fs << "filterByInertia" << (int)filterByInertia;166fs << "minInertiaRatio" << minInertiaRatio;167fs << "maxInertiaRatio" << maxInertiaRatio;168169fs << "filterByConvexity" << (int)filterByConvexity;170fs << "minConvexity" << minConvexity;171fs << "maxConvexity" << maxConvexity;172}173174SimpleBlobDetectorImpl::SimpleBlobDetectorImpl(const SimpleBlobDetector::Params ¶meters) :175params(parameters)176{177}178179void SimpleBlobDetectorImpl::read( const cv::FileNode& fn )180{181params.read(fn);182}183184void SimpleBlobDetectorImpl::write( cv::FileStorage& fs ) const185{186writeFormat(fs);187params.write(fs);188}189190void SimpleBlobDetectorImpl::findBlobs(InputArray _image, InputArray _binaryImage, std::vector<Center> ¢ers) const191{192CV_INSTRUMENT_REGION();193194Mat image = _image.getMat(), binaryImage = _binaryImage.getMat();195CV_UNUSED(image);196centers.clear();197198std::vector < std::vector<Point> > contours;199Mat tmpBinaryImage = binaryImage.clone();200findContours(tmpBinaryImage, contours, RETR_LIST, CHAIN_APPROX_NONE);201202#ifdef DEBUG_BLOB_DETECTOR203// Mat keypointsImage;204// cvtColor( binaryImage, keypointsImage, CV_GRAY2RGB );205//206// Mat contoursImage;207// cvtColor( binaryImage, contoursImage, CV_GRAY2RGB );208// drawContours( contoursImage, contours, -1, Scalar(0,255,0) );209// imshow("contours", contoursImage );210#endif211212for (size_t contourIdx = 0; contourIdx < contours.size(); contourIdx++)213{214Center center;215center.confidence = 1;216Moments moms = moments(Mat(contours[contourIdx]));217if (params.filterByArea)218{219double area = moms.m00;220if (area < params.minArea || area >= params.maxArea)221continue;222}223224if (params.filterByCircularity)225{226double area = moms.m00;227double perimeter = arcLength(Mat(contours[contourIdx]), true);228double ratio = 4 * CV_PI * area / (perimeter * perimeter);229if (ratio < params.minCircularity || ratio >= params.maxCircularity)230continue;231}232233if (params.filterByInertia)234{235double denominator = std::sqrt(std::pow(2 * moms.mu11, 2) + std::pow(moms.mu20 - moms.mu02, 2));236const double eps = 1e-2;237double ratio;238if (denominator > eps)239{240double cosmin = (moms.mu20 - moms.mu02) / denominator;241double sinmin = 2 * moms.mu11 / denominator;242double cosmax = -cosmin;243double sinmax = -sinmin;244245double imin = 0.5 * (moms.mu20 + moms.mu02) - 0.5 * (moms.mu20 - moms.mu02) * cosmin - moms.mu11 * sinmin;246double imax = 0.5 * (moms.mu20 + moms.mu02) - 0.5 * (moms.mu20 - moms.mu02) * cosmax - moms.mu11 * sinmax;247ratio = imin / imax;248}249else250{251ratio = 1;252}253254if (ratio < params.minInertiaRatio || ratio >= params.maxInertiaRatio)255continue;256257center.confidence = ratio * ratio;258}259260if (params.filterByConvexity)261{262std::vector < Point > hull;263convexHull(Mat(contours[contourIdx]), hull);264double area = contourArea(Mat(contours[contourIdx]));265double hullArea = contourArea(Mat(hull));266if (fabs(hullArea) < DBL_EPSILON)267continue;268double ratio = area / hullArea;269if (ratio < params.minConvexity || ratio >= params.maxConvexity)270continue;271}272273if(moms.m00 == 0.0)274continue;275center.location = Point2d(moms.m10 / moms.m00, moms.m01 / moms.m00);276277if (params.filterByColor)278{279if (binaryImage.at<uchar> (cvRound(center.location.y), cvRound(center.location.x)) != params.blobColor)280continue;281}282283//compute blob radius284{285std::vector<double> dists;286for (size_t pointIdx = 0; pointIdx < contours[contourIdx].size(); pointIdx++)287{288Point2d pt = contours[contourIdx][pointIdx];289dists.push_back(norm(center.location - pt));290}291std::sort(dists.begin(), dists.end());292center.radius = (dists[(dists.size() - 1) / 2] + dists[dists.size() / 2]) / 2.;293}294295centers.push_back(center);296297298#ifdef DEBUG_BLOB_DETECTOR299// circle( keypointsImage, center.location, 1, Scalar(0,0,255), 1 );300#endif301}302#ifdef DEBUG_BLOB_DETECTOR303// imshow("bk", keypointsImage );304// waitKey();305#endif306}307308void SimpleBlobDetectorImpl::detect(InputArray image, std::vector<cv::KeyPoint>& keypoints, InputArray mask)309{310CV_INSTRUMENT_REGION();311312keypoints.clear();313CV_Assert(params.minRepeatability != 0);314Mat grayscaleImage;315if (image.channels() == 3 || image.channels() == 4)316cvtColor(image, grayscaleImage, COLOR_BGR2GRAY);317else318grayscaleImage = image.getMat();319320if (grayscaleImage.type() != CV_8UC1) {321CV_Error(Error::StsUnsupportedFormat, "Blob detector only supports 8-bit images!");322}323324std::vector < std::vector<Center> > centers;325for (double thresh = params.minThreshold; thresh < params.maxThreshold; thresh += params.thresholdStep)326{327Mat binarizedImage;328threshold(grayscaleImage, binarizedImage, thresh, 255, THRESH_BINARY);329330std::vector < Center > curCenters;331findBlobs(grayscaleImage, binarizedImage, curCenters);332std::vector < std::vector<Center> > newCenters;333for (size_t i = 0; i < curCenters.size(); i++)334{335bool isNew = true;336for (size_t j = 0; j < centers.size(); j++)337{338double dist = norm(centers[j][ centers[j].size() / 2 ].location - curCenters[i].location);339isNew = dist >= params.minDistBetweenBlobs && dist >= centers[j][ centers[j].size() / 2 ].radius && dist >= curCenters[i].radius;340if (!isNew)341{342centers[j].push_back(curCenters[i]);343344size_t k = centers[j].size() - 1;345while( k > 0 && centers[j][k].radius < centers[j][k-1].radius )346{347centers[j][k] = centers[j][k-1];348k--;349}350centers[j][k] = curCenters[i];351352break;353}354}355if (isNew)356newCenters.push_back(std::vector<Center> (1, curCenters[i]));357}358std::copy(newCenters.begin(), newCenters.end(), std::back_inserter(centers));359}360361for (size_t i = 0; i < centers.size(); i++)362{363if (centers[i].size() < params.minRepeatability)364continue;365Point2d sumPoint(0, 0);366double normalizer = 0;367for (size_t j = 0; j < centers[i].size(); j++)368{369sumPoint += centers[i][j].confidence * centers[i][j].location;370normalizer += centers[i][j].confidence;371}372sumPoint *= (1. / normalizer);373KeyPoint kpt(sumPoint, (float)(centers[i][centers[i].size() / 2].radius) * 2.0f);374keypoints.push_back(kpt);375}376377if (!mask.empty())378{379KeyPointsFilter::runByPixelsMask(keypoints, mask.getMat());380}381}382383Ptr<SimpleBlobDetector> SimpleBlobDetector::create(const SimpleBlobDetector::Params& params)384{385return makePtr<SimpleBlobDetectorImpl>(params);386}387388String SimpleBlobDetector::getDefaultName() const389{390return (Feature2D::getDefaultName() + ".SimpleBlobDetector");391}392393}394395396