Actually, you can use the approxPolyDP() function and approximate your contours with accuracy proportional to the contour perimeter. Here is some code:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
/// Load image
Mat src = imread( "holes.jpg");
if( !src.data )
{ return -1; }
// Show source image
imshow("src", src);
// Create binary image from source image
Mat bw;
cvtColor(src, bw, CV_BGR2GRAY);
threshold(bw, bw, 40, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
imshow("bin", bw);
// Find contours
vector<Vec4i> hierarchy;
std::vector<std::vector<cv::Point> > contours;
cv::findContours(bw.clone(), contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
// The array for storing the approximation curve
std::vector<cv::Point> approx;
// We'll put the labels in this destination image
cv::Mat dst = Mat::zeros(bw.size(), CV_8UC1);
// Loop through detected contours, and process accordingly
for (int i = 0; i < contours.size(); i++)
{
// Approximate contour with accuracy proportional
// to the contour perimeter with approxPolyDP. In this,
// the third argument is called epsilon, which is maximum
// distance from contour to approximated contour. It is
// an accuracy parameter. A wise selection of epsilon is
//needed to get the correct output.
double epsilon = cv::arcLength(cv::Mat(contours[i]), true) * 0.1; // epsilon = 10% of arc length
cv::approxPolyDP(
cv::Mat(contours[i]),
approx,
epsilon,
true
);
// Skip small or convex objects
if (std::fabs(cv::contourArea(contours[i])) < 500 || cv::isContourConvex(approx))
continue;
drawContours(dst, contours, i, Scalar(255, 255, 255), CV_FILLED, 8, hierarchy, 0, Point());
}
imshow("contours", dst);
// sum two images
dst += bw;
imshow("output", dst);
waitKey(0);
return 0;
}
of course you should play with the epsilon value in order to pick a value robust enough for all of your cases. Enjoy.
can you please post a link to the previous question? I cannot find it. If your element are like that, maybe the fitting ellipse or Hough line detection will work
actually if you could upload the source image, that might help.
Have you tried closing + opening?
Thanks for the comments. I edited the post to put a link to the previous question.
I haven't tried closing and opening this time, but my experience tells me (as I posted in the previous question) that first, closing sometimes have the undesirable effect of linking separated areas (that is why I posted the previous question) and also even when it works it would fill the insides of the triangle too.
I am thinking maybe some combination of opening closing and separating and joining elements would work, but would like to hear your ideas on this. (I am sure I am not the only one who has find himself in this situation)
what about showing us the source image, maybe there is a feature that can be used and it is not clear through the binary images that you have now
In another way you can try contour approach,
@Haris this will not work because the outermost contour will fill the whole blob and he will get the same result as in the second image. Moreover, the inner contours (i.e. the 3 ellipses-ish and the triangle-ish) are all in the same level. So, you need one more procedure to distinguish what is what.
You are not getting the 3 ellipses and the triangle? Then you shall apply THRESH_BIN_INV flag and you should think of something for filtering the triangles, like area, or length
I suggest that you can use the algorithm of Haris, and for each contour you can get the bounding-box, the minrect, apply a hough transform to get the lines. With all this information, you can do a simple decisor to verify if the region is a triangle or not.