morphology — Minkowski functionals and tensors
morphology computes topological and geometrical statistics on excursion
sets — the subsets of pixels that exceed a given intensity threshold ν.
These statistics complement the power spectrum by probing non-Gaussian
morphological features such as the shapes and connectivity of tSZ clusters.
Minkowski functionals
The three scalar functionals characterise excursion-set topology as a function of threshold ν (paper §4.1, Figure 6):
M0: area fraction — fraction of pixels above ν
M1: perimeter — total boundary length normalised by map area
M2: Euler characteristic — connected components minus holes
Requires pip install quantimpy:
from foregrounds_diffusion.morphology import compute_mfs
from foregrounds_diffusion.preprocessing import apply_maxmin_normalization
thresholds = np.linspace(0.05, 0.95, 19)
M0, M1, M2 = compute_mfs(maps_nhw, apply_maxmin_normalization, thresholds, n_jobs=-1)
# Each: (N, len(thresholds))
Minkowski tensors
compute_minkowski_tensors() is a tensorial extension that additionally
captures anisotropy and orientation per excursion set. It returns the
anisotropy index β = λ_min / λ_max ∈ [0, 1] (β = 1 is perfectly isotropic)
and major-axis orientation θ for three tensor types (W012, W200, W201).
W012 (boundary normal tensor) is recommended as the default:
from foregrounds_diffusion.morphology import compute_minkowski_tensors
results = compute_minkowski_tensors(
maps_nhw, apply_maxmin_normalization, thresholds,
tensor_types=["W012"], centred=True,
)
beta = results["W012"]["beta"] # (N, len(thresholds))
theta = results["W012"]["theta"]
See 12 — Minkowski Tensors for β(ν) curves, polar orientation histograms, and DDPM/AGORA residuals.
API
Morphological statistics: Minkowski functionals and Minkowski tensors.
This module computes two families of morphological statistics on excursion sets — the subsets of pixels above a given intensity threshold ν.
Minkowski Functionals
The three scalar Minkowski functionals characterise the topology of excursion sets (paper §4.1, Figure 6):
M0 (area fraction): fraction of pixels above threshold ν.
M1 (perimeter): total boundary length normalised by map area.
M2 (Euler characteristic): number of connected components minus number of holes, measuring the connectivity of the excursion set.
Computed by compute_mfs() using the quantimpy library
(Boelens & Tchelepi 2021). Requires pip install quantimpy.
Minkowski Tensors
Rank-2 tensorial generalisations of the functionals that additionally encode
the anisotropy and orientation of morphological features.
compute_minkowski_tensors() returns the anisotropy index
β = λ_min / λ_max ∈ [0, 1] (β = 1 is perfectly isotropic) and the
major-axis orientation θ per map per threshold, for three tensor types:
W012: boundary normal tensor via Sobel-estimated outward normals n⊗n. Probes isotropy of cluster boundary shapes. Recommended default.
W200: area inertia tensor r⊗r over interior pixels. Measures elongation of filled excursion regions.
W201: boundary position tensor r⊗r over boundary pixels.
See tutorial docs/tutorials/12_minkowski_tensors.ipynb for β(ν) curves
and polar orientation histograms comparing AGORA and DDPM maps.
Reference: Schroder-Turk et al. (2013), New J. Phys. 15 083028.
- foregrounds_diffusion.morphology.compute_mfs(maps_nhw, norm_fn, thresholds, n_jobs=1)[source]
Compute Minkowski functionals M0, M1, M2 across a stack of maps.
Requires the
quantimpypackage (Boelens & Tchelepi 2021).- Parameters:
maps_nhw (ndarray, shape (N, H, W))
norm_fn (callable) – Normalisation function mapping a 2D array to [0, 1] (e.g.
apply_maxmin_normalization()).thresholds (array_like) – Intensity thresholds ν.
n_jobs (int) – Number of parallel workers (joblib). 1 = serial (default). -1 = all cores.
- Returns:
M0, M1, M2 (ndarray, each shape (N, len(thresholds))) – M0 = area fraction, M1 = perimeter, M2 = Euler characteristic.
- foregrounds_diffusion.morphology.compute_minkowski_tensors(maps_nhw, norm_fn, thresholds, tensor_types=('W012',), centred=True, n_jobs=1)[source]
Compute Minkowski tensor anisotropy indices across intensity thresholds.
For each map and threshold ν, the map is binarised to the excursion set K = {x : T(x) > ν}. The requested rank-2 Minkowski tensor(s) are computed from K, then eigendecomposed to give the anisotropy index β = λ_min/λ_max and the major-axis orientation θ.
- Parameters:
maps_nhw (ndarray, shape (N, H, W)) – Stack of flat-sky patches.
norm_fn (callable) – Normalisation function applied to each 2D patch before thresholding (e.g.
apply_maxmin_normalization). Passlambda x: xto use raw pixel values.thresholds (array_like) – Intensity thresholds ν in the normalised range.
tensor_types (tuple of str) – Which tensor(s) to compute. Any subset of:
'W012'W^{0,2}_1 — interface normal tensor (default). Boundary normals n⊗n via Sobel gradients. Most sensitive to boundary shape.
'W200'W^{2,0}_0 — area inertia tensor. Interior pixel positions r⊗r. Measures elongation of filled regions.
'W201'W^{2,0}_1 — boundary position tensor. Boundary pixel positions r⊗r. Hybrid between W012 and W200.
centred (bool) – If True (default), subtract the excursion-set centroid from position vectors before forming the tensor. Affects W200 and W201 only.
n_jobs (int) – Number of parallel workers (joblib). 1 = serial (default). −1 = use all cores.
- Returns:
results (dict) – Keyed by tensor type string. Each value is a dict:
'beta'ndarray, shape (N, len(thresholds))Anisotropy index β = λ_min/λ_max ∈ [0, 1]. 1 = isotropic.
'theta'ndarray, shape (N, len(thresholds))Major-axis orientation in radians ∈ (-π/2, π/2].
Notes
β = 1 and θ = 0 are returned for empty or full excursion sets (fewer than 2 interior pixels or fewer than 2 exterior pixels). As a sanity check, a Gaussian random field should give β ≈ 1 at all thresholds.
Examples
>>> thresholds = np.linspace(0.05, 0.95, 50) >>> results = compute_minkowski_tensors( ... maps, apply_maxmin_normalization, thresholds, ... tensor_types=('W012', 'W200')) >>> beta_W012 = results['W012']['beta'] # shape (N, 50) >>> theta_W012 = results['W012']['theta'] # shape (N, 50)