Tendero
Tendero

Reputation: 1166

Find filled regions given its boundaries in an image

I have a binary image with boundaries, like this one:

enter image description here

I want to find a binary image that contains the filled regions corresponding to those boundaries. I did it manually on Paint, and the result should look like this:

enter image description here

I've been looking for a solution, and I just found this post but the solution provided just works if the image consists mainly of a background, which may not be necessarily my case. I tried using scipy.ndimage.binary_fill_holes but it does not work with regions that touch the boundary:

enter image description here

Is there some other way to make this work for all regions (including those that touch the boundary)?

EDIT: I don't mind which one is the filled region and which one is not. Namely, if the values are inverted, it is fine too. I just need a binary division, it doesn't matter which ones are filled and which ones are not, as long as the boundaries correctly separate the two kinds.

Upvotes: 2

Views: 758

Answers (1)

t2solve
t2solve

Reputation: 928

Since my last answer missed the point of the corner issues. I tried to find a better fitting solution: I also tried the morphological transformation like cv2.morphologyEx but the results were not so good at all in the end.

So : why not adding an artificial border to close all contours ? General workflow: a) extend image and add border b) apply some gaussian filter c) convert to greyscale d) find contours in tree hierachy structure e) use the strucuture to find the right contours

So the code looks like:

import cv2
from random import randrange

# read the image
image = cv2.imread('QFqC8m.jpeg')

#we extend the image to add a small border
borderSize = 1
image = cv2.copyMakeBorder(image,borderSize,borderSize,borderSize,borderSize,cv2.BORDER_CONSTANT,value=[255,255,255])

# convert the image to grayscale format
img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow('Binary image', img_gray)
cv2.waitKey(0)

#we apply GaussianBlur filter
image_blured = cv2.GaussianBlur(img_gray,(3,3),0)
cv2.imshow('Blured image', image_blured)
cv2.waitKey(0)

# apply binary thresholding
ret, img_thres = cv2.threshold(image_blured, 50, 200, cv2.THRESH_BINARY)
# visualize the binary image
cv2.imshow('Threshold image', img_thres)
cv2.waitKey(0)

# detect the contours on the
contours, hierarchy = cv2.findContours(image=img_thres, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE)

hierarchy = hierarchy[0]  # Remove redundant dimension of hiers.
# we iterate the hierachy
# openCV represents it as an array of four values :
# 0      1            2          3 
#[Next, Previous, First_Child, Parent]
# Find contour with the maximum area our artifical box is the largest contour
# its the outside square box
rootSquareContour = max(contours, key=cv2.contourArea)
rootSquareContourArea = cv2.contourArea(rootSquareContour)

# we do search the right first tree lvl via hand
#first lvl
root_idx = contours.index(rootSquareContour)
root_hierarchy = hierarchy[root_idx]
child_idx = root_hierarchy[2]  # Index of the first child

while child_idx != -1:    
    child_hierarchy = hierarchy[child_idx]
    #get the contour
    c = contours[child_idx]
    #we draw it with random color
    cv2.fillPoly(image,pts=[c],color=(randrange(255),randrange(255),randrange(255)))
    #get next element
    child_hierarchy = hierarchy[child_idx]
    child_idx = child_hierarchy[0]  #get next element on same lvl

    #TODO might need not all lvl ? 
    if child_idx==-1: #switch to next lvl
        child_idx = child_hierarchy[2] 
                

cv2.imshow("Filled Contours", image)
cv2.imwrite("filled.jpg", image)

cv2.waitKey(0)
cv2.destroyAllWindows()

ouput

There is still some work todo but the code explains the idea behind. A good explanation about the hierachy structure could be found here

Upvotes: -1

Related Questions