Prior-guided tissue segmentation with a Gaussian mixture and an MRF
Source: R/imaging-segment-gmm.R
segment_volume_tissue_gmm.RdSegments a 3D intensity volume into K tissue classes with a finite
Gaussian mixture model, optional spatial prior probability maps, and
a mean-field Markov random field (MRF) that favors
spatially coherent labels. This is a self-contained re-implementation of
the Atropos algorithm of 'ANTs' (Avants and
colleagues, 2011) in Rcpp; the defaults reproduce the
'ANTsPy' atropos
call i = 'Kmeans[3]', m = '[0.2,1x1x1]', c = '[5,0]',
priorweight = 0.25.
Usage
segment_volume_tissue_gmm(
volume,
mask = NULL,
priors = NULL,
n_classes = 3L,
prior_weight = 0.25,
mrf_beta = 0.2,
mrf_radius = 1L,
iterations = 5L,
tolerance = 0,
vox2ras = NULL,
verbose = FALSE
)Arguments
- volume
a 3D numeric (or integer, logical) array of intensities; a
"vox2ras"attribute, if present, is used whenvox2rasisNULL. Trailing singleton dimensions (for example aNIfTIvolume read asnx x ny x nz x 1) are dropped fromvolume,maskandpriors, and the outputs are always 3D. Extreme outlier intensities inside the mask (hot voxels, rawCTvalues) should be truncated or clipped beforehand, since thek-meansseeds are spread over the intensity range (see 'Initialization'). The result also depends on the absolute intensity scale, because theGaussiandensity is floored at1e-10as in'ANTs'(see 'Details'): intensities in very large units (class standard deviations beyond roughly1e7) should be rescaled first- mask
optional 3D array of the same dimensions as
volume; non-zero (and non-NA) voxels are segmented, everything else receives label0and zero posteriors. The defaultNULLuses every voxel with a finite intensity. Voxels with non-finite intensities are always excluded from the mask- priors
optional list of
Knon-negative 3D arrays on the grid ofvolume, the spatial prior probability of each class (for example tissue probability maps warped from a template withapply_transform3d_volume). They are normalized per voxel; at an isolated voxel where all priors are zero a uniform prior is used, but priors that are zero at every voxel inside the mask (for example integer-truncated probability maps, or maps warped onto the wrong grid) are an error, as is a prior that is zero everywhere inside the mask. The number of classes is the number of priors, and the output classes follow the order of the list. Values outside the mask are ignored- n_classes
number of classes for the
k-meansinitialization (default3, the classicCSF, gray matter, white matter); ignored whenpriorsare given, unless it is passed explicitly and conflicts with the number of priors, which is an error- prior_weight
weight \(w \in [0, 1]\) of the spatial priors in the posterior (default
0.25), with the meaning of thepriorweightof'ANTs';0uses the priors only to initialize the model,1ignores the image and segments by the priors alone (see 'Details'). Ignored withoutpriors- mrf_beta
MRFsmoothing factor \(\beta \ge 0\) (default0.2); larger values produce smoother label maps,0disables theMRF- mrf_radius
MRFneighborhood radius in voxels, either one non-negative integer for all axes or one per axis (default1, the 26-connected neighborhood); non-integer values are an error and0disables theMRF. A radius beyond the grid extent is equivalent to the extent along that axis and is clamped to it- iterations
maximum number of
EMiterations (default5); the iterations usually stop earlier, seetolerance- tolerance
non-negative convergence threshold on the mean maximum posterior probability over the mask (default
0), with the meaning of the convergence threshold of'ANTs'Atropos: after each iteration (except the first) the change of this value from the previous iteration is compared withtolerance, and the iterations stop as soon as the change is below it. With the default0the iterations stop as soon as the value decreases (asAtroposdoes withc = [n,0]), otherwise they run up toiterations; the posteriors and labels of the last iteration performed are returned (see 'Details'). Negative values are an error- vox2ras
optional 4x4 (or 3x4) matrix mapping 0-indexed voxel coordinates to
RAS; only its \(3\times 3\) part matters, to express theMRFneighbor weights in physical distance. DefaultNULLlooks for a"vox2ras"attribute onvolumeand otherwise uses voxel units (unit spacing)- verbose
logical; print per-iteration progress (default
FALSE)
Value
A list with
segmentationinteger 3D array,
0outside the mask and1..Kinside, theargmaxof the posteriorsposteriorslist of
Kdouble 3D arrays with the posterior probability of each class (summing to one inside the mask,0outside)means,sds,proportionsthe final class intensity means, standard deviations and mixture proportions, estimated from the returned posteriors
tracethe mean maximum posterior probability over the mask after each iteration performed (one entry per iteration, so its length is the number of iterations run; see
tolerance)
The segmentation and each posterior carry the "vox2ras"
attribute when one is known.
Details
Model. For a voxel \(i\) inside the mask with intensity
\(y_i\), the posterior of class \(k\) follows the default
(Socrates) posterior formulation of Atropos,
$$P_i(k) \propto s_k(i)^{w}\,\left(N(y_i \mid \mu_k, \sigma_k^2)\,
M_k(i)\right)^{1-w},$$
where \(w\) is prior_weight,
$$s_k(i) = \frac{\pi_k\, p_k(i)}{\sum_c \pi_c\, p_c(i)}$$
is the spatial prior (the per-voxel normalized prior \(p_k(i)\)
re-weighted by the mixture proportions \(\pi_k\)) and
$$M_k(i) = \frac{\exp\left(\beta \sum_j \omega_{ij} P_j(k)\right)}{%
\sum_c \exp\left(\beta \sum_j \omega_{ij} P_j(c)\right)}$$
is the MRF term, with \(\beta\) equal to mrf_beta,
the sums over the \((2r+1)^3 - 1\) neighbors \(j\) inside the mask, their
posteriors \(P_j(k)\) and the inverse physical distance
\(\omega_{ij} = 1 / d_{ij}\) between the two voxels.
As in 'ANTs', \(s_k\), \(M_k\) and the Gaussian density
are each floored at 1e-10 before the powers are taken, so a zero
prior is a very strong but not absolute constraint. Consequently, with
prior_weight = 0 the priors only initialize the model; with
prior_weight = 1 the image and the MRF are ignored and the
segmentation is \(\arg\max_k \pi_k p_k(i)\); and
without priors the spatial prior is the same constant for every
class (Atropos applies no prior weight then), so the posterior is
proportional to \(N(y_i \mid \mu_k, \sigma_k^2)\, M_k(i)\) and the mixture proportions do not enter it. Each
iteration performs one synchronous E-step (every voxel reads the
posteriors of the previous iteration, initially the one-hot initial
labels) followed by an M-step that re-estimates \(\mu_k\) and
\(\sigma_k\) (posterior-weighted mean and unbiased weighted
variance, the ITK estimators used by Atropos, with a small
variance floor relative to the intensity variance inside the mask) and
\(\pi_k\) (the mean posterior over the mask).
Intensity scale. Because the floor of 1e-10 is applied to
the (unnormalized) Gaussian density, whose peak is
\(1 / (\sqrt{2\pi}\,\sigma_k)\), the result is
not invariant to the intensity scale, exactly as in Atropos: once a
class standard deviation exceeds roughly 1e7 the floor starts to
flatten its likelihood, and beyond roughly 1e9 the EM
inflates the variances until every likelihood is floored and the
segmentation collapses to a single class. Intensities in such units
should be rescaled (or truncated) before segmenting; the usual MRI
and CT ranges are unaffected.
Convergence. After each iteration the mean maximum posterior
probability over the mask (the returned trace) is compared with the
previous iteration's value, and the iterations stop as soon as the signed
change is below tolerance; the first iteration never stops. This is
the stopping rule of Atropos, so with the default
tolerance = 0 the iterations stop after the first iteration in
which this value decreased, and the posteriors, labels and parameters
of that iteration are returned (there is no rollback to the previous
iteration). The value itself equals the posterior probability that
Atropos reports only without priors or with
prior_weight = 0, and with mrf_beta = 0: Atropos
computes its measure during its iterated-conditional-modes sweep, with the
neighbors' hard labels and the normalized prior \(p_k(i)\) itself (not
re-weighted by the proportions) instead of \(s_k(i)\), so with weighted
priors or an active MRF the two measures, and hence the iteration
at which the two implementations stop, can differ.
Initialization. Without priors, a deterministic
k-means (Lloyd iterations seeded at equally spaced intensities
between the minimum and the maximum inside the mask, as in 'ANTs')
clusters the masked intensities and the classes are ordered by ascending
mean, so with three classes label 1 is the darkest tissue. A
handful of extreme intensities stretches that range so much that a seed
can end up without any voxel, which is reported as an error naming the
intensity range: truncate or clip the intensities first (the intensity
truncation of bias_correction_n4 does this), tighten
the mask, or reduce n_classes. With priors, each voxel
starts at the class with the largest prior (voxels where every prior is
zero start unlabeled), the initial class statistics are weighted by that
prior value and the initial proportions are the label fractions; the
class order is the order of the list.
Relation to 'ANTs' Atropos. The posterior formula
above, the Gaussian likelihood, the k-means initialization,
the inverse-distance neighborhood weights, the probability floor, the
parameter estimators and the stopping rule are those of Atropos
with its defaults (the Socrates formulation with mixture
proportions and no annealing), so prior_weight is interchangeable
with its priorweight. The remaining difference is the MRF
term: Atropos plugs the neighbors' hard labels (updated by an
iterated-conditional-modes sweep) into \(M_k(i)\), whereas here the
neighbors' posteriors are used (a mean-field update), so the two can
differ along tissue boundaries when mrf_beta > 0; with
mrf_beta = 0 the same model is computed, although the convergence
measure (see above) and therefore the number of iterations can still
differ when prior_weight > 0. The posteriors returned here are the
ones the labels were taken from, whereas Atropos re-evaluates the
probability images it writes out with the parameters updated in its last
iteration. Two safeguards also differ: a class that receives no voxel at
initialization falls back to prior-weighted statistics instead of a zero
likelihood, and priors that are zero everywhere inside the mask are an
error.
Memory. All computation is restricted to the mask; the working
memory is about \(2 \times 8 K N_{mask}\) bytes for
the two posterior buffers (plus \(8 K N_{mask}\) when
prior_weight > 0) and one integer per voxel of the full grid.
References
Avants BB, Tustison NJ, Wu J, Cook PA, Gee JC (2011).
An open source multivariate framework for n-tissue segmentation with
evaluation on public data. Neuroinformatics, 9(4), 381-400.
doi:10.1007/s12021-011-9109-y
Zhang Y, Brady M, Smith S (2001). Segmentation of brain MR images
through a hidden Markov random field model and the
expectation-maximization algorithm. IEEE Transactions on Medical
Imaging, 20(1), 45-57. doi:10.1109/42.906424
Dempster AP, Laird NM, Rubin DB (1977). Maximum likelihood from
incomplete data via the EM algorithm. Journal of the Royal
Statistical Society, Series B, 39(1), 1-38.
doi:10.1111/j.2517-6161.1977.tb01600.x
See also
register_volume3d to bring template priors into the
subject space
Examples
# A toy phantom: nested spheres with three intensity classes
nd <- 32
ctr <- (nd - 1) / 2
idx <- arrayInd(seq_len(nd^3), rep(nd, 3)) - 1
r <- sqrt(rowSums((idx - ctr)^2))
truth <- integer(nd^3)
truth[r <= 14] <- 1L
truth[r <= 10] <- 2L
truth[r <= 6] <- 3L
dim(truth) <- rep(nd, 3)
mask <- truth > 0
set.seed(1)
volume <- array(0, dim(truth))
volume[mask] <- c(30, 80, 130)[truth[mask]] + rnorm(sum(mask), sd = 12)
# k-means initialization, classes ordered by intensity
res <- segment_volume_tissue_gmm(volume, mask = mask)
res$means
#> [1] 29.90402 79.58744 129.79314
table(truth = truth[mask], segmented = res$segmentation[mask])
#> segmented
#> truth 1 2 3
#> 1 7290 22 0
#> 2 21 3286 5
#> 3 0 7 905
# the same with spatial priors (here a softened version of the truth, in a
# shuffled order) that fix the class order
priors <- lapply(c(3, 1, 2), function(k) {
p <- (truth == k) * 1
p <- (p + 0.1) / 1.3 # a soft, not quite informative prior
p
})
res2 <- segment_volume_tissue_gmm(volume, mask = mask, priors = priors,
prior_weight = 0.25)
res2$means # follows the prior order: bright, dark, medium
#> [1] 129.93406 29.96174 79.68156