Foam bubble size distribution

Good afternoon everyone!

I will highly appreciate your advice about the calculation of bubble size distribution in foams using ImageJ.
I’ve tried to do it by adjusting threshold but obviously resultless.
I am a beginner in ImageJ so I’s a bit challenging to understand is there a solution of this case, since counting them manually seems impossible.

Thank you in advance!

The link for the picture of foam:

Hi @Yulia_Klimanova and welcome to the forum!

You could have a look at this recent post (Shameless self promotion)

There is also this post where a user has a very similar project to yours

And see what was done there… but basically you can rougly segment the bubbles with a threshold and then use the Hough transform to find patching circles of a certain size…

Seeing your images, I also wanted to know, are you interested in also finding the bubbles within the bubbles?



1 Like


Realized with MorpholibJ

run("Duplicate...", "title=1");
run("Duplicate...", "title=2");
run("Enhance Local Contrast", "blocksize=127 histogram=256 maximum=3");
run("Gaussian Blur...", "sigma=5");
setOption("BlackBackground", true);
run("Convert to Mask");
run("Set Measurements...", "area redirect=None decimal=3");
run("Analyze Particles...", "  circularity=0.10-1.00 exclude clear add");
roiManager("Show All without labels");
function morpholib()
run("Morphological Segmentation");
selectWindow("Morphological Segmentation");
call("inra.ijpb.plugins.MorphologicalSegmentation.setInputImageType", "object");
call("inra.ijpb.plugins.MorphologicalSegmentation.setGradientRadius", "1");
call("inra.ijpb.plugins.MorphologicalSegmentation.setGradientType", "Morphological");
call("inra.ijpb.plugins.MorphologicalSegmentation.setDisplayFormat", "Watershed lines");
call("inra.ijpb.plugins.MorphologicalSegmentation.segment", "tolerance=4.0", "calculateDams=true", "connectivity=4");
selectWindow("Morphological Segmentation");

Hope this can help.

1 Like

Hi Mathew! Thanks for this! Looks fantastic.
Will try to apply to all my pics and compare the deviation. Good thing is that I do not too precise measurement of all bubbles. Thus, the deviation should be enough.

Thanks again!

Hi Oli!

Thanks for your suggestion!
I did not like how works the second solution in my case, so I focused on yours.
The small thing is that in line 62 the word “original” misses “i”, but it’s not a problem at all:)
When I implement the solution I’ve got several separate images and cannot combine them in one to get result (result). The result table is empty.
Could you please suggest how it can be solved?

And regarding your question, no, I do not need to count those bubble reflections in big ones.
Thank you!

Yes, the script does not work out of the box with your data, whose properties are rather different from the ones from the previous post.

Here is a modified version that does a better job of segmenting your images.

There are still quite a few improvements that can be done, like working on a smaller version of your image. I noticed I got good results working on a 50% smaller raw image, but some parameters must be tweaked.
Here is a version that gives good starting results and that can help you see the sort of thing you can obtain.

Make sure to crop your images to remove the very strong vignetting effect from your image

// Hough Transform to find circles, 
// Author: Olivier Burri, from a request of the forum: 
// Due to the simple nature of this code, no copyright applies

#@ImagePlus image
#@Double hough_threshold (value=0.55)

//Crop edges, keep only center of image.
makeRectangle(561, 252, 1024, 1024);

// Some housekeeping
original = getTitle();

// Preprocessing make 32 bit, flatten and subtract BG
run("Median...", "radius=2");
run("Duplicate...", "title=BG");
run("Gaussian Blur...", "sigma=10");
imageCalculator("Divide create 32-bit", original,"BG");

// Apply laplacian to enhance edges
run("FeatureJ Laplacian", "compute smoothing=3");

// Auto-threshold and over-estimate particles
setAutoThreshold("Huang dark");

// Remove particles smaller than 100px2
run("Analyze Particles...", "size=100-Infinity pixel show=Masks");

// Erode sligntly to stop hough from finding too many circles

// suggestion: Speed up, use a smaller image
//run("Scale...", "x=0.5 y=0.5 width=512 height=512 interpolation=Bilinear create");

// Use a Hough transform on the mask to find likely candidates.
// These parameters must be optimized!!
run("Hough Circle Transform","minRadius=13, maxRadius=61, inc=2, minCircles=1, maxCircles=65000, threshold="+hough_threshold+", resolution=500, ratio=1.0, bandwidth=10, local_radius=10, reduce show_mask show_scores results_table");

// BUG: ImageJ does not wait for the Hough plugin to finish, so we have to check and wait until it is done
// we do this by checking the existence of the 'Score map' image
done = false;
while (!done) {
	done = isOpen("Score map");

// Merge all images for easy viewing
// Need to make all 32-bit to match score image

selectImage("Mask of Hough Laplacian");

// Give nice lookup table 
selectImage("Score map");

// Merge and display as stack
run("Merge Channels...", "c1=["+original+"] c2=[Mask of Hough Laplacian] c3=[Score map] create keep");


// Results table now contains number of circles and sizes
1 Like


Thanks a lot for that! I optimised parameters that you mentioned, now everything work perfectly.
Will apply it to all my images.

1 Like


It’s nice to receive these kinds of messages while on holidays :slightly_smiling_face:.
Glad we could help!