Search code examples
pythonimage-processingpolygonmask

How to create a closed region around cluster of pixels?


I am doing some image processing in Python 3.5.2. After some work I have segmented and image using Support Vector Machines (used as pixel-wise classification task). As expected after training, when I try to predict a new image I get some pixels misslabeled. I only have to classes for the segmentation so the result will work as a mask with 1 in the desired region and 0 elsewhere.

An example predicted mask looks like this:

one

EDIT: Here is the link for this image (saved using cv2.imwrite()):

https://i.ibb.co/74nxLvZ/img.jpg


As you can see there is a big region with some holes in it that means they are False Negative (FN) pixel predictions. Also, there are some False Positive (FP) pixels outside that big region.

I want to be able to get a mask for that big region alone and filled. Therefore I've been thinking about using some clustering method like DBSCAN or K-means to create clusters on this data points hopefully getting a cluster for the big region. Do you have any suggestion on the matter?

Now, assume I have that clusters. How can I fill the holes in tha big region. I would want to create some sort of figure/polygon/roi around that big region and then get all the pixels inside. Can any one shed some light on how to achieve this?

Somehow I would want something like this:

two

Hope I made myself clear. If I wasn´t let me know on the comments. Hope someone can help me figure this out.

Thanks in advance


Solution

  • You can in fact use DBSCAN to cluster data points. Specially when you don't know the number of clusters you are trying to get.

    Then, you can get the contour of the region you want to fill. In this case the big white region with holes.

    # im_gray: is the binary image you have
    cnt, _ = cv2.findContours(im_gray, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_NONE)
    

    You can loop through cnt to select the correct contour. Then, assuming you "know" the contour you want, you can use the function cv2.approxPolyDP() from OpenCV

    taken from OpenCV tutorial

    It approximates a contour shape to another shape with less number of vertices depending upon the precision we specify. It is an implementation of Douglas-Peucker algorithm.

    epsilon = 0.001
    approxPoly= cv2.approxPolyDP(np.array(maxPoly), epsilon, closed=True)
    

    epsilon is an accuracy parameter, it is the maximum distance from the contour to approximated contour. As suggested in the documentation (link above) you can use epsilon=0.1*cv2.arcLength(cnt,True). In this case I used value 0.001.

    Once you have this, you can just draw it:

    poligon_mask = np.zeros(im_gray.shape)
    poligon_mask = cv2.drawContours(max_poligon_mask, [approxPoly], cv2.FILLED, (255), -1)
    

    Hope this helps.