Note
Go to the end to download the full example code
An introduction to the Probabilistic Direction Getter#
Probabilistic fiber tracking is a way of reconstructing white matter connections using diffusion MR imaging. Like deterministic fiber tracking, the probabilistic approach follows the trajectory of a possible pathway step by step starting at a seed, however, unlike deterministic tracking, the tracking direction at each point along the path is chosen at random from a distribution. The distribution at each point is different and depends on the observed diffusion data at that point. The distribution of tracking directions at each point can be represented as a probability mass function (PMF) if the possible tracking directions are restricted to discrete numbers of well distributed points on a sphere.
This example is an extension of the Introduction to Basic Tracking example. We’ll begin by repeating a few steps from that example, loading the data and fitting a Constrained Spherical Deconvolution (CSD) model.
from dipy.core.gradients import gradient_table
from dipy.data import get_fnames, small_sphere, default_sphere
from dipy.direction import ProbabilisticDirectionGetter, peaks_from_model
from dipy.io.gradients import read_bvals_bvecs
from dipy.io.image import load_nifti, load_nifti_data
from dipy.io.stateful_tractogram import Space, StatefulTractogram
from dipy.io.streamline import save_trk
from dipy.reconst.csdeconv import (ConstrainedSphericalDeconvModel,
auto_response_ssst)
from dipy.reconst.shm import CsaOdfModel
from dipy.tracking import utils
from dipy.tracking.local_tracking import LocalTracking
from dipy.tracking.streamline import Streamlines
from dipy.tracking.stopping_criterion import ThresholdStoppingCriterion
from dipy.viz import window, actor, colormap, has_fury
# Enables/disables interactive visualization
interactive = False
hardi_fname, hardi_bval_fname, hardi_bvec_fname = get_fnames('stanford_hardi')
label_fname = get_fnames('stanford_labels')
data, affine, hardi_img = load_nifti(hardi_fname, return_img=True)
labels = load_nifti_data(label_fname)
bvals, bvecs = read_bvals_bvecs(hardi_bval_fname, hardi_bvec_fname)
gtab = gradient_table(bvals, bvecs)
seed_mask = (labels == 2)
white_matter = (labels == 1) | (labels == 2)
seeds = utils.seeds_from_mask(seed_mask, affine, density=1)
response, ratio = auto_response_ssst(gtab, data, roi_radii=10, fa_thr=0.7)
csd_model = ConstrainedSphericalDeconvModel(gtab, response, sh_order_max=6)
csd_fit = csd_model.fit(data, mask=white_matter)
0%| | 0/58788 [00:00<?, ?it/s]
1%|█▍ | 621/58788 [00:00<00:10, 5677.97it/s]
2%|███ | 1338/58788 [00:00<00:09, 6193.97it/s]
4%|████▉ | 2190/58788 [00:00<00:08, 6923.39it/s]
5%|██████▋ | 2975/58788 [00:00<00:07, 7023.14it/s]
7%|████████▊ | 3885/58788 [00:00<00:07, 7490.65it/s]
8%|██████████▊ | 4759/58788 [00:00<00:07, 7660.47it/s]
10%|████████████▊ | 5671/58788 [00:00<00:06, 7882.10it/s]
11%|██████████████▉ | 6576/58788 [00:00<00:06, 8006.93it/s]
13%|████████████████▉ | 7498/58788 [00:00<00:06, 8139.83it/s]
14%|███████████████████▏ | 8455/58788 [00:01<00:06, 8328.04it/s]
16%|█████████████████████▏ | 9342/58788 [00:01<00:05, 8261.24it/s]
17%|███████████████████████ | 10256/58788 [00:01<00:05, 8290.14it/s]
19%|█████████████████████████ | 11149/58788 [00:01<00:05, 8251.38it/s]
20%|██████████████████████████▉ | 11979/58788 [00:01<00:05, 8051.19it/s]
22%|████████████████████████████▋ | 12787/58788 [00:01<00:05, 7851.03it/s]
23%|██████████████████████████████▋ | 13656/58788 [00:01<00:05, 7879.38it/s]
25%|████████████████████████████████▋ | 14571/58788 [00:01<00:05, 8025.83it/s]
26%|██████████████████████████████████▊ | 15502/58788 [00:01<00:05, 8172.23it/s]
28%|████████████████████████████████████▋ | 16320/58788 [00:02<00:05, 7630.59it/s]
29%|██████████████████████████████████████▋ | 17204/58788 [00:02<00:05, 7765.94it/s]
31%|████████████████████████████████████████▋ | 18110/58788 [00:02<00:05, 7920.38it/s]
32%|██████████████████████████████████████████▊ | 19050/58788 [00:02<00:04, 8119.51it/s]
34%|████████████████████████████████████████████▊ | 19945/58788 [00:02<00:04, 8138.53it/s]
35%|██████████████████████████████████████████████▊ | 20841/58788 [00:02<00:04, 8154.85it/s]
37%|████████████████████████████████████████████████▊ | 21716/58788 [00:02<00:04, 8108.07it/s]
39%|██████████████████████████████████████████████████▉ | 22658/58788 [00:02<00:04, 8259.48it/s]
40%|████████████████████████████████████████████████████▉ | 23590/58788 [00:02<00:04, 8337.76it/s]
42%|██████████████████████████████████████████████████████▉ | 24490/58788 [00:03<00:04, 8298.68it/s]
43%|████████████████████████████████████████████████████████▉ | 25368/58788 [00:03<00:04, 8224.52it/s]
45%|██████████████████████████████████████████████████████████▊ | 26191/58788 [00:03<00:04, 8013.74it/s]
46%|████████████████████████████████████████████████████████████▋ | 27033/58788 [00:03<00:04, 7919.71it/s]
48%|██████████████████████████████████████████████████████████████▋ | 27928/58788 [00:03<00:03, 7997.96it/s]
49%|████████████████████████████████████████████████████████████████▌ | 28771/58788 [00:03<00:03, 7911.44it/s]
50%|██████████████████████████████████████████████████████████████████▍ | 29563/58788 [00:03<00:03, 7709.72it/s]
52%|████████████████████████████████████████████████████████████████████ | 30335/58788 [00:03<00:03, 7205.58it/s]
53%|██████████████████████████████████████████████████████████████████████ | 31213/58788 [00:03<00:03, 7445.46it/s]
55%|███████████████████████████████████████████████████████████████████████▉ | 32054/58788 [00:04<00:03, 7517.04it/s]
56%|█████████████████████████████████████████████████████████████████████████▉ | 32911/58788 [00:04<00:03, 7610.55it/s]
57%|███████████████████████████████████████████████████████████████████████████▊ | 33762/58788 [00:04<00:03, 7661.04it/s]
59%|█████████████████████████████████████████████████████████████████████████████▋ | 34620/58788 [00:04<00:03, 7715.66it/s]
60%|███████████████████████████████████████████████████████████████████████████████▊ | 35544/58788 [00:04<00:02, 7934.82it/s]
62%|█████████████████████████████████████████████████████████████████████████████████▊ | 36425/58788 [00:04<00:02, 7970.72it/s]
64%|███████████████████████████████████████████████████████████████████████████████████▉ | 37363/58788 [00:04<00:02, 8151.14it/s]
65%|█████████████████████████████████████████████████████████████████████████████████████▉ | 38290/58788 [00:04<00:02, 8248.39it/s]
67%|████████████████████████████████████████████████████████████████████████████████████████▏ | 39259/58788 [00:04<00:02, 8431.22it/s]
68%|██████████████████████████████████████████████████████████████████████████████████████████▎ | 40209/58788 [00:05<00:02, 8507.55it/s]
70%|████████████████████████████████████████████████████████████████████████████████████████████▎ | 41110/58788 [00:05<00:02, 8424.11it/s]
72%|██████████████████████████████████████████████████████████████████████████████████████████████▍ | 42054/58788 [00:05<00:01, 8487.78it/s]
73%|████████████████████████████████████████████████████████████████████████████████████████████████▌ | 42994/58788 [00:05<00:01, 8520.45it/s]
75%|██████████████████████████████████████████████████████████████████████████████████████████████████▋ | 43951/58788 [00:05<00:01, 8588.40it/s]
76%|████████████████████████████████████████████████████████████████████████████████████████████████████▋ | 44861/58788 [00:05<00:01, 8508.57it/s]
78%|██████████████████████████████████████████████████████████████████████████████████████████████████████▋ | 45746/58788 [00:05<00:01, 8382.73it/s]
79%|████████████████████████████████████████████████████████████████████████████████████████████████████████▊ | 46695/58788 [00:05<00:01, 8470.92it/s]
81%|██████████████████████████████████████████████████████████████████████████████████████████████████████████▊ | 47587/58788 [00:05<00:01, 8376.14it/s]
82%|████████████████████████████████████████████████████████████████████████████████████████████████████████████▉ | 48498/58788 [00:06<00:01, 8362.04it/s]
84%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████ | 49443/58788 [00:06<00:01, 8445.30it/s]
86%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████ | 50362/58788 [00:06<00:00, 8432.44it/s]
87%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏ | 51273/58788 [00:06<00:00, 8401.69it/s]
89%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ | 52163/58788 [00:06<00:00, 8322.06it/s]
90%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏ | 53080/58788 [00:06<00:00, 8340.80it/s]
92%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▎ | 54005/58788 [00:06<00:00, 8375.74it/s]
93%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▏ | 54860/58788 [00:06<00:00, 8207.85it/s]
95%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ | 55681/58788 [00:06<00:00, 7998.02it/s]
96%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▉ | 56555/58788 [00:07<00:00, 7995.93it/s]
98%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▊ | 57355/58788 [00:07<00:00, 7791.39it/s]
99%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▌ | 58135/58788 [00:07<00:00, 7592.94it/s]
100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 58788/58788 [00:07<00:00, 7988.11it/s]
We use the GFA of the CSA model to build a stopping criterion.
csa_model = CsaOdfModel(gtab, sh_order_max=6)
gfa = csa_model.fit(data, mask=white_matter).gfa
stopping_criterion = ThresholdStoppingCriterion(gfa, .25)
The Fiber Orientation Distribution (FOD) of the CSD model estimates the
distribution of small fiber bundles within each voxel. We can use this
distribution for probabilistic fiber tracking. One way to do this is to
represent the FOD using a discrete sphere. This discrete FOD can be used by
the ProbabilisticDirectionGetter
as a PMF for sampling tracking
directions. We need to clip the FOD to use it as a PMF because the latter
cannot have negative values. Ideally, the FOD should be strictly positive,
but because of noise and/or model failures sometimes it can have negative
values.
fod = csd_fit.odf(small_sphere)
pmf = fod.clip(min=0)
prob_dg = ProbabilisticDirectionGetter.from_pmf(pmf, max_angle=30.,
sphere=small_sphere)
streamline_generator = LocalTracking(prob_dg, stopping_criterion, seeds,
affine, step_size=.5)
streamlines = Streamlines(streamline_generator)
sft = StatefulTractogram(streamlines, hardi_img, Space.RASMM)
save_trk(sft, "tractogram_probabilistic_dg_pmf.trk")
if has_fury:
scene = window.Scene()
scene.add(actor.line(streamlines, colormap.line_colors(streamlines)))
window.record(scene, out_path='tractogram_probabilistic_dg_pmf.png',
size=(800, 800))
if interactive:
window.show(scene)
Corpus Callosum using probabilistic direction getter from PMF
One disadvantage of using a discrete PMF to represent possible tracking
directions is that it tends to take up a lot of memory (RAM). The size of the
PMF, the FOD in this case, must be equal to the number of possible tracking
directions on the hemisphere, and every voxel has a unique PMF. In this case
the data is (81, 106, 76)
and small_sphere
has 181 directions so the
FOD is (81, 106, 76, 181)
. One way to avoid sampling the PMF and holding
it in memory is to build the direction getter directly from the spherical
harmonic (SH) representation of the FOD. By using this approach, we can also
use a larger sphere, like default_sphere
which has 362 directions on the
hemisphere, without having to worry about memory limitations.
prob_dg = ProbabilisticDirectionGetter.from_shcoeff(csd_fit.shm_coeff,
max_angle=30.,
sphere=default_sphere,
sh_to_pmf=True)
streamline_generator = LocalTracking(prob_dg, stopping_criterion, seeds,
affine, step_size=.5)
streamlines = Streamlines(streamline_generator)
sft = StatefulTractogram(streamlines, hardi_img, Space.RASMM)
save_trk(sft, "tractogram_probabilistic_dg_sh.trk")
if has_fury:
scene = window.Scene()
scene.add(actor.line(streamlines, colormap.line_colors(streamlines)))
window.record(scene, out_path='tractogram_probabilistic_dg_sh.png',
size=(800, 800))
if interactive:
window.show(scene)
Corpus Callosum using probabilistic direction getter from SH
Not all model fits have the shm_coeff
attribute because not all models
use this basis to represent the data internally. However we can fit the ODF
of any model to the spherical harmonic basis using the peaks_from_model
function.
peaks = peaks_from_model(csd_model, data, default_sphere, .5, 25,
mask=white_matter, return_sh=True, parallel=True,
num_processes=2)
fod_coeff = peaks.shm_coeff
prob_dg = ProbabilisticDirectionGetter.from_shcoeff(fod_coeff, max_angle=30.,
sphere=default_sphere,
sh_to_pmf=True)
streamline_generator = LocalTracking(prob_dg, stopping_criterion, seeds,
affine, step_size=.5)
streamlines = Streamlines(streamline_generator)
sft = StatefulTractogram(streamlines, hardi_img, Space.RASMM)
save_trk(sft, "tractogram_probabilistic_dg_sh_pfm.trk")
if has_fury:
scene = window.Scene()
scene.add(actor.line(streamlines, colormap.line_colors(streamlines)))
window.record(scene, out_path='tractogram_probabilistic_dg_sh_pfm.png',
size=(800, 800))
if interactive:
window.show(scene)
Corpus Callosum using probabilistic direction getter from SH (peaks_from_model)
Total running time of the script: (0 minutes 52.617 seconds)