Tutorial: Measure particle size distribution via SEM
Note
Particle size measurements are not available in the AutoEMX GUI yet: use the script described here.
This tutorial explains how to collect particle-size statistics with
AutoEMX using the script:
autoemx/scripts/Particle size Statistics measurements/Collect_Particle_Statistics.py
It also explains how to post-process results and, for advanced users, how to add a separate particle-segmentation model.
What this script does
Collect_Particle_Statistics.py:
Acquires SEM frames across the selected sample region.
Segments particles in each frame.
Filters particles by size.
Continues until the requested number of particles is reached (or frames are exhausted).
Saves per-particle measurements and summary statistics.
Saves a particle-size histogram.
Step 1 - Open script to edit
Open:
autoemx/scripts/Particle size Statistics measurements/Collect_Particle_Statistics.py
When you run it, a GUI appears to collect the main inputs.
Step 2 - Define required inputs in the GUI
Fill in the following fields:
Sample IDName used to create the output folder and output files.
Sample Center X, YStage coordinates (mm) of the center of the sample.
Number of Particles to AnalyseTarget number of particles to analyse.
Min & Max Particle Diameter (um)Accepted particle-diameter range (used to compute area filters). The range is split automatically into size bands (largest first), each searched with frames matched to its particle sizes, and the number of particles to analyse is collected in each band. The statistics are corrected so that they describe the sample (weighting of the bands, particles cut by the frame edges), and reported with their confidence intervals, number- and volume-weighted: see Particle size statistics. Coarse frames use normal labels (for example
A0); finer bands scan sub-frames inside visited parent FOVs with hierarchical ids such asA0_a0. Results are stored inledger.json, and saved as Par_sizes / Par_size_stats CSV files and a size distribution plot. An interrupted measurement is resumed by running it again with the same settings. Every frame image is saved, so that the measurement can be reanalysed with other segmentation settings (Reanalyse_Particle_Stats.py).
Carbon Tape Diameter (mm)Used to estimate the effective sample area to scan over the carbon tape.
Working Distance (mm)Nominal working distance used by autofocus to prevent catastrophic focus drift.
Segmentation ModelParticle segmentation model. Default is
threshold_bright. This default model works best when there is clear contrast between carbon tape and particles. For this reason, electron backscatter detector mode is recommended to maximize Z-contrast. Particles should also be well separated; touching/overlapping particles can be segmented as a single particle. For more complex samples, machine-learning segmentation models are recommended:SAM2andmicroSAMsegment touching particles separately (optional installs, see Particle size statistics). You can also add your own segmentation model (see the advanced section below).
Manual NavigationIf enabled, you manually navigate to regions/particles and determine the frame size.
Auto Detect Carbon TapeEnables automatic substrate detection for carbon tape workflows.
Auto Adjust Brightness and ContrastIf set to
No, you must also provide:BrightnessContrast
After submitting, choose results_dir (save directory) in the folder picker.
Step 3 - Run the script
Note that the measurement runs using the microscope’s current settings, including detector type, beam current, and acceleration voltage.
Run from your AutoEMX environment, for example:
python "autoemx/scripts/Particle size Statistics measurements/Collect_Particle_Statistics.py"
Output
In the sample output directory, the workflow writes:
ledger.jsonEverything recorded by the measurement: the frames acquired with the particles found in them, the segmentation settings, the size bands, and the statistics (see Particle size statistics for its contents). An interrupted measurement resumes when the script is run again with the same settings.
<SampleID>_Par_sizes.csvOne row per particle: frame ID, particle number in the frame, area, equivalent diameter, and its
Weightin the statistics (relative, 1 on average).
<SampleID>_Par_size_stats.csvStatistics of the equivalent diameters, weighted to describe the sample: number-weighted (mean, standard deviation, min, max, D10, D25, D50, D75, D90), volume-weighted (D43, Dv10, Dv50, Dv90), and the 95 % confidence intervals (
<name>_CI95_low,<name>_CI95_high). See Reported statistics.
<SampleID>_Par_size_distribution_hist.pngPlot of the weighted size distribution, with cumulative number and volume curves and D10, D50, D90 marked (format set by
size_stats_plot_format).
SEM images/Every frame analysed, as
<SampleID>_fr<FrameID>_particles.pngwith its particle mask_par_mask.png, saved without annotations so that it can be segmented again (Reanalyse_Particle_Stats.py). Annotated copies can be made withAnnotate_Particle_Images.py.
particle_outlines/Outline of each particle found, per segmentation and frame (shown in the GUI).
Optional: Reprocess particle statistics after filtering
If some particles or full frames were incorrectly segmented and you want to exclude them before recomputing the size distribution, use:
autoemx/scripts/Particle size Statistics measurements/Process_Particle_Stats_Files.py
Main editable fields in that script are:
sample_ID: sample folder to processresults_path: parent path where sample folder is storedshow_results_only:Trueto only list the statistics results of the sample (one per segmentation, e.g. per segmentation model) and choose oneresult_id: the result to recalculate (default: the active one)particles_to_filter: particles to ignore, as(Frame ID, Particle #)(columns of<SampleID>_Par_sizes.csv)frame_IDs_to_filter: list of frame IDs to ignore (if a full frame is bad, all its particles are ignored)particles_IDs_to_filter: particle IDs to ignore, for samples measured before AutoEMX stored particle sizes in the ledger
The same can be done in the GUI (Analysis > Particle sizes): choose the result, then click the dot at the centre of a particle in the frame browser, or tick “Leave this frame out”, and “Recalculate and save” (see Tutorial: particle size statistics in the GUI).
The script generates processed outputs with suffix _processed.
Optional (Advanced): Add a separate segmentation model
Most users do not need this section. Use it only if the built-in segmentation models are not suitable for your images.
AutoEMX automatically discovers custom segmentation models from:
autoemx/core/em_runtime/particle_segmentation_models/
To add a new model:
- Create a new Python file in that folder, for example:
my_segmentation_model.py
- Use
autoemx/core/em_runtime/particle_segmentation_models/segmentation_model_template.pyas the template.
Implement a function with this signature:
def segment_particles(frame_image, powder_meas_config=None, save_image=False, EM=None):
...
return par_mask
Return either:
a binary mask (background 0, particles > 0), or
a labeled image (background 0, each particle with a unique positive label).
- Restart the particle-statistics script. Your model name should appear in the
Segmentation Modeldropdown automatically.
- Select it in the GUI (or set
par_segmentation_modeldirectly in powder_meas_cfg_kwargs).
- Select it in the GUI (or set
Notes:
- If your model needs extra packages (e.g. torch), import them inside
segment_particlesand defineis_available()andINSTALL_HINT(seeSAM2.py): the model is then only offered where installed.
- If your model requires external files (for example ONNX weights), keep paths
stable and preferably relative to the model file location.
If a model name is invalid, AutoEMX falls back to
threshold_bright.