1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
|
"""
=================================================================
Continuous and analytical diffusion signal modelling with MAP-MRI
=================================================================
We show how to model the diffusion signal as a linear combination of continuous
functions from the MAP-MRI basis :footcite:p:`Ozarslan2013`. This continuous
representation allows for the computation of many properties of both the signal
and diffusion propagator.
We show how to estimate the analytical orientation distribution function (ODF)
and a variety of scalar indices. These include rotationally invariant
quantities such as the mean squared displacement (MSD), Q-space inverse
variance (QIV), teturn-to-origin probability (RTOP) and non-Gaussianity (NG).
Interestingly, the MAP-MRI basis also allows for the computation of directional
indices, such as the return-to-axis probability (RTAP), the return-to-plane
probability (RTPP), and the parallel and perpendicular non-Gaussianity.
The estimation of these properties from noisy and sparse DWIs requires that the
fitting of the MAP-MRI basis is constrained and/or regularized. This can be
done by constraining the diffusion propagator to positive values
:footcite:p:`Ozarslan2013`, :footcite:p:`DelaHaije2020`, and through analytic
Laplacian regularization (MAPL) :footcite:p:`Fick2016b`.
First import the necessary modules:
"""
from dipy.core.gradients import gradient_table
from dipy.data import get_fnames, get_sphere
from dipy.io.gradients import read_bvals_bvecs
from dipy.io.image import load_nifti
from dipy.reconst import mapmri
from dipy.viz import actor, window
from dipy.viz.plotting import compare_maps
###############################################################################
# Download and read the data for this tutorial.
#
# MAP-MRI requires multi-shell data, to properly fit the radial part of the
# basis. The total size of the downloaded data is 187.66 MBytes, however you
# only need to fetch it once.
fraw, fbval, fbvec, t1_fname = get_fnames(name="cfin_multib")
###############################################################################
# ``data`` contains the voxel data and ``gtab`` contains a ``GradientTable``
# object (gradient information e.g. b-values). For example, to show the
# b-values it is possible to write::
#
# print(gtab.bvals)
#
# For the values of the q-space indices to make sense it is necessary to
# explicitly state the ``big_delta`` and ``small_delta`` parameters in the
# gradient table.
data, affine = load_nifti(fraw)
bvals, bvecs = read_bvals_bvecs(fbval, fbvec)
gtab = gradient_table(bvals, bvecs=bvecs)
big_delta = 0.0365 # seconds
small_delta = 0.0157 # seconds
gtab = gradient_table(
bvals=gtab.bvals, bvecs=gtab.bvecs, big_delta=big_delta, small_delta=small_delta
)
data_small = data[40:65, 50:51]
print(f"data.shape {data.shape}")
###############################################################################
# The MAP-MRI Model can now be instantiated. The ``radial_order`` determines
# the expansion order of the basis, i.e., how many basis functions are used to
# approximate the signal.
#
# First, we must decide to use the anisotropic or isotropic MAP-MRI basis. As
# was shown in :footcite:p:`Fick2016b`, the isotropic basis is best used for
# tractography purposes, as the anisotropic basis has a bias towards smaller
# crossing angles in the ODF. For signal fitting and estimation of scalar
# quantities the anisotropic basis is suggested. The choice can be made by
# setting ``anisotropic_scaling=True`` or ``anisotropic_scaling=False``.
#
# First, we must select the method of regularization and/or constraining the
# basis fitting.
#
# - ``laplacian_regularization=True`` makes it use Laplacian regularization
# :footcite:p:`Fick2016b`. This method essentially reduces spurious
# oscillations in the reconstruction by minimizing the Laplacian of the fitted
# signal. Several options can be given to select the regularization weight:
#
# - ``regularization_weighting=GCV`` uses generalized cross-validation
# :footcite:p:`Craven1979` to find an optimal weight.
# - ``regularization_weighting=0.2`` for example would omit the GCV and
# just set it to 0.2 (found to be reasonable in HCP data
# :footcite:p:`Fick2016b`).
# - ``regularization_weighting=np.array(weights)`` would make the GCV use
# a custom range to find an optimal weight.
#
# - ``positivity_constraint=True`` makes it use a positivity constraint on the
# diffusion propagator. This method constrains the final solution of the
# diffusion propagator to be positive either globally
# :footcite:p:`DelaHaije2020` or at a set of discrete points
# :footcite:p:`Ozarslan2013`, since negative values should not exist.
#
# - Setting ``global_constraints=True`` enforces positivity everywhere.
# With the setting ``global_constraints=False`` positivity is enforced on
# a grid determined by ``pos_grid`` and ``pos_radius``.
#
# Both methods do a good job of producing viable solutions to the signal
# fitting in practice. The difference is that the Laplacian regularization
# imposes smoothness over the entire signal, including the extrapolation
# beyond the measured signal. In practice this may result in, but does not
# guarantee, positive solutions of the diffusion propagator. The positivity
# constraint guarantees a positive solution which in general results in smooth
# solutions, but does not guarantee it.
#
# A suggested strategy is to use a low Laplacian weight together with a
# positivity constraint. In this way both desired properties are guaranteed
# in final solution. Higher Laplacian weights are recommended when the data is
# very noisy.
#
# We use the package CVXPY (https://www.cvxpy.org/) to solve convex
# optimization problems when ``positivity_constraint=True``, so we need to
# first install CVXPY. When using ``global_constraints=True`` to ensure
# global positivity, it is recommended to use the MOSEK solver
# (https://www.mosek.com/) together with CVXPY by setting
# ``cvxpy_solver='MOSEK'``. Different solvers can differ greatly in
# terms of runtime and solution accuracy, and in some cases solvers may show
# warnings about convergence or recommended option settings.
#
# For now we will generate the anisotropic models for different combinations.
radial_order = 6
map_model_laplacian_aniso = mapmri.MapmriModel(
gtab,
radial_order=radial_order,
laplacian_regularization=True,
laplacian_weighting=0.2,
)
map_model_positivity_aniso = mapmri.MapmriModel(
gtab,
radial_order=radial_order,
laplacian_regularization=False,
positivity_constraint=True,
)
map_model_both_aniso = mapmri.MapmriModel(
gtab,
radial_order=radial_order,
laplacian_regularization=True,
laplacian_weighting=0.05,
positivity_constraint=True,
)
map_model_plus_aniso = mapmri.MapmriModel(
gtab,
radial_order=radial_order,
laplacian_regularization=False,
positivity_constraint=True,
global_constraints=True,
)
###############################################################################
# Note that when we use only Laplacian regularization, the ``GCV`` option may
# select very low regularization weights in very anisotropic white matter such
# as the corpus callosum, resulting in corrupted scalar indices. In this
# example we therefore choose a preset value of 0.2, which was shown to be
# quite robust and also faster in practice :footcite:p:`Fick2016b`.
#
# We can then fit the MAP-MRI model to the data:
mapfit_laplacian_aniso = map_model_laplacian_aniso.fit(data_small)
mapfit_positivity_aniso = map_model_positivity_aniso.fit(data_small)
mapfit_both_aniso = map_model_both_aniso.fit(data_small)
mapfit_plus_aniso = map_model_plus_aniso.fit(data_small)
fits = [
mapfit_laplacian_aniso,
mapfit_positivity_aniso,
mapfit_both_aniso,
mapfit_plus_aniso,
]
fit_labels = ["MAPL", "CMAP", "CMAPL", "MAP+"]
###############################################################################
# From the fitted models we will first illustrate the estimation of q-space
# indices. We will compare the estimation using only Laplacian regularization
# (MAPL), using only the global positivity constraint (MAP+), using only
# positivity in discrete points (CMAP), or using both Laplacian regularization
# and positivity in discrete points (CMAPL). We first show the RTOP
# :footcite:p:`Ozarslan2013`.
compare_maps(
fits,
maps=["rtop"],
fit_labels=fit_labels,
map_labels=["RTOP"],
filename="MAPMRI_rtop.png",
)
###############################################################################
# .. rst-class:: centered small fst-italic fw-semibold
#
# It can be seen that all maps appear quite smooth and similar, although it is
# clear that the global positivity constraints provide smoother maps than the
# discrete constraints. Higher Laplacian weights also produce smoother maps,
# but tend to suppress the estimated RTOP values. The similarity and
# differences in reconstruction can be further illustrated by visualizing the
# analytic norm of the Laplacian of the fitted signal.
compare_maps(
fits,
maps=["norm_of_laplacian_signal"],
fit_labels=fit_labels,
map_labels=["Norm of Laplacian"],
map_kwargs={"vmin": 0, "vmax": 3},
filename="MAPMRI_norm_laplacian.png",
)
###############################################################################
# .. rst-class:: centered small fst-italic fw-semibold
#
# A high Laplacian norm indicates that the gradient in the three-dimensional
# signal reconstruction changes a lot -- something that may indicate spurious
# oscillations. In the Laplacian reconstruction (left) we see that there are
# some isolated voxels that have a higher norm than the rest. In the positivity
# constrained reconstruction the norm is already smoother. When both methods
# are used together the overall norm gets smoother still, since both smoothness
# of the signal and positivity of the propagator are imposed.
#
# From now on we will just use the combined approach and the globally
# constrained approach, show all maps we can generate, and explain their
# significance.
fits = fits[2:]
fit_labels = fit_labels[2:]
compare_maps(
fits,
maps=["msd", "qiv", "rtop", "rtap", "rtpp"],
fit_labels=fit_labels,
map_labels=["MSD", "QIV", "RTOP", "RTAP", "RTPP"],
filename="MAPMRI_maps.png",
)
###############################################################################
# .. rst-class:: centered small fst-italic fw-semibold
#
# From left to right:
#
# - Mean squared displacement (MSD) is a measure for how far protons are able
# to diffuse. a decrease in MSD indicates protons are hindered/restricted
# more, as can be seen by the high MSD in the CSF, but low in the white
# matter.
# - Q-space Inverse Variance (QIV) is a measure of variance in the signal,
# which is said to have higher contrast to white matter than the MSD
# :footcite:p:`Hosseinbor2013`. QIV has also been shown to have high
# sensitivity to tissue composition change in a simulation study
# :footcite:p:`Fick2016a`.
# - Return-to-origin probability (RTOP) quantifies the probability that a
# proton will be at the same position at the first and second diffusion
# gradient pulse. A higher RTOP indicates that the volume a spin is inside
# of is smaller, meaning more overall restriction. This is illustrated by
# the low values in CSF and high values in white matter.
# - Return-to-axis probability (RTAP) is a directional index that quantifies
# the probability that a proton will be along the axis of the main
# eigenvector of a diffusion tensor during both diffusion gradient pulses.
# RTAP has been related to the apparent axon diameter
# :footcite:p:`Ozarslan2013`, :footcite:p:`Fick2016b` under several strong
# assumptions on the tissue composition and acquisition protocol.
# - Return-to-plane probability (RTPP) is a directional index that quantifies
# the probability that a proton will be on the plane perpendicular to the
# main eigenvector of a diffusion tensor during both gradient pulses. RTPP
# is related to the length of a pore :footcite:p:`Ozarslan2013` but in
# practice should be similar to that of Gaussian diffusion.
#
#
# It is also possible to estimate the amount of non-Gaussian diffusion in every
# voxel :footcite:p:`Ozarslan2013`. This quantity is estimated through the ratio
# between Gaussian volume (MAP-MRI's first basis function) and the non-Gaussian
# volume (all other basis functions) of the fitted signal. For this value to be
# physically meaningful we must use a b-value threshold in the MAP-MRI model.
# This threshold makes the scale estimation in MAP-MRI use only samples that
# realistically describe Gaussian diffusion, i.e., at low b-values.
map_model_both_ng = mapmri.MapmriModel(
gtab,
radial_order=radial_order,
laplacian_regularization=True,
laplacian_weighting=0.05,
positivity_constraint=True,
bval_threshold=2000,
)
map_model_plus_ng = mapmri.MapmriModel(
gtab,
radial_order=radial_order,
positivity_constraint=True,
global_constraints=True,
bval_threshold=2000,
)
mapfit_both_ng = map_model_both_ng.fit(data_small)
mapfit_plus_ng = map_model_plus_ng.fit(data_small)
fits = [mapfit_both_ng, mapfit_plus_ng]
fit_labels = ["CMAPL", "MAP+"]
compare_maps(
fits,
maps=["ng", "ng_perpendicular", "ng_parallel"],
fit_labels=fit_labels,
map_labels=["NG", "NG perpendicular", "NG parallel"],
filename="MAPMRI_ng.png",
)
###############################################################################
# .. rst-class:: centered small fst-italic fw-semibold
#
# On the left we see the overall NG and on the right the directional
# perpendicular NG and parallel NG. The NG ranges from 1
# (completely non-Gaussian) to 0 (completely Gaussian). The overall NG of a
# voxel is always higher or equal than each of its components. It can be seen
# that NG has low values in the CSF and higher in the white matter.
#
# Increases or decreases in these values do not point to a specific
# microstructural change, but can indicate clues as to what is happening,
# similar to fractional anisotropy. An initial simulation study that quantifies
# the added value of q-space indices over DTI indices is given in
# :footcite:p:`Fick2016a`.
#
# The MAP-MRI framework also allows for the estimation of orientation
# distribution functions (ODFs). We recommend to use the isotropic
# implementation for this purpose, as the anisotropic implementation has a bias
# towards smaller crossing angles.
#
# For the isotropic basis we recommend to use a ``radial_order`` of 8, as the
# basis needs more generic and needs more basis functions to approximate the
# signal.
radial_order = 8
map_model_both_iso = mapmri.MapmriModel(
gtab,
radial_order=radial_order,
laplacian_regularization=True,
laplacian_weighting=0.1,
positivity_constraint=True,
anisotropic_scaling=False,
)
mapfit_both_iso = map_model_both_iso.fit(data_small)
###############################################################################
# Load an ODF reconstruction sphere.
sphere = get_sphere(name="repulsion724")
###############################################################################
# Compute the ODFs.
#
# The radial order ``s`` can be increased to sharpen the results, but it might
# also make the ODFs noisier. Always check the results visually.
odf = mapfit_both_iso.odf(sphere, s=2)
print(f"odf.shape {odf.shape}")
###############################################################################
# Display the ODFs.
# Enables/disables interactive visualization
interactive = False
scene = window.Scene()
sfu = actor.odf_slicer(odf, sphere=sphere, colormap="plasma", scale=0.5)
sfu.display(y=0)
sfu.RotateX(-90)
scene.add(sfu)
window.record(scene=scene, out_path="odfs.png", size=(600, 600))
if interactive:
window.show(scene)
###############################################################################
# .. rst-class:: centered small fst-italic fw-semibold
#
# Orientation distribution functions (ODFs).
#
#
# References
# ----------
#
# .. footbibliography::
#
|