Network Extraction¶
A pore network reduces the pore space to a graph: pores are nodes, wide regions with a size, volume and position, and throats are edges, the constrictions that connect pairs of pores. Networks are compact (a 1000³ image becomes a few hundred thousand pores) and fast to simulate, and their pore and throat statistics are a common language for describing microstructure.
- Solves segmentation of the pore space into pores and throats
- Backends PoreSpy on CPU, with SNOW2 (watershed, default) or MAGNET (medial axis)
- Reports a network with pore and throat properties, and its pore and throat counts
- How to run it Network Extraction in the User guide
PoreStore runs PoreSpy's extraction algorithms.3 The papers below describe them in full. This page covers what PoreStore passes them and what comes back.
The problem being solved¶
The result is a graph whose nodes are pores and whose edges are throats, each with geometric properties measured on the image. SNOW2 makes each region of a watershed of the distance transform a pore, and each pair of touching regions a throat; MAGNET puts pores at the junctions and ends of the pore space's medial axis, and throats along the branches between them.
The backends¶
The default. SNOW (Sub-Network of an Over-segmented Watershed)1 splits the pore space into regions with a marker-based watershed of the distance transform:
- Compute the distance transform \(d(\mathbf{x})\) of the pore space.
- Smooth it with a Gaussian of width \(\sigma\) (Gaussian sigma) to suppress spurious peaks on rough walls.
- Find peaks with a maximum filter of radius \(r_\text{max}\) (Peak filter radius). These are candidate pore centres.
- Remove peaks on saddles and plateaus, and merge peaks that are too close together. This step corrects the watershed's tendency to over-segment.
- Flood \(-d\) from the remaining peaks. Each basin is one pore region.
Each region becomes a pore, and each pair of touching regions becomes a throat whose size comes from their shared boundary. Layers of boundary pores, \(w_b\) voxels thick (Boundary width), are added on all six faces, labelled xmin, xmax and so on, which simulations can use as inlets and outlets. PoreStore's Network Validation doesn't read these labels: it picks its own inlet and outlet pores.
| Setting | Symbol | Suggested |
|---|---|---|
| Boundary width (voxels) | \(w_b\) | 3 |
| Accuracy | Standard | |
| Gaussian sigma | \(\sigma\) | 0.4 |
| Peak filter radius (r_max) | \(r_\text{max}\) | 4 |
With Accuracy set to Standard, volumes and areas are voxel counts. High measures them on marching-cubes surfaces, which is slower but less biased (see Interfacial Surface Area).
| Pore property | Meaning |
|---|---|
pore.coords |
Centre of mass |
pore.volume, pore.region_volume |
Volume (voxel count) |
pore.equivalent_diameter |
Diameter of a sphere of the same volume, \(2(3V/4\pi)^{1/3}\) |
pore.inscribed_diameter |
Twice the largest distance to solid inside the region |
pore.extended_diameter |
Twice the largest global distance-transform value in the region's bounding box |
pore.surface_area |
Pore–solid surface area |
| Throat property | Meaning |
|---|---|
throat.conns |
The two pores it joins |
throat.inscribed_diameter |
Twice the largest distance to solid on the shared boundary |
throat.cross_sectional_area |
Area of the shared boundary |
throat.equivalent_diameter |
Diameter of a circle of that area, \(\sqrt{4A/\pi}\) |
throat.total_length, throat.direct_length |
Pore-to-throat-to-pore and straight-line lengths |
throat.perimeter |
Perimeter of the shared boundary |
The network file also holds pore.geometric_centroid (the centre of mass again), pore.local_peak and pore.global_peak (where the region's own distance transform, and the global one in its bounding box, are largest), pore.region_label (the watershed region number), pore.phase and throat.phases (the phase of each pore, and of the two pores a throat joins), throat.global_peak (where the distance transform is largest on the shared boundary), and pore.all, throat.all, param.voxel_size and param.ndim. With boundary pores, pore.boundary marks all of them and pore.xmin to pore.zmax each face. A multi-phase image adds a label for each phase, such as pore.void, pore.solid and throat.solid_void.
MAGNET (Medial Axis Guided Network Extraction Tool)2 starts from the skeleton of the pore space instead of from a watershed:
- Skeletonise the pore space to its medial axis, optionally after trimming solid pieces that float free of the rest (Trim floating surface solids).
- Junctions and end points of the skeleton become pores. Junctions closer together than the local pore size, or than \(s\) (Junction merge threshold) if it's given, are merged.
- Long skeleton branches between junctions are split with extra pores, by a maximum filter of length \(\ell_\text{max}\) (Throat max-filter length) or by fast marching (Throat junction mode).
- Optionally (Compute throat area), random walkers measure the cross-sectional area of each irregular throat, which gives a better hydraulic diameter. Throat area walkers, Throat area step size and Throat area max steps set how many walkers run, how far each step goes and how many steps each takes.
| Setting | Symbol | Suggested |
|---|---|---|
| Junction merge threshold (s) | \(s\) | chosen from the local pore size |
| Throat max-filter length (l_max) | \(\ell_\text{max}\) | 7 |
| Throat area walkers | 10 | |
| Throat area step size | 0.5 |
MAGNET preserves the topology of the pore space by construction, and is faster than SNOW on large images. Pores get pore.coords, pore.inscribed_diameter, pore.equivalent_diameter and pore.index. Throats get throat.conns, throat.actual_length, throat.area, the diameters throat.max_diameter, throat.min_diameter, throat.avg_diameter, throat.inscribed_diameter and throat.integrated_diameter, and throat.equivalent_diameter only when Compute throat area is on. A MAGNET network has no boundary-pore labels.
Units¶
Extraction runs on the voxel grid. The network file states its unit: sizes are in micrometres when the image had a voxel size at extraction, and in voxels otherwise, in which case lengths scale with the voxel size, areas with its square and volumes with its cube. The job page says which applies. See Units and voxel size.
Reproducibility¶
Extraction is deterministic: the same image and settings give the same network every time. Networks extracted before 22 October 2026 can differ slightly from a new extraction of the same image: a few pores of a SNOW2 network are split differently, and MAGNET pore centres can sit on a different voxel of equal depth, which moves a MAGNET network's permeability by up to a few percent.
Reading the result¶
- Settings set the pore count. The headline counts every pore, boundary pores included. With SNOW2, a larger \(r_\text{max}\) or \(\sigma\) gives fewer, larger pores, so compare counts only between networks extracted with the same method, settings and voxel size.
- Coordination number. The mean number of throats per pore is \(2 \times\) throats / pores, boundary pores and their throats included.
- Check it before you use it. Run Network Validation and compare its permeability and diffusivity with the image-based values.
References¶
-
J. T. Gostick, "Versatile and efficient pore network extraction method using marker-based watershed segmentation," Physical Review E 96 (2017) 023307. doi:10.1103/PhysRevE.96.023307 ↩
-
M. McKague, H. Fathiannasab, M. A. Sadeghi, J. T. Gostick, "MAGNET: Medial Axis Guided Network Extraction Tool," InterPore Journal 2 (2025) IPJ011225-7. doi:10.69631/g47x8w91 ↩
-
J. T. Gostick et al., "PoreSpy: A Python toolkit for quantitative analysis of porous media images," Journal of Open Source Software 4 (2019) 1296. doi:10.21105/joss.01296 ↩