# Separate touching drops in Binary image

26 views (last 30 days)
nitin arora on 17 Aug 2015
Answered: Brian Neiswander on 19 Aug 2015
I have a binary image containing touching water drops (black edges with white centers) for marking centroids and proximity analysis. For touching objects, only one centroid is detected which messes up the analysis. In order to separate the touching drops, I have tried following methods 1. watershed segmentation: Did not work. Splits the edges of drops into separate objects 2. EDM open(erode+dilate) iteration: did not work. I guess it only works if the objects are slightly touching. 3. Manual separating drops using brush with 100% hardness in Photoshop: This method works but I am trying to find an automatic method because I have to analyze 1000s of images and I want to do unbiased analysis. Please advice.

Brian Neiswander on 19 Aug 2015
The "watershed" approach should be able to handle this problem. Even though the "watershed" function splits the edges into separate objects, it should still produce a decent approximation of the drop centroid. I will describe my approach to this problem below. If this is not useful to you, please upload one of your images so I can better understand the situation.
I will start by making a sample image with three overlapping drops:
%make a sample image
N = 100;
im = zeros(N);
xCentroid = [40,60,50];
yCentroid = [40,40,70];
theta = linspace(0,2*pi,100);
[X,Y] = meshgrid(1:N);
for ai = 1:length(xCentroid)
end
The "bwdist" function is used to compute the distance transform of the inverted binary image. The "watershed" function is then used to detect the different drops:
%use watershed analysis
D = -bwdist(~im,'euclidean');
D(~im) = -inf; %set background to be infinitely far away
im2 = watershed(D);
Now, the "regionprops" function is used to detect the centroid, equivalent diameter, and perimeter of each drop:
%get the region properties
props = regionprops(im2,'centroid','equivdiameter','perimeter');
Now, the results are plotted. Note that when plotting the detected centroids, I have included a conditional statement to check if the detected drop is roughly a circle. This is used to automatically ignore other objects that are not drops.
%plot the results
figure;
imagesc(im);
hold on;
%plot the actual centroids
for ai = 1:length(xCentroid)
p(1) = plot(xCentroid(ai),yCentroid(ai),'r+','markersize',10);
end
%loop over all detected objects
for ai = 1:length(props)
%check if it is a circle
if abs(props(ai).Perimeter-pi*props(ai).EquivDiameter)/props(ai).EquivDiameter < 0.1
%plot the detected centroid
p(2) = plot(props(ai).Centroid(1),props(ai).Centroid(2),'kx','markersize',10);
end
end
legend(p,{'Actual centroid','Detected centroid'}); The error associated with each detection is as follows:
Circle 1: xCentroidError = 0.7387%, yCentroidError = 1.1352%, DiameterError = 11.39%
Circle 2: xCentroidError = 0.1190%, yCentroidError = 0.4080%, DiameterError = 7.51%
Circle 3: xCentroidError = 0.9140%, yCentroidError = 0.0000%, DiameterError = 16.89%
This method is very accurate at picking out the centroids but is less accurate in determining the drop diameter. A portion of the diameter error may be due to the single pixel outline removed from each object by the "watershed" function. To mitigate this, you can "thicken" each detected object by one pixel around its edge using the "bwmorph" function. For example, if you want to thicken the second object detected, you can use:
nextdrop = bwmorph(im2==2,'thicken');
Just as a check, you can see that this operation increases the equivalent diameter by about two pixels:
>> regionprops(im2==2,'equivdiameter') %standard
ans =
EquivDiameter: 26.5829
>> regionprops(bwmorph(im2==2,'thicken'),'equivdiameter') %thickened
ans =
EquivDiameter: 28.4790
For your reference, I have included links to some useful documentation pages below: