Calculate bubble areas in a foam picture. How to fill all holes?

Tags: #<Tag:0x00007fd541f55398> #<Tag:0x00007fd541f55168>


I am beginner of ImageJ. Currently I am calculating bubble areas in attachment. When I make image binary and fill holes, I get an Image which you can see in the second file attached. There are still a lot of bubbles(holes) needed to be filled in order to calculate their area, the reason is that some circles are disconnected. Does anyone know how to make these circles connected or other ways to calculate bubble area in the first file attached?


You want something like this, no? Or more precise edge detection, maybe. I am new to measuring bubbles.

It’s missing the elongated bubble in the top right, and there are some artefact ROIs between bubbles (38, 101). But these are easily removed.

Total surface area is 166777.


Yes, sure. That’s exactly what I want. Of course, it would be better to be more precise. Could you please tell me how you manage to do it?


I will get back to you once I’m on my computer, but the really quick work I did for this example pic:

Process >> Find edges

Process … fill holes.

Analyze particles, set size to smallest to largest bubbles. Set desired circularity of bubbles.

Then simply measure the area of this selection.

Maybe I applied some more preprocessing, I will get back to you with details.



thanks, following your steps, I still can’t get desired results and I don’t know which step I miss.


This is what i did from the macro recorder, play around with different filters and the analyze particles parameters to see if you can improve it. You can also try the trainable weka segmentation, it is well documented and you will find guides on youtube!

    run("Find Edges");
    run("Make Binary");
    run("Fill Holes");
    run("Analyze Particles...", "size=200-16000 circularity=0.28-1.00 show=Outlines display summarize add");


This is a result following your macros. What’s wrong with it?


Good day Hao Wang,

working with thresholds is difficult and most often they don’t generalize well…

For your example image you may try the following ImageJ-macro:

///////////////macro start///////////////
run(“Set Measurements…”, “area_fraction redirect=None decimal=3”);
setAutoThreshold(“Mean dark”);
run(“Analyze Particles…”, “size=200-Infinity circularity=0.30-1.00 show=Masks”);
run(“Invert LUT”);
setOption(“BlackBackground”, true);
run(“Show All”);
///////////////macro end///////////////

It gives you the following result image with a pore area fraction of 67.76%.

Evidently, there are two glitches at the bottom right and partial pores at the borders are missing.

You may adjust the Particle Size as well as the Particle Circularity.
Furthermore, you may repeat “Dilate” and if bubbles fuse, you may separate them by introducing


Good luck and be successful



I have no idea how you ended up there, can you open your macro recorder, do this again and then copy/paste the record?

plugins >> macros >> recorder


Good day Sverre Grødem

the macro was created by using the macro recorder.

If the macro does what you want, why should I paste the result of the recorder again?

If you are unclear about the processing steps, then you should consult the ImageJ manual:




I was replying to HW315, no?

You are both H… :slight_smile:


Sorry Sverre Grødem,

for my wrong interpretation. In any case it was my fault!




Thanks Herbie. I got a similar result as you suggested. One quick question: when running Analyze particles, does ImageJ find the edges of grayscale 255 or grayscale 0?


Thanks for your reply. Here is the Macro I recorded.

run(“Find Edges”);
run(“Make Binary”);
run(“Fill Holes”);
run(“Analyze Particles…”, “size=200-16000 circularity=0.28-1.00 show=Outlines display summarize add”);



“Analyze Particles…” actually does no edge finding in the sense run(“Find Edges”) does. It simply takes the binarized image produced by setAutoThreshold(“A Method”).

However, “Analyze Particles…” assumes bright (255) particles on dark (0) background.




Thanks you. In my case, gas in bubbles and liquid between bubbles are bright, bubble edges are dark. As you said, when I analysis particle size, if I don’t restrict size range, does that mean liquid between bubbles is also count as particle?

In addition, I want to count bubble area, but threshold doesn’t achieve a precise result. Do you have any suggestions? Like taking a clearer photo?


Hao Wang,

the quality of your image isn’t bad at all. I think it is taken quite carefully, but of course there is always “room” for improvements …

As I’ve written before, thresholding doesn’t generalize well.

However, you should think about an objective measure of precision first. E.g. you could draw ROIs by hand and determine the percentage bubble area plus/minus an allowed tolerance. This would help with finding appropriate approaches.

My current estimate is that the bubble area fraction is 75%±5%.




Ok Hao, I had some more time and wrote a short ad hoc script for your analysis. This utilizes the trainable weka segmentation plugin. I trained a quick example classifier for you, but I encourage you to make your own, and train it on more than one image, see how robust you can make it! You can for sure improve on the one I made in 5 minutes. Once you have your classifier you can automate this whole procedure, you can run it on files in a directory or large stacks, whatever you prefer.

Regarding making a classifier for WEKA: Video, detailed wiki.

Code in python, copy paste from here to File >> new >> script in IJ. Select language python.

Download the classifier I trained. Remember to set the path to the classifier in the script.

from ij import IJ, WindowManager
from trainableSegmentation import WekaSegmentation
import Watershed_Irregular_Features
from ij.process import ImageConverter
def bubblesizer():

    	# Please define the path to your classifier.
    	classifer_path = "D:\\Bubbles.model"
    	# Gets the image you want to segment.
    	your_image = IJ.getFilePath("Select image for WEKA segmentation")
    	target = IJ.openImage(your_image)
    	#launches weka segmentation.
    	weka = WekaSegmentation()

    	# Loads the classifier you have trained manually.

    	# Shows you the segmented image.
    	segmentation = weka.getClassifiedImage()

    	# Converts to binary, runs watershed segmentation and analyze particles. Outputs area measurements.

    	WindowManager.getCurrentImage()"Make Binary")"Invert"), "Watershed Irregular Features", "erosion=1 convexity_treshold=0 separator_size=0-200")"Analyze Particles...", "size=50-Infinity circularity=0.1-1.00 show=Outlines display summarize")


And this is the result, which I think is more accurate as the other methods created too much space between bubbles. Not all bubles are segmented but it doesn’t really matter, you want total area. By this logic maybe the watershed is redundant, up to you. Oh, and you may need to add the biovoxxel update site to get the watershed irregular features plugin.

How to count number of neuronal cells in fluorescence staining

Is it possible to have the size of every individual bubble and export the results?


Good day,

the various selections that result from “Analyze Particles…” are stored in the “Roi Manager” and you may measure them together or in isolation. If you choose “Area” and “Area fraction” in “Set Measurement…” you will get the desired values.

Beacuse this is an elementary question concerning ImageJ, I recommend to thoroughly study the User Guide: