Ask Your Question
0

How to eliminate small contours in a binary image?

asked 2016-12-28 21:55:53 -0600

hoang anh tuan gravatar image

updated 2016-12-28 21:58:18 -0600

I am currently working on image processing project. I am using Opencv 3.x.x with VC++.

My code:

Mat result;
vector<vector<Point> >contours;
vector<Vec4i>hierarchy;
int savedContour = -1;
double maxArea = 0.0;
// Find the largest contour
findContours(binarayImage, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point());
for (int i = 0; i< contours.size(); i++)
{
    double area = contourArea(contours[i]);
    if (area > maxArea)
    {
        maxArea = area;
        savedContour = i;
    }
}
// Create mask
drawContours(result, contours, savedContour, Scalar(255), CV_FILLED, 8);

The binary image which I obtained :

image description

The result/output image which I want to obtain :

image description

but when I applied the above code, the result was

image description

did not look like I expected.

Help me! Thank you!

P/s: I know this problem is caused by drawContours. But I have no way to fix it.

edit retag flag offensive close merge delete

Comments

Hi, take the biggest contour instead.

Akhilesh gravatar imageAkhilesh ( 2016-12-28 23:27:37 -0600 )edit

Yeah. I took the biggest contour. But when I used drawContours, it looked like the last picture

hoang anh tuan gravatar imagehoang anh tuan ( 2016-12-29 00:46:14 -0600 )edit

3 answers

Sort by ยป oldest newest most voted
4

answered 2016-12-29 05:33:23 -0600

berak gravatar image

you're almost there ! all it needs is to actually apply the mask from drawContours !

(bitwise_and it with your binary image)

Mat result;
vector<vector<Point> >contours;
vector<Vec4i>hierarchy;
int savedContour = -1;
double maxArea = 0.0;
// Find the largest contour
//
//// findContours will "eat" the input image, so clone the binary Mat, we need it later:
//
findContours(binarayImage.clone(), contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point());
for (int i = 0; i< contours.size(); i++)
{
    double area = contourArea(contours[i]);
    if (area > maxArea)
    {
        maxArea = area;
        savedContour = i;
    }
}
// Create mask
drawContours(result, contours, savedContour, Scalar(255), CV_FILLED, 8);

// apply the mask:
binarayImage &= result;
edit flag offensive delete link more

Comments

I'm new to OpenCV and never used C++, I'm doing something very basic wrong as 'findcontours', 'drawContours' and 'binarayImage' have an error "this declaration has no storage class or type specifier" (I've run other OpenCV C++ test code with those functions that work). All I've done though is take your solution and plop the 'includes' and 'using namespace' at the beginning but it looks like I'm still missing something to actually make it run.

User284093 gravatar imageUser284093 ( 2020-06-19 07:22:16 -0600 )edit

@sirpuppington -- please ask your own question, show code etc. thank you !

berak gravatar imageberak ( 2020-06-19 07:24:47 -0600 )edit
1

answered 2016-12-28 22:42:13 -0600

Tetragramm gravatar image

When you find contours, you only find the boundaries, and each boundary is a separate contour. So when you eliminate the small ones, you eliminate the small contours inside the large one. You either need to explicitly keep all contours that are inside the large one (use the hierarchy output variable), or use connected components instead.

Connected Components might be closer to what you want. It finds all the blobs of white pixels. You simply filter the ones that have too small an area, and it keeps both the inner and outer boundaries.

Which one is best depends on what exactly you're doing with them, but both should work.

edit flag offensive delete link more

Comments

Tks! I'll think...

hoang anh tuan gravatar imagehoang anh tuan ( 2016-12-29 00:48:12 -0600 )edit
0

answered 2019-12-14 14:47:51 -0600

essamzaky gravatar image

updated 2019-12-15 00:56:11 -0600

I will write some other methods to find the largest contour the first method use flooFill to eliminate small regions the second method uses connected componet parameter ( RETR_CCOMP) in findcontours then pass the biggest contour to drawContours with hierarchy C:\fakepath\biggest.png

Mat gray = imread(".\\temp\\Image.jpg", IMREAD_GRAYSCALE);
gray = (gray > 200) * 255;          
Mat binarayImage = (gray > 200) * 255;  
vector<vector<Point> >contours;
vector<Vec4i>hierarchy;
int savedContour = -1;
double maxArea = 0.0;   
Mat result1 = binarayImage.clone();
// Find the largest contour
// findContours will "eat" the input image, so clone the binary Mat, we need it later:  
Rect rect;
findContours(binarayImage.clone(), contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_NONE, Point());
for (int i = 0; i < contours.size(); i++)
{
    //ignore holes the following condition for  
    if (hierarchy[i][3] > -1)
        continue;
    double area = contourArea(contours[i]);
    if (area > maxArea)
    {
        if (savedContour >= 0)
            floodFill(result1, contours[savedContour][0], Scalar(0), &rect, 0, 0, 8);
        maxArea = area;
        savedContour = i;
    }
    else
        floodFill(result1, contours[i][0], Scalar(0), &rect, 0, 0, 8);
}

imwrite("c:\\temp\\biggest1.png", result1);//save method 1  
Mat result2 = Mat::zeros(binarayImage.size(), CV_8U);
drawContours(result2, contours, savedContour, Scalar(255), FILLED, 8,hierarchy);
imwrite("c:\\temp\\biggest2.png", result2);//save method 2
edit flag offensive delete link more

Question Tools

1 follower

Stats

Asked: 2016-12-28 21:55:53 -0600

Seen: 18,221 times

Last updated: Dec 15 '19