11

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.

Adrian
  • 2,028
  • 2
  • 17
  • 28
  • 1
    Passing it through a Gaussian kernel, and then thresholding it should smoothen out the edges. Perhaps this is the result you want? – swalog Sep 14 '11 at 12:26
  • I'm making the assumption that the "noise" you are talking about is the high frequency components you seemed to have removed in your second example. Is [this](http://i.imgur.com/33GIT.png) a desired output? If only binary output is desired, a [hard threshold](http://i.imgur.com/VelRg.png) would be the closest thing. – swalog Sep 14 '11 at 12:56
  • Yes, the desired output is to remove the noise as to have as smooth contours as possible, i have tried Gaussian + threshold also at your suggestion but getting same results as median filter. – Adrian Sep 14 '11 at 13:01
  • Perhaps increasing the Gaussian kernel size removes the remaining pixelated contours? If you desire a anti-aliased edge, then you should do the remapping I suggested. – swalog Sep 14 '11 at 13:11
  • Can you explain more how to do this remapping or maybe give an example? Anti-aliased edges is exactly what i need, thats what i meant by smooth :) – Adrian Sep 14 '11 at 13:15

1 Answers1

8

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
swalog
  • 4,403
  • 3
  • 32
  • 60
  • Oh, I understand now what you're saying, for that I could use cvLUT instead of cvThreshold. But now that I think about it after applying the cvFindContours to get the contours I'll be back to the pixelated contour :(. Thanks for the response anyway, if in a few days no better answer pops up, I'll mark yours as correct. – Adrian Sep 14 '11 at 13:59
  • Consider that you do have the smooth gradient edge after the smoothing operation. It should still be possible to get a nice looking contour image using cvLUT and the correct table values. In short it would have three ranges, from white to black (start of line) from black to black (inside the line) and from black to white (end of line). If follows the same structure as above. All values outside this are also mapped to white. Thus you end up with a nice looking contour :) – swalog Sep 14 '11 at 14:02
  • See update 2 in the answer. I'm also just guessing good values, so you might want to play around with it :) – swalog Sep 14 '11 at 14:13