How do i smooth the curves(contours) in OpenCV?

Adrian picture Adrian · Sep 14, 2011 · Viewed 18k times · Source

I am working on a black&white image just like the first one from the link : http://imageshack.us/g/33/firstwm.png/ It has a lot of "noise" so I applied a Median filter over it to smooth it, thus getting the second picture.

cvSmooth(TempImage, TempImage, CV_MEDIAN, 5, 0);

After this i get the contours and draw them on another image like the 3rd picture from the link. My problem is that the contours are still a little pixelated(edgy). Is there a way to smooth the B&W image even more so to obtain better contours? Or maybe do something with the contours. I have also tried Dilate and Erode with different kernels but the problem remains the same. Thank you for anything that helps.

EDIT: Also tried:

cvSmooth(TempImage, TempImage, CV_GAUSSIAN, 9, 9, 3);
cvThreshold(TempImage, TempImage, 127, 255, CV_THRESH_BINARY);

Same results as median filter, ok, but still leaves some pixelated contours.

Answer

swalog picture swalog · Sep 14, 2011

enter image description here

If this is the smoothing result you're after, it can be obtained by doing a Gaussian blur, followed by a thresholding. I.e. using cvSmooth with CV_GAUSSIAN as the paramater. Followed by a cvThreshold.

If you want a smoother transition than thresholding (like this), you can get that with adjusting levels (remapping the color range so that you preserve some of the edge transition).

update To explain how to get the smooth (anti-aliased) edge on the thresholding, consider what the thresholding does. It basically processes each pixel in the image, one at a time. If the pixel value is lower than the threshold, it is set to black (0), if not it is set to white (255).

The threshold operator is thus very simple, however, any other general mapping function can be used. Basically it's a function f(i), where i is the intensity pixel value (ranged 0-255) and f(i) is the mapped value. For threshold this function is simple

 f(i) = {   0, for i  < threshold
          255, for i >= threshold

What you have is a smoothed image (by cvSmooth using a Gaussian kernel, which gives you the "smoothest" smoothing, if that makes sense). Thus you have a soft transition of values on the edges, ranging from 0 to 255. What you want to do is make this transition much smaller, so that you get a good edge. If you go ballistic on it, you go directly from 0 to 255, which is the same as the binary thresholding you've already done.

Now, consider a function that maps, maybe a range of 4 intensity values (127 +- 4) to the full range of 0-255. I.e.

         f(i) = {   0,  for i  < 123
                  255,  for i >= 131
       linear mapping,  for 123 <= i < 131

And you get the desired output. I'll take a quick look and see if it is implemented in openCV already. Shouldn't be too hard to code it yourself though.

update 2 The contour version would be something like this:

              f(i) = { 255,  for        i < 122
   linear mapping (255->0),  for 122 <= i < 126
                         0,  for 126 <= i < 127
   linear mapping (0->255),  for 127 <= i < 131
                       255,  for 131 <= i