Defining and filling holes on the border of an image
Blog reader Alex asked a question this week about filling holes in this binary image.
url = 'https://blogs.mathworks.com/images/steve/2013/eye-heart-matlab.png'; bw = imread(url); imshow(bw)
Alex tried imfill but it didn't give him exactly what he was looking for.
bw2 = imfill(bw,'holes'); imshow(bw2) title('Filling holes (first attempt)')
Alex asked how he could fill the objects that are touching the border. At this point, MathWorker Sean de Wolski jumped in and pointed out the problem wasn't well defined; there are multiple interpretations and possible solutions.
It might not be obvious from Alex's image that this is the case, so let me illustrate with a different example. This image has one object, and it touches the border in a way that divides the background into two regions. Topologically, this object is the same as each of the five "unfilled" objects in Alex's image.
bw_eye = imdilate(eye(200),ones(5,5)); imshow(bw_eye)
imfill doesn't do anything to this image:
imshow(imfill(bw_eye,'holes')) title('Output of imfill')
What does it mean to "fill" this object? Would you expect the upper-right portion of the background to be filled, or the lower-right? This is the point Sean was making.
Well, I stared at Alex's image for a while, trying to come up with a formulation of the problem that would be better defined and that would do what Alex expects.
I think I've hit upon a reasonable formulation. It's not the only one, however, and I'd be interested in what ideas other readers have about this.
Here's my idea: Fill objects against two adjacent borders at a time. By "fill against a border", I mean pad that border with white pixels, do the fill, and then remove the padding.
Let's go back to Alex's image and "fill against" the left and the top border. The following call to padarray adds a column of white pixels on the left and a row of white pixels on the top.
bw_a = padarray(bw,[1 1],1,'pre'); bw_a_filled = imfill(bw_a,'holes'); bw_a_filled = bw_a_filled(2:end,2:end); imshow(bw_a_filled)
Now fill against the top and the right border.
bw_b = padarray(padarray(bw,[1 0],1,'pre'),[0 1],1,'post'); bw_b_filled = imfill(bw_b,'holes'); bw_b_filled = bw_b_filled(2:end,1:end-1); imshow(bw_b_filled);
Next, fill against the right and bottom borders.
bw_c = padarray(bw,[1 1],1,'post'); bw_c_filled = imfill(bw_c,'holes'); bw_c_filled = bw_c_filled(1:end-1,1:end-1); imshow(bw_c_filled)
Finally, fill against the bottom and left borders. (I wouldn't expect this to have any effect on this particular image.)
bw_d = padarray(padarray(bw,[1 0],1,'post'),[0 1],1,'pre'); bw_d_filled = imfill(bw_d,'holes'); bw_d_filled = bw_d_filled(1:end-1,2:end); imshow(bw_d_filled)
The last step is then to "logical OR" all these images together.
bw_filled = bw_a_filled | bw_b_filled | bw_c_filled | bw_d_filled; imshow(bw_filled) title('Final filled result')
Readers, please post your thoughts about this problem. It wouldn't surprise me at all to learn of other useful ways to think about it and other implementation methods.
Alex, I hope you find this useful.
To leave a comment, please click here to sign in to your MathWorks Account or create a new one.