Reputation: 1166
I have a binary image with boundaries, like this one:
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:
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:
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
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()
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