namespace cv
{
+// 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");
+}
+
class CV_EXPORTS_W SimpleBlobDetectorImpl : public SimpleBlobDetector
{
public:
};
virtual void detect( InputArray image, std::vector<KeyPoint>& keypoints, InputArray mask=noArray() ) CV_OVERRIDE;
- virtual void findBlobs(InputArray image, InputArray binaryImage, std::vector<Center> ¢ers) const;
+ virtual void findBlobs(InputArray image, InputArray binaryImage, std::vector<Center> ¢ers,
+ std::vector<std::vector<Point> > &contours, std::vector<Moments> &moments) const;
+ virtual const std::vector<std::vector<Point> >& getBlobContours() const CV_OVERRIDE;
Params params;
+ std::vector<std::vector<Point> > blobContours;
};
/*
//minConvexity = 0.8;
minConvexity = 0.95f;
maxConvexity = std::numeric_limits<float>::max();
+
+ collectContours = false;
}
void SimpleBlobDetector::Params::read(const cv::FileNode& fn )
filterByConvexity = (int)fn["filterByConvexity"] != 0 ? true : false;
minConvexity = fn["minConvexity"];
maxConvexity = fn["maxConvexity"];
+
+ collectContours = (int)fn["collectContours"] != 0 ? true : false;
}
void SimpleBlobDetector::Params::write(cv::FileStorage& fs) const
fs << "filterByConvexity" << (int)filterByConvexity;
fs << "minConvexity" << minConvexity;
fs << "maxConvexity" << maxConvexity;
+
+ fs << "collectContours" << (int)collectContours;
}
SimpleBlobDetectorImpl::SimpleBlobDetectorImpl(const SimpleBlobDetector::Params ¶meters) :
params.write(fs);
}
-void SimpleBlobDetectorImpl::findBlobs(InputArray _image, InputArray _binaryImage, std::vector<Center> ¢ers) const
+void SimpleBlobDetectorImpl::findBlobs(InputArray _image, InputArray _binaryImage, std::vector<Center> ¢ers,
+ std::vector<std::vector<Point> > &contoursOut, std::vector<Moments> &momentss) const
{
CV_INSTRUMENT_REGION();
Mat image = _image.getMat(), binaryImage = _binaryImage.getMat();
CV_UNUSED(image);
centers.clear();
+ contoursOut.clear();
+ momentss.clear();
std::vector < std::vector<Point> > contours;
findContours(binaryImage, contours, RETR_LIST, CHAIN_APPROX_NONE);
}
centers.push_back(center);
-
+ if (params.collectContours)
+ {
+ contoursOut.push_back(contours[contourIdx]);
+ momentss.push_back(moms);
+ }
#ifdef DEBUG_BLOB_DETECTOR
circle( keypointsImage, center.location, 1, Scalar(0,0,255), 1 );
CV_INSTRUMENT_REGION();
keypoints.clear();
+ blobContours.clear();
+
CV_Assert(params.minRepeatability != 0);
Mat grayscaleImage;
if (image.channels() == 3 || image.channels() == 4)
}
std::vector < std::vector<Center> > centers;
+ std::vector<Moments> momentss;
for (double thresh = params.minThreshold; thresh < params.maxThreshold; thresh += params.thresholdStep)
{
Mat binarizedImage;
threshold(grayscaleImage, binarizedImage, thresh, 255, THRESH_BINARY);
std::vector < Center > curCenters;
- findBlobs(grayscaleImage, binarizedImage, curCenters);
+ std::vector<std::vector<Point> > curContours;
+ std::vector<Moments> curMomentss;
+ findBlobs(grayscaleImage, binarizedImage, curCenters, curContours, curMomentss);
std::vector < std::vector<Center> > newCenters;
+ std::vector<std::vector<Point> > newContours;
+ std::vector<Moments> newMomentss;
for (size_t i = 0; i < curCenters.size(); i++)
{
bool isNew = true;
centers[j][k] = centers[j][k-1];
k--;
}
+
+ 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];
+ }
+ }
centers[j][k] = curCenters[i];
break;
}
}
if (isNew)
+ {
newCenters.push_back(std::vector<Center> (1, curCenters[i]));
+ if (params.collectContours)
+ {
+ newContours.push_back(curContours[i]);
+ newMomentss.push_back(curMomentss[i]);
+ }
+ }
}
std::copy(newCenters.begin(), newCenters.end(), std::back_inserter(centers));
+ if (params.collectContours)
+ {
+ std::copy(newContours.begin(), newContours.end(), std::back_inserter(blobContours));
+ std::copy(newMomentss.begin(), newMomentss.end(), std::back_inserter(momentss));
+ }
}
for (size_t i = 0; i < centers.size(); i++)
if (!mask.empty())
{
- KeyPointsFilter::runByPixelsMask(keypoints, mask.getMat());
+ if (params.collectContours)
+ {
+ KeyPointsFilter::runByPixelsMask2VectorPoint(keypoints, blobContours, mask.getMat());
+ }
+ else
+ {
+ KeyPointsFilter::runByPixelsMask(keypoints, mask.getMat());
+ }
}
}
+const std::vector<std::vector<Point> >& SimpleBlobDetectorImpl::getBlobContours() const {
+ return blobContours;
+}
+
Ptr<SimpleBlobDetector> SimpleBlobDetector::create(const SimpleBlobDetector::Params& params)
{
return makePtr<SimpleBlobDetectorImpl>(params);
detector->detect(image, keypoints);
ASSERT_NE((int) keypoints.size(), 0);
}
+
+TEST(Features2d_BlobDetector, withContours)
+{
+ cv::Mat image = cv::Mat(cv::Size(100, 100), CV_8UC1, cv::Scalar(255, 255, 255));
+ cv::circle(image, Point(50, 50), 20, cv::Scalar(0), -1);
+ SimpleBlobDetector::Params params;
+ params.minThreshold = 250;
+ params.maxThreshold = 260;
+ params.minRepeatability = 1; // https://github.com/opencv/opencv/issues/6667
+ params.collectContours = true;
+ std::vector<KeyPoint> keypoints;
+
+ Ptr<SimpleBlobDetector> detector = SimpleBlobDetector::create(params);
+ detector->detect(image, keypoints);
+ ASSERT_NE((int)keypoints.size(), 0);
+
+ ASSERT_GT((int)detector->getBlobContours().size(), 0);
+ std::vector<Point> contour = detector->getBlobContours()[0];
+ ASSERT_TRUE(std::any_of(contour.begin(), contour.end(),
+ [](Point p)
+ {
+ return abs(p.x - 30) < 2 && abs(p.y - 50) < 2;
+ }));
+}
}} // namespace