File: nii_2_tracks.rst

package info (click to toggle)
dipy 0.5.0-3
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 4,780 kB
  • sloc: python: 10,563; makefile: 218; pascal: 138
file content (348 lines) | stat: -rw-r--r-- 9,871 bytes parent folder | download
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
.. AUTO-GENERATED FILE -- DO NOT EDIT!

.. _example_nii_2_tracks:



===============================
From raw data to tractographies
===============================

Overview
========

**This example gives a tour of some simple features of dipy.**

First import the necessary modules
----------------------------------

``numpy`` is for numerical computation


::
  
  import numpy as np
  

``nibabel`` is for data formats

::
  
  import nibabel as nib
  

``dipy.reconst`` is for the reconstruction algorithms which we use to create directionality models
for a voxel from the raw data.

::
  
  import dipy.reconst.gqi as gqi
  import dipy.reconst.dti as dti
  

``dipy.tracking`` is for tractography algorithms which create sets of tracks by integrating
  directionality models across voxels.

::
  
  from dipy.tracking.propagation import EuDX
  

``dipy.data`` is for small datasets we use in tests and examples.

::
  
  from dipy.data import get_data
  
  

Isotropic voxel sizes required
------------------------------
``dipy`` requires its datasets to have isotropic voxel size. If you have datasets with anisotropic voxel size
then you need to resample with isotropic voxel size. We have provided an algorithm for this.
You can have a look at the example ``resample_aniso_2_iso.py``

Accessing the necessary datasets
--------------------------------
``get_data`` provides data for a small region of interest from a real
diffusion weighted MR dataset acquired with 102 gradients (including one for b=0).

In order to make this work with your data you should comment out the line below and add the paths
for your nifti file (``*.nii`` or ``*.nii.gz``) and your ``*.bvec`` and ``*.bval files``.

If you are not using nifti files or you don't know how to create the ``*.bvec`` and ``*.bval`` files
from your raw dicom (``*.dcm``) data then you can either try recent module nibabel.nicom

::
  
  try:
      from nibabel.nicom.dicomreaders import read_mosaic_dir
  except:
      print('nicom for dicom is not installed')
  

or to convert the dicom files to nii, bvec and bval files using ``dcm2nii``.

::
  
  fimg,fbvals,fbvecs=get_data('small_101D')
  

**Load the nifti file found at path fimg as an Nifti1Image.**

::
  
  img=nib.load(fimg)
  

**Read the datasets from the Nifti1Image.**

::
  
  data=img.get_data()
  print('data.shape (%d,%d,%d,%d)' % data.shape)
  

This produces the output::

  data.shape (6,10,10,102)

As you would expect, the raw diffusion weighted MR data is 4-dimensional as
we have one 3-d volume (6 by 10 by 10) for each gradient direction.

**Read the affine matrix**
  which gives the mapping between volume indices (voxel coordinates) and world coordinates.

::
  
  affine=img.get_affine()
  

**Read the b-values** which are a function of the strength, duration, temporal spacing
and timing parameters of the specific paradigm used in the scanner, one per gradient direction.

::
  
  bvals=np.loadtxt(fbvals)
  

**Read the b-vectors**, the unit gradient directions.

::
  
  gradients=np.loadtxt(fbvecs).T
  

Calculating models and parameters of directionality
---------------------------------------------------
We are now set up with all the data and parameters to start calculating directional models
for voxels and their associated parameters, e.g. anisotropy.

**Calculate the Single Tensor Model (STM).**

::
  
  ten=dti.Tensor(data,bvals,gradients,thresh=50)
  

**Calculate Fractional Anisotropy (FA) from STM**

::
  
  FA=ten.fa()
  print('FA.shape (%d,%d,%d)' % FA.shape)
  

As expected the FA is a 3-d array with one value per voxel::

  FA.shape (6,10,10)

Generate a tractography
-----------------------
Here we use the Euler Delta Crossings (EuDX) algorithm.
The main input parameters of ``EuDX`` are

  * an anisotropic scalar metric e.g. FA
  * the indices for the peaks on the sampling sphere.

Other important options are

  * the number of random seeds where the track propagation is initiated,
  * a stopping criterion, for example a low threshold for anisotropy. For instance
    if we are using *Fractional Anisotropy (FA)* a typical threshold value might be ``a_low=.2``


::
  
  eu=EuDX(a=FA,ind=ten.ind(),seeds=10000,a_low=.2)
  

EuDX returns a generator class which yields a further track each time this class is called.
In this way we can generate millions of tracks without using a substantial amount of memory.
For an example of what to do when you want to generate millions of tracks with minimum memory usage have a look at
``save_dpy.py`` in the ``examples`` directory. However, in the current example that we only have 10000 seeds, and we can load all tracks
in a list using list comprehension([]) without having to worry about memory.

::
  
  ten_tracks=[track for track in eu]
  

In dipy we usually represent tractography as a list of tracks. Every track is a numpy array of shape (N,3)
where N is the number of points in the track.

::
  
  print ('The number of FA tracks is %d' % len(ten_tracks))
  print ('The number of points in ten_tracks[130] is %d' % len(ten_tracks[130]))
  print ('The points in ten_tracks[130] are:')
  print ten_tracks[130]
  

As we use random seeding for the tractography the results will differ when repeated, however
one run gave us the following information::

  The number of FA tracks is 8280
  The number of points in ten_track[130] is 7
  The points in ten_tracks[130] are:
  [[ 1.73680878  5.08249903  4.48492956]
   [ 1.45797026  4.76981783  4.21201992]
   [ 1.14244306  4.46308756  3.97461915]
   [ 0.84001541  4.14648438  3.73316503]
   [ 0.53758776  3.82988143  3.49171114]
   [ 0.22055824  3.52935386  3.24845099]
   [ 0.22055824  3.52935386  3.24845099]]

Another way to represent tractography is as a numpy array of numpy objects.
This way has an additional advantage that it can be saved very easily using numpy utilities.
In theory, in a list it is faster to append an element, and in an array is faster to access.
In other words both representations have different pros and cons.
Other representations are possible too e.g. graphtheoretic etc.

::
  
  ten_tracks_asobj=np.array(ten_tracks,dtype=np.object)
  np.save('ten_tracks.npy',ten_tracks_asobj)
  print('FA tracks saved in ten_tracks.npy')
  

Crossings and Generalized Q-Sampling
------------------------------------
You probably have heard about the problem of crossings in diffusion MRI.
The single tensor model cannot detect a simple crossing of two fibres.
However with *Generalized Q-Sampling (GQS)* this is possible even up to a quadruple crossing
or higher depending on the resolution of your datasets. Resolution will
typically depend on signal-to-noise ratio and voxel-size.

::
  
  gqs=gqi.GeneralizedQSampling(data,bvals,gradients)
  

A useful metric derived from GQS is *Quantitative Anisotropy* (QA).

::
  
  QA=gqs.qa()
  print('QA.shape (%d,%d,%d,%d)' % QA.shape)
  

QA is a 4-d array with up to 5 peak QA values for each voxel::

  QA.shape (6,10,10,5)

QA array is
significantly different in shape from the FA array,
however it too can be directly input to the EuDX class:

::
  
  eu2=EuDX(a=QA,ind=gqs.ind(),seeds=10000,a_low=.0239)
  

This shows one of the advantages of our EuDX algorithm: it can be used with a wide range of model-based methods, such as
  - Single Tensor
  - Multiple Tensor
  - Stick & Ball
  - Higher Order Tensor

and model-free methods such as
  - DSI
  - QBall
  - GQI *etc.*

We designed the algorithm this way so we that we can compare directly tractographies generated
from the same dataset
with very different models and/or choices of threshold.

Now we look at the QA tractography:

::
  
  gqs_tracks=[track for track in eu2]
  print('The number of QA tracks is %d' % len(gqs_tracks))
  

with output::

  The number of QA tracks is 14022

Note the difference between the number of gqs_tracks and ten_tracks. There are more with
QA than with FA. This is because of the
presence of crossings which GQI can detect but STM cannot. When the underlying directionality model supports crossings then
distinct tracks will be propagated from a seed towards the different directions in equal abundance.

In ``dipy`` it is very easy to count the number of crossings in a voxel, volume or region of interest


::
  
  gqs_tracks_asobj=np.array(gqs_tracks,dtype=np.object)
  np.save('gqs_tracks.npy',gqs_tracks_asobj)
  print('QA tracks saved in gqs_tracks.npy')
  

**This is the end of this very simple example** You can reload the saved tracks using
``np.load`` from your current directory. You can optionaly install ``python-vtk``
and visualize the tracks using ``fvtk``:

::
  
  from dipy.viz import fvtk
  r=fvtk.ren()
  fvtk.add(r,fvtk.line(ten_tracks,fvtk.red,opacity=0.05))
  gqs_tracks2=[t+np.array([10,0,0]) for t in gqs_tracks]
  fvtk.add(r,fvtk.line(gqs_tracks2,fvtk.green,opacity=0.05))
  

Press 's' to save this screenshot when you have displayed it with ``fvtk.show``.
Or you can even record a video using ``fvtk.record``.

You would show the figure with something like::

    fvtk.show(r,png_magnify=1,size=(600,600))

To record a video of 50 frames of png, something like::

    fvtk.record(r,cam_pos=(0,40,-40),cam_focal=(5,0,0),n_frames=50,magnification=1,out_path='nii_2_tracks',size=(600,600),bgr_color=(0,0,0))

.. figure:: nii_2_tracks1000000.png
   :align: center

   **Same region of interest with different underlying voxel representations generates different tractographies**.


::
  
  # Here's how we make the figure.
  print('Saving illustration as nii_2_tracks1000000.png')
  fvtk.record(r,n_frames=1,out_path='nii_2_tracks',size=(600,600))
  

        
.. admonition:: Example source code

   You can download :download:`the full source code of this example <./nii_2_tracks.py>`.
   This same script is also included in the dipy source distribution under the
   :file:`doc/examples/` directory.