Segment connected regions in binary image

Hi MATLAB community
I want to extract the area of these spherical structures from my binary image but there are circles which are too close to each other and hence became connected. I tried to use watershed to separate them but it does not work good enough. Also, I have half-opened circles (the crescent like structures) which bwareaopen is not helpful to close them too. Any suggestion is welcome!

 Accepted Answer

10 Comments

Thank you, I think the tech support creates the best mask for now. bwconvhull is kind of too "harsh" on the closing, it joined many nearby blobs together after succesful watershed segmentation... I do not see any advanced parameters that I could twist with it, do you have any other suggestions?
Yes, you can use the tech support code to separate blobs. Then you can use bwconvhull to fill in any bays/incursions into the blobs. bwconvhull will not connect blobs as long as one blob does not jut into a bay of another blob, which is unlikely. Are you sure you used the "objects" option to get the convex hull of individual blobs and not 'union' which will give the convex hull of the entire set of blobs? And try using a connectivity of 4 so blobs touching only by one diagonal pixel are not considered as one blob.
Please attach the binary images of before and after calling bwconvhull so I can see the blobs you say it's connecting.
These are the intermediate results before bwconvhull
thisImage = imread(thisFile);
% Plot the original image
subplot(2, 3, 1);
imshow(thisImage)
titleLabel = sprintf("Original Image #%d", ii); % title label
title(titleLabel, 'FontSize', titlefontSize);
% Convert the RGB values to HSV values
hsv = rgb2hsv((thisImage));
% Extract the brightness value from the third dimension of the array
brightnessValue = hsv(:, :, 3);
% Plot the brightness value of the image
subplot(2, 3, 2);
imshow(brightnessValue, [])
title('Brightness Value', 'FontSize', titlefontSize);
% Convert to binary image
BW = imbinarize(brightnessValue, 'adaptive', 'Sensitivity', 0.05);
subplot(2, 3, 3);
imshow(BW, [])
title('Value Mask', 'FontSize', titlefontSize);
% Remove all small objects/connected components
P = 20; % the number of connected pixels
BW2 = bwareaopen(BW, P); % default: 8-connected
% Fill holes in the binary image --> area opening
BW3 = imfill(BW2, 'holes'); % default: 4-connected
% Plot the finalised value mask
subplot(2, 3, 4);
imshow(BW3, [])
title('Filled Value Mask', 'FontSize', titlefontSize);
% Donut segmentation by watershed to resolve connected donuts/touching
% blobs
% Checkout https://blogs.mathworks.com/steve/2013/11/19/watershed-transform-question-from-tech-support/
% Distance transform
D = -bwdist(~BW3);
% Minima imposition
% Create a new image (mask) whose catchment basins/local minima are
% the donuts for segmentation by filtering out tiny local minima
mask = imextendedmin(D, 2);
subplot(2, 3, 5);
% Superimpose the mask on the original image
imshowpair(BW3, mask, "blend")
title('Minima Imposed', 'FontSize', titlefontSize);
% Modify the distance transform so it only has minima at the desired
% locations
D2 = imimposemin(D, mask);
% The watershed ridge lines (in white) correspond to Ld == 0
% Set these ridge lines to 0 to recreate the donut boundaries/segments
Ld2 = watershed(D2);
BW4 = BW3;
BW4(Ld2 == 0) = 0;
subplot(2, 3, 6);
imshow(BW4, [])
title("Watershed Segment", 'FontSize', titlefontSize);
After applying bwconvhull
CH_objects = bwconvhull(BW4, 'objects', 4);
imshow(CH_objects);
title('Objects Convex Hull');
Now I guess probably the problem starts from the creation of the initial mask? In the first value mask (3rd subplot), that problematic circle was already fragmented, hence bwconvhull cannot join it back together.
Tell me what a good segmentation would look like. Do you want the outer bright green blobs with the inner dark circles filled in? It looks like maybe your original threshold was not optimal so the one blob was not closed on the sides. Can you attach the original green image?
It looks like some of the blobs may be 8-connected in the final segmentation but you can label them using 4-connected to avoid considering them as the same blob. If you use the defaul 8-connected it might consider them joined/merged when you don't want them to be.
If you don't want that weird shaped blob you could call bwconvhull again, then reassign the watershed lines with
BW4(Ld2 == 0) = 0;
This will split them apart again in case any of them got joined because adding pixels to the outline made them 4-connected. Then you should be able to tell bwlabel to use 4-connected algorithm to label them.
I do want to get the inner dark circles to be filled in, as I need the size/area of the whole circles. The original green image is here. I will try a second bwconvhull and update later...
It won't let me in. Please attach it here with the paperclip icon.
It won't let me upload a tif file here, so I zipped it.
Is there a minimum size? So is there a size for which you only want blobs larger than that size? Like, can we ignore small blobs? Or blobs with no holes?
Probably no, they can be of a variety of sizes, but you are right, I guess I will have to sacrifice the very small one just to eliminate the background glitches too. I do want to include bigger blobs without holes.

Sign in to comment.

More Answers (1)

Shivansh
Shivansh on 6 Sep 2024
Edited: Shivansh on 6 Sep 2024
Hi Adenine!
It seems like you are having issues in identifying the circles present in your image.
You can start by applying a Gaussian blur to reduce noise, followed by adaptive thresholding for more effective segmentation.
You can then use "imclose" to fill gaps in half-opened circles. You can also try to compute the distance transform with "bwdist" and apply a marker-controlled watershed technique. If the spherical structures are circular, the Hough Transform via "imfindcircles" can be quite useful for detecting and distinguishing these shapes.
You can refer to the below script for reference:
I = imread('5KQ-005_RGB_488.tif');
if size(I, 3) == 3
I = rgb2gray(I);
end
% Apply Gaussian blur
I_blur = imgaussfilt(I, 2);
% Convert to binary image
bw = imbinarize(I_blur, 'adaptive', 'Sensitivity', 0.5);
% Apply morphological closing to fill gaps
se = strel('disk', 5);
bw_closed = imclose(bw, se);
% Compute the distance transform
D = -bwdist(~bw_closed);
% Set background pixels to -Inf
D(~bw_closed) = -Inf;
% Apply watershed transform
L = watershed(D);
% Display the labeled image
imshow(label2rgb(L, 'jet', 'w', 'shuffle'));
You can experiment with different combinations of techniques and parameters to analyze the results for your case.
If you don't get the desired results, you can also look towards deep learning related approaches.
You can refer to the following documentation link for DL related approaches: https://www.mathworks.com/help/images/deep-learning.html.
I hope this helps in resolving your issue.

1 Comment

This is roughly how I do it in the beginning which does not work well, but the DL based approach is something worth looking at! Thanks!

Sign in to comment.

Tags

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!