Skip to content

Commit 7b78184

Browse files
authored
Fiber Tracking: FBI tractography (#235)
* Fix DTI protocol (#228) * Update reslicing to use new `mrgrid` command This also works with older `mrreslice` * Fix bad indent in tensorReorder() * Update documentation * Add bval scaling (#230) * Fix SNR plotting issues (#233) * Add try/except loop to SNR plot * Fix eddy path when `--noqc` is parsef * Fix a bad indent * Update CHANGELOG * Init tractography module (#234) * Init tractography module with DSI Studio * Update main to use tractography modules * Update version and MANIFEST * Update docs
1 parent d8bd921 commit 7b78184

12 files changed

Lines changed: 358 additions & 42 deletions

File tree

CHANGELOG.rst

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,35 @@ Changelog
44
All notable changes to this project will be documented in this file or
55
page
66

7+
`v1.0_RC5`_
8+
-----------
9+
10+
Oct 26, 2020
11+
12+
**Added**:
13+
14+
* Check for b-value scaling so .bval file so values
15+
specified as either 2.0 or 2000 can be processed.
16+
* ``fitting.dwipy()`` can now be pointed to user-defined
17+
bvec and bval paths. It previously required bvec and
18+
bval files to have the same name and path as DWI.
19+
* **DSI Studio tractography** for FBI. Processing FBI dataset now
20+
produces an ``fbi_tractography_dsi.fib`` file that can be loaded
21+
into DSI Studio to perform tractography.
22+
23+
**Changed**:
24+
25+
* Fixed issue where eddy correction would attempt
26+
to QC and fail despite parsing the ``--noqc`` flag.
27+
* SNR plotting works in very specific scenarious when
28+
input DWIs are of the same same dimensions. A try/except
29+
loop now ensure that the entire pipeline doesn't halt
30+
due to errors in plotting.
31+
32+
**Removed**:
33+
34+
* None
35+
736
`v1.0_RC4`_
837
-----------
938

@@ -218,6 +247,7 @@ Initial port of MATLAB code to Python. 200,000,000,000 BCE
218247

219248
.. Links
220249
250+
.. _v1.0_RC5: https://github.com/m-ama/PyDesigner/releases/tag/v1.0_RC5
221251
.. _v1.0_RC4: https://github.com/m-ama/PyDesigner/releases/tag/v1.0_RC4
222252
.. _v1.0_RC3: https://github.com/m-ama/PyDesigner/releases/tag/v1.0_RC3
223253
.. _v1.0_RC2: https://github.com/m-ama/PyDesigner/releases/tag/v1.0_RC2

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
include designer/fitting/*.csv
2+
include desiger/tractography/*.mat
23
global-exclude *.py[cod] __pycache__ *.so

designer/fitting/dwipy.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class DWI(object):
4747
workers : int
4848
Number of CPU workers to use in processing
4949
"""
50-
def __init__(self, imPath, mask=None, nthreads=-1):
50+
def __init__(self, imPath, bvecPath=None, bvalPath=None, mask=None, nthreads=-1):
5151
"""
5252
DWI class initializer
5353
@@ -71,20 +71,37 @@ def __init__(self, imPath, mask=None, nthreads=-1):
7171
(path, file) = os.path.split(imPath)
7272
# Remove extension from NIFTI filename
7373
fName = os.path.splitext(file)[0]
74-
# Add .bval to NIFTI filename
75-
bvalPath = os.path.join(path, fName + '.bval')
76-
# Add .bvec to NIFTI filename
77-
bvecPath = os.path.join(path, fName + '.bvec')
74+
if bvecPath:
75+
if not isinstance(bvecPath, str):
76+
raise TypeError('Path to .bvec is not specified '
77+
'as a string')
78+
if not os.path.exists(bvecPath):
79+
raise OSError('Path to .bvec does not exist: '
80+
'{}'.format(bvecPath))
81+
else:
82+
bvecPath = os.path.join(path, fName + '.bvec')
83+
if bvalPath:
84+
if not isinstance(bvalPath, str):
85+
raise TypeError('Path to .bval is not specified '
86+
'as a string')
87+
if not os.path.exists(bvalPath):
88+
raise OSError('Path to .bvec does not exist: '
89+
'{}'.format(bvalPath))
90+
else:
91+
bvalPath = os.path.join(path, fName + '.bval')
7892
if os.path.exists(bvalPath) and os.path.exists(bvecPath):
7993
# Load bvecs
8094
bvecs = np.loadtxt(bvecPath)
8195
# Load bvals
82-
bvals = np.rint(np.loadtxt(bvalPath) / 1000)
96+
bvals = np.rint(np.loadtxt(bvalPath))
97+
# Scale bvals by checking for number of digits in max bval
98+
if int(np.log10(np.max(bvals)))+1 >= 3: # if no. of digits >= 3
99+
bvals = bvals / 1000
83100
# Combine bvecs and bvals into [n x 4] array where n is
84101
# number of DWI volumes. [Gx Gy Gz Bval]
85102
self.grad = np.c_[np.transpose(bvecs), bvals]
86103
else:
87-
raise NameError('Unable to locate BVAL or BVEC files')
104+
raise OSError('Unable to locate BVAL or BVEC files')
88105
if mask is None:
89106
maskPath = os.path.join(path,'brain_mask.nii')
90107
else:

designer/info.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
)
1111
)
1212
__packagename__ = 'PyDesigner'
13-
__version__='v1.0-RC4'
13+
__version__='v1.0-RC5'
1414
__author__ = 'PyDesigner developers'
1515
__copyright__ = 'Copyright 2020, PyDesigner developers, MUSC Advanced Image Analysis (MAMA)'
1616
__credits__ = [
@@ -48,7 +48,8 @@
4848
'joblib >= 0.16',
4949
'tqdm >= 4.40',
5050
'multiprocess >= 0.70',
51-
'nibabel >= 3.1',
51+
'nibabel >= 3.2',
52+
'dipy >= 1.2',
5253
'cvxpy >= 1.1'
5354
]
5455

designer/preprocessing/mrpreproc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,8 @@ def undistort(input, output, rpe='rpe_header', epib0=1,
303303
epib0 : int
304304
Number of reverse PE dir B0 pairs to use in TOPUP correction
305305
(Default: 1)
306-
qc : bool
307-
Specify whether to generate eddy QC metrics (Default: True)
306+
qc : str
307+
Specify path to QC directior. No QC metrics generated if None
308308
nthreads : int, optional
309309
Specify the number of threads to use in processing
310310
(Default: all available threads)

designer/pydesigner.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from designer.plotting import snrplot, outlierplot, motionplot
2121
from designer.fitting import dwipy as dp
2222
from designer.postprocessing import filters
23+
from designer.tractography import dsistudio as ds
2324
DWIFile = util.DWIFile
2425
DWIParser = util.DWIParser
2526

@@ -591,6 +592,8 @@ def main():
591592
undistorted_name_full = str(step_count)+ '_' + undistorted_name
592593
nii_undistorted = op.join(intermediatepath, undistorted_name_full + '.nii')
593594
mif_undistorted = op.join(outpath, undistorted_name_full + '.mif')
595+
if args.noqc:
596+
eddyqcpath = None
594597
# check to see if this already exists
595598
if not (args.resume and op.exists(nii_undistorted)):
596599
# run undistort function
@@ -764,15 +767,19 @@ def main():
764767
files = []
765768
files.append(init_nii)
766769
files.append(filetable['HEAD'].getFull())
767-
if 'mask' in filetable:
768-
snr = snrplot.makesnr(dwilist=files,
769-
noisepath=nii_noisemap,
770-
maskpath=filetable['mask'].getFull())
771-
else:
772-
snr = snrplot.makesnr(dwilist=files,
773-
noisepath=filetable['noisemap'].getFull(),
774-
maskpath=None)
775-
snr.makeplot(path=qcpath, smooth=True, smoothfactor=3)
770+
try:
771+
if 'mask' in filetable:
772+
snr = snrplot.makesnr(dwilist=files,
773+
noisepath=nii_noisemap,
774+
maskpath=filetable['mask'].getFull())
775+
else:
776+
snr = snrplot.makesnr(dwilist=files,
777+
noisepath=filetable['noisemap'].getFull(),
778+
maskpath=None)
779+
snr.makeplot(path=qcpath, smooth=True, smoothfactor=3)
780+
except:
781+
print('[WARNING] SNR plotting failed, see above. '
782+
'Proceeding with processing.')
776783

777784
#-----------------------------------------------------------------
778785
# Write logs
@@ -823,6 +830,7 @@ def main():
823830
fn_fbi_zeta = 'fbi_zeta'
824831
fn_fbi_faa = 'fbi_faa'
825832
fn_fbi_sph = 'fbi_fodf'
833+
fn_fbi_tract = 'fbi_tractography_dsi'
826834
fn_fbi_awf = 'fbwm_awf'
827835
fn_fbi_Da = 'fbwm_da'
828836
fn_fbi_De_mean = 'fbwm_de_mean'
@@ -875,9 +883,10 @@ def main():
875883
if (img.isdti() or img.isdki()) and not args.noakc:
876884
akc_out = img.akcoutliers()
877885
img.akccorrect(akc_out)
878-
dp.writeNii(akc_out,
879-
img.hdr,
880-
op.join(fitqcpath, 'outliers_akc'))
886+
if not args.noqc:
887+
dp.writeNii(akc_out,
888+
img.hdr,
889+
op.join(fitqcpath, 'outliers_akc'))
881890

882891
# reorder tensor for mrtrix3
883892
if 'dki' in img.tensorType():
@@ -1015,6 +1024,17 @@ def main():
10151024
input=op.join(metricpath, x + fn_ext),
10161025
output=op.join(metricpath, x + fn_ext),
10171026
mask=filetable['mask'].getFull())
1018-
1027+
if 'mask' in filetable:
1028+
ds.makefib(
1029+
input=op.join(metricpath, fn_fbi_sph + fn_ext),
1030+
output=op.join(metricpath, fn_fbi_tract + '.fib'),
1031+
mask=filetable['mask'].getFull()
1032+
)
1033+
else:
1034+
ds.makefib(
1035+
input=op.join(metricpath, fn_fbi_sph + fn_ext),
1036+
output=op.join(metricpath, fn_fbi_tract + '.fib'),
1037+
mask=None
1038+
)
10191039
if __name__ == '__main__':
10201040
main()

designer/tractography/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)