Ask Your Question
2

remove small object faster

asked 2017-01-31 11:38:58 -0600

BenNG gravatar image

Hello all !

In my project I have a function that find contour within an image and if this contour is too small then the contour is set to the color of the background in other words I remove it. here is the function

Mat removeTinyVolume(Mat input, int area, Scalar color)
{
// we draw to the color of the background
Mat output = input.clone();
vector<vector<Point>> contours;
findContours(input, contours, RETR_LIST, CV_CHAIN_APPROX_SIMPLE);

// cout << "contours : " << contours.size() << endl;

for (int i = 0; i < contours.size(); i++)
{
    if (contourArea(contours[i]) < area)
    {
        drawContours(output, contours, i, color, -1, 8);
    }
}
return output;
}

It accurate I'm happy but it is really slow so I was wondering if you guys have some tips&ticks for that ? Thank you a lot ;) By the way this function is part of an open source project that you can find in comments.

edit retag flag offensive close merge delete

Comments

1
BenNG gravatar imageBenNG ( 2017-01-31 11:39:51 -0600 )edit

1 answer

Sort by ยป oldest newest most voted
3

answered 2017-01-31 11:46:39 -0600

updated 2017-02-01 05:40:16 -0600

No wonder it is slow, if every time you need to remove a contour you need to find ALL contours and verify which should be removed, it will be really slow.

Probably, somewhere in your code you already calculated the contours,it makes no sense that a function for such a simple operation needs to find ALL contours of the image. So, the arguments that this function take in should be the already calculated contours that you are probably using somewhere else in the code.

If this is in fact the only operation where you need to use findContours, then there is small space for optimization. Still, there are things that can be done:

  1. Do not use .clone(). In that case it doesn't matter if findContours changes the original image, since you do not use it for anything else. Creating a hard copy takes longer that using the one that already exists.
  2. Instead of using drawContours() to remove the item, see if you can instead draw a rectangle on the contour's bounding box. Drawing a rectangle is many times faster that drawing a contour, and for many applications it wont cause any problems in the functionality.

Replace the line drawContours(output, contours, i, color, -1, 8); with

rectangle(output, cv::boundingRect(contours[i]), color, -1, 8);

Cheers.

edit flag offensive delete link more

Comments

Thank you a lot for your answer ! I had to think a lot on how I preprocess my picture. Unfortunatly I think it is not possible to "reuse" the result of findContour because.
- I preprocess the original picture to extract the puzzle
- I extract the cell on the puzzle
- I preprocess the cell to extract the number

I realize that I never apply findContour on the same image or part of image so unfortunately for me I can't apply what you advise me :( and unfortunately for me again

rectangle(output, cv::boundingRect(contours[i]), color, -1, 8);

breaks the behavior of my app so zero optimization for me today !!

But thanks because it makes me think about my process again.

BenNG gravatar imageBenNG ( 2017-02-01 07:42:03 -0600 )edit

@Pedro Batista drawing a rectangle will covers those contours that falls into the area between original contour and its bounding rect

pklab gravatar imagepklab ( 2017-02-01 12:52:58 -0600 )edit

@BenNG Is it so relevant the area ? Instead of if(contourArea(contours[i]) < area) you can try if(contours[i].size()) < lenght)... less accurate but really faster

pklab gravatar imagepklab ( 2017-02-01 13:00:30 -0600 )edit

Hi. I stuck with contourArea(contours[i]) unfortunately ...

BenNG gravatar imageBenNG ( 2017-02-08 04:33:52 -0600 )edit

and what if(contours[i].size()) < lenght) ?

pklab gravatar imagepklab ( 2017-02-08 11:00:30 -0600 )edit

Yes I tried this one but it breaks my behavior. I'm stuck with contourArea(contours[i]) unfortunately ...

BenNG gravatar imageBenNG ( 2017-02-09 09:43:59 -0600 )edit

contours[i].size() can be seen as the perimeter of the contour.

contourArea(contours[i]) can be seen as the total number of pixels in the contour minus the perimeter. If you can change your thresholds to work with perimeter instead of area, it should work.

Pedro Batista gravatar imagePedro Batista ( 2017-02-09 09:54:06 -0600 )edit

contours[i].size() is not exactly the perimeter (or arcLength) in special case of CV_CHAIN_APPROX_SIMPLE. For example, a contour of:

  • filled circle r=10 has size()=36, Area=288, Perimeter=65
  • filled square 20x20, no rotation, has size()=4, Area=400 and Perimeter=80
  • line 20x1 has size()=2, Area=0 and Perimeter=20 (or 40 if the contour is considered closed around the object)
pklab gravatar imagepklab ( 2017-02-09 11:07:56 -0600 )edit
pklab gravatar imagepklab ( 2017-02-19 11:38:35 -0600 )edit

great to see that ! thank you for pointing this to me ! I finished my project but I will test it again 3.2 and see how it goes !! I hope it's not going to be painful !!! thanks again :)

BenNG gravatar imageBenNG ( 2017-02-20 02:16:01 -0600 )edit

Question Tools

1 follower

Stats

Asked: 2017-01-31 11:38:58 -0600

Seen: 2,213 times

Last updated: Feb 01 '17