1
votes

I'm trying to identify contours using a Canny filter in openCV and fill the contours, in order to create a mask. I've got this kind of starting image: preprocessed image

I'm trying to identify all features in my image, draw the contours, and fill them. I'm trying to implement it with this code in openCV:

img = cv2.imread(os.path.join(fName,f), 0)  
img = cv2.GaussianBlur(img,(5,5),0)
cv2.bilateralFilter(img, 9, 150,450)
edge = np.zeros((img.shape[0] + 2, img.shape[1] + 2), np.uint8)
edge = cv2.Canny(img, 500, 300, apertureSize=5)
cont, heir = cv2.findContours(edge.copy(), cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
contours = [cv2.approxPolyDP(cnt, 3, True) for cnt in cont]
cv2.morphologyEx(img, cv2.MORPH_CLOSE, np.ones((5,5), dtype='uint8'))
cv2.drawContours(img, contours, -1, 255, -1)
plt.imshow(img, cmap = 'gray', interpolation = 'bicubic')
plt.xticks([]), plt.yticks([])  # to hide tick values on X and Y axis

Mostly, I'm guessing at the parameters (trial and error; it's hard!). Unfortunately, rather than neat outlines, filled in with white, I get something more like this:image with edges detected

Are there any thoughts? Clearly, I need to close edges better, and adjust some of my Canny parameters, but I could really use some guidance.

Thanks!

EDIT: Thresholding doesn't do a very good job of creating the kind of mask that I want, either: Binary Thresholding I think that my 'bright' dots aren't sufficiently brighter than the background, but I'd like to be able to have white dots on a black background.

Code:

    img = cv2.imread(os.path.join(fName,f), 0)
    cv2.bilateralFilter(img, 9, 90,16)
    #img = cv2.GaussianBlur(img,(5,5),0)
    #binImg = np.zeros((img.shape[0], img.shape[1]), np.uint8)   
    binImg = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,           cv2.THRESH_BINARY, 25, 2)
    #binImg = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
    #newimg = np.multiply(img, np.divide(edge, 255.0))    
    plt.imshow(binImg, cmap = 'gray', interpolation = 'bicubic')
    plt.xticks([]), plt.yticks([])  # to hide tick values on X and Y axis
    #plt.show()  
4

4 Answers

2
votes

What do you mean by "identify contours" and "identify all features"? If you need just the mask of bright image parts you don't need find contours and then fill them for this kind of image you showed, just use thresholding of any kind and you will have desired binary mask.

2
votes

If you would like to make it more or less parameter independent, I would suggest the following pipeline:-

  1. Do a superpixel Segmentation . I would suggest : https://github.com/PSMM/SLIC-Superpixels
  2. For each superpixel, calculate the average brightness/intensity.
  3. Threshold (automatically or some kind of clever way) to get superpixels which are bright.
  4. Create mask accordingly
1
votes

For what it's worth, a binary mask was the better way to go. Thank you for the advice. It just meant playing with some parameters. For these features, I found that the following code:

img = cv2.imread(os.path.join(fName,f), 0)
cv2.bilateralFilter(img, 9, 90,16)
img = cv2.GaussianBlur(img,(5,5),0)
#binImg = np.zeros((img.shape[0], img.shape[1]), np.uint8)   
binImg = cv2.adaptiveThreshold(img, 1, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 55, -3)
#cv2.bilateralFilter(binImg, 9, 90,16)
#binImg = cv2.GaussianBlur(binImg, (3,3), 0)
#ret, binImg = cv2.threshold(img, 35000, 1, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
plt.imshow(binImg, cmap = 'gray', interpolation = 'bicubic')
plt.xticks([]), plt.yticks([])  # to hide tick values on X and Y axis

produces exactly the kind of mask I was looking for: Successful

So, I'm going to close the question.

0
votes

You can try floodFill(). Remember to include

#include    <opencv2/imgproc.hpp>