Skip to content

Commit b03dad0

Browse files
committed
Inital commit to add niftyfit interfaces
1 parent d387481 commit b03dad0

16 files changed

+1818
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
4+
"""
5+
The niftyfit module provides classes for interfacing with the `NiftyFit`_
6+
command line tools.
7+
8+
Top-level namespace for niftyfit.
9+
"""
10+
11+
from .asl import FitAsl
12+
from .base import no_niftyfit, get_custom_path
13+
from .dwi import FitDwi, DwiTool
14+
from .qt1 import FitQt1

nipype/interfaces/niftyfit/asl.py

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
4+
"""
5+
The ASL module of niftyfit, which wraps the fitting methods in NiftyFit.
6+
"""
7+
8+
from ..base import TraitedSpec, traits, isdefined, CommandLineInputSpec
9+
from .base import NiftyFitCommand, get_custom_path
10+
11+
12+
class FitAslInputSpec(CommandLineInputSpec):
13+
""" Input Spec for FitAsl. """
14+
desc = 'Filename of the 4D ASL (control/label) source image (mandatory).'
15+
source_file = traits.File(position=1,
16+
exists=True,
17+
argstr='-source %s',
18+
mandatory=True,
19+
desc=desc)
20+
pasl = traits.Bool(desc='Fit PASL ASL data [default]', argstr='-pasl')
21+
pcasl = traits.Bool(desc='Fit PCASL ASL data', argstr='-pcasl')
22+
23+
# *** Output options:
24+
desc = 'Filename of the Cerebral Blood Flow map (in ml/100g/min).'
25+
cbf_file = traits.File(genfile=True, argstr='-cbf %s', desc=desc)
26+
desc = 'Filename of the CBF error map.'
27+
error_file = traits.File(genfile=True, argstr='-error %s', desc=desc)
28+
desc = 'Filename of the synthetic ASL data.'
29+
syn_file = traits.File(genfile=True, argstr='-syn %s', desc=desc)
30+
31+
# *** Input options (see also fit_qt1 for generic T1 fitting):
32+
desc = 'Filename of the estimated input T1 map (in ms).'
33+
t1map = traits.File(exists=True, argstr='-t1map %s', desc=desc)
34+
desc = 'Filename of the estimated input M0 map.'
35+
m0map = traits.File(exists=True, argstr='-m0map %s', desc=desc)
36+
desc = 'Filename of the estimated input M0 map error.'
37+
m0mape = traits.File(exists=True, argstr='-m0mape %s', desc=desc)
38+
desc = 'Filename of a [1,2,5]s Inversion Recovery volume (T1/M0 fitting \
39+
carried out internally).'
40+
IRvolume = traits.File(exists=True, argstr='-IRvolume %s', desc=desc)
41+
desc = 'Output of [1,2,5]s Inversion Recovery fitting.'
42+
IRoutput = traits.File(exists=True, argstr='-IRoutput %s', desc=desc)
43+
44+
# *** Experimental options (Choose those suitable for the model!):
45+
mask = traits.File(position=2,
46+
exists=True,
47+
desc='Filename of image mask.',
48+
argstr='-mask %s')
49+
T1a = traits.Float(desc='T1 of arterial component [1650ms].',
50+
argstr='-T1a %f')
51+
desc = 'Single plasma/tissue partition coefficient [0.9ml/g].'
52+
L = traits.Float(desc=desc, argstr='-L %f')
53+
desc = 'Labelling efficiency [0.99 (pasl), 0.85 (pcasl)], ensure any \
54+
background suppression pulses are included in -eff'
55+
eff = traits.Float(desc=desc, argstr='-eff %f')
56+
desc = 'Outlier rejection for multi CL volumes (enter z-score threshold \
57+
(e.g. 2.5)) [off].'
58+
out = traits.Float(desc=desc, argstr='-out %f')
59+
60+
# *** PCASL options (Choose those suitable for the model!):
61+
PLD = traits.Float(desc='Post Labelling Delay [2000ms].', argstr='-PLD %f')
62+
LDD = traits.Float(desc='Labelling Duration [1800ms].', argstr='-LDD %f')
63+
desc = 'Difference in labelling delay per slice [0.0 ms/slice.'
64+
dPLD = traits.Float(desc=desc, argstr='-dPLD %f')
65+
66+
# *** PASL options (Choose those suitable for the model!):
67+
Tinv1 = traits.Float(desc='Saturation pulse time [800ms].',
68+
argstr='-Tinv1 %f')
69+
Tinv2 = traits.Float(desc='Inversion time [2000ms].', argstr='-Tinv2 %f')
70+
desc = 'Difference in inversion time per slice [0ms/slice].'
71+
dTinv2 = traits.Float(desc=desc, argstr='-dTinv2 %f')
72+
73+
# *** Other experimental assumptions:
74+
75+
# Not programmed yet
76+
# desc = 'Slope and intercept for Arterial Transit Time.'
77+
# ATT = traits.Float(desc=desc, argstr='-ATT %f')
78+
79+
gmT1 = traits.Float(desc='T1 of GM [1150ms].', argstr='-gmT1 %f')
80+
gmL = traits.Float(desc='Plasma/GM water partition [0.95ml/g].',
81+
argstr='-gmL %f')
82+
gmTTT = traits.Float(desc='Time to GM [ATT+0ms].', argstr='-gmTTT %f')
83+
wmT1 = traits.Float(desc='T1 of WM [800ms].', argstr='-wmT1 %f')
84+
wmL = traits.Float(desc='Plasma/WM water partition [0.82ml/g].',
85+
argstr='-wmL %f')
86+
wmTTT = traits.Float(desc='Time to WM [ATT+0ms].', argstr='-wmTTT %f')
87+
88+
# *** Segmentation options:
89+
desc = 'Filename of the 4D segmentation (in ASL space) for L/T1 \
90+
estimation and PV correction {WM,GM,CSF}.'
91+
seg = traits.File(exists=True, argstr='-seg %s', desc=desc)
92+
desc = 'Use sigmoid to estimate L from T1: L(T1|gmL,wmL) [Off].'
93+
sig = traits.Bool(desc=desc, argstr='-sig')
94+
desc = 'Simple PV correction (CBF=vg*CBFg + vw*CBFw, with CBFw=f*CBFg) \
95+
[0.25].'
96+
pv0 = traits.Int(desc=desc, argstr='-pv0 %d')
97+
pv2 = traits.Int(desc='In plane PV kernel size [3x3].', argstr='-pv2 %d')
98+
pv3 = traits.Tuple(traits.Int, traits.Int, traits.Int,
99+
desc='3D kernel size [3x3x1].',
100+
argstr='-pv3 %d %d %d')
101+
desc = 'Multiply CBF by this value (e.g. if CL are mislabelled use -1.0).'
102+
mul = traits.Float(desc=desc, argstr='-mul %f')
103+
mulgm = traits.Bool(desc='Multiply CBF by segmentation [Off].',
104+
argstr='-sig')
105+
desc = 'Set PV threshold for switching off LSQR [O.05].'
106+
pvthreshold = traits.Bool(desc=desc, argstr='-pvthreshold')
107+
segstyle = traits.Bool(desc='Set CBF as [gm,wm] not [wm,gm].',
108+
argstr='-segstyle')
109+
110+
111+
class FitAslOutputSpec(TraitedSpec):
112+
""" Output Spec for FitAsl. """
113+
desc = 'Filename of the Cerebral Blood Flow map (in ml/100g/min).'
114+
cbf_file = traits.File(exists=True, desc=desc)
115+
desc = 'Filename of the CBF error map.'
116+
error_file = traits.File(exists=True, desc=desc)
117+
desc = 'Filename of the synthetic ASL data.'
118+
syn_file = traits.File(exists=True, desc=desc)
119+
120+
121+
class FitAsl(NiftyFitCommand):
122+
"""Interface for executable fit_asl from Niftyfit platform.
123+
124+
Use NiftyFit to perform ASL fitting.
125+
126+
ASL fitting routines (following EU Cost Action White Paper recommendations)
127+
Fits Cerebral Blood Flow maps in the first instance.
128+
129+
For source code, see https://cmiclab.cs.ucl.ac.uk/CMIC/NiftyFit-Release
130+
131+
Examples
132+
--------
133+
>>> from nipype.interfaces.niftyfit import FitAsl
134+
>>> node = FitAsl()
135+
>>> node.inputs.source_file = "asl.nii.gz" # doctest: +SKIP
136+
>>> node.cmdline # doctest: +SKIP
137+
'fit_asl -source asl.nii -cbf asl_cbf.nii.gz -error asl_error.nii.gz \
138+
-syn asl_syn.nii.gz'
139+
140+
"""
141+
_cmd = get_custom_path('fit_asl')
142+
input_spec = FitAslInputSpec
143+
output_spec = FitAslOutputSpec
144+
_suffix = '_fit_asl'
145+
146+
def _gen_filename(self, name):
147+
if name == 'error_file':
148+
return self._gen_fname(self.inputs.source_file,
149+
suffix='_error', ext='.nii.gz')
150+
if name == 'syn_file':
151+
return self._gen_fname(self.inputs.source_file,
152+
suffix='_syn', ext='.nii.gz')
153+
if name == 'cbf_file':
154+
return self._gen_fname(self.inputs.source_file,
155+
suffix='_cbf', ext='.nii.gz')
156+
return None
157+
158+
def _list_outputs(self):
159+
outputs = self.output_spec().get()
160+
161+
if isdefined(self.inputs.error_file):
162+
outputs['error_file'] = self.inputs.error_file
163+
else:
164+
outputs['error_file'] = self._gen_filename('error_file')
165+
166+
if isdefined(self.inputs.syn_file):
167+
outputs['syn_file'] = self.inputs.syn_file
168+
else:
169+
outputs['syn_file'] = self._gen_filename('syn_file')
170+
171+
if isdefined(self.inputs.cbf_file):
172+
outputs['cbf_file'] = self.inputs.cbf_file
173+
else:
174+
outputs['cbf_file'] = self._gen_filename('cbf_file')
175+
176+
return outputs

nipype/interfaces/niftyfit/base.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
4+
"""
5+
The niftyfit module provide an interface with the niftyfit software
6+
developed in TIG, UCL.
7+
8+
Software available at:
9+
https://cmiclab.cs.ucl.ac.uk/CMIC/NiftyFit-Release
10+
11+
Version used for this version of the interfaces (git):
12+
13+
commit c6232e4c4223c3d19f7a32906409da5af36299a2
14+
Date: Fri Jan 6 13:34:02 2017 +0000
15+
16+
Examples
17+
--------
18+
See the docstrings of the individual classes for examples.
19+
"""
20+
21+
import os
22+
import warnings
23+
24+
from ..base import CommandLine
25+
from ...utils.filemanip import split_filename
26+
27+
28+
warn = warnings.warn
29+
warnings.filterwarnings('always', category=UserWarning)
30+
31+
32+
def get_custom_path(command):
33+
"""Get path of niftyfit."""
34+
try:
35+
specific_dir = os.environ['NIFTYFITDIR']
36+
command = os.path.join(specific_dir, command)
37+
return command
38+
except KeyError:
39+
return command
40+
41+
42+
def no_niftyfit(cmd='fit_dwi'):
43+
"""Check if niftyfit is installed."""
44+
if True in [os.path.isfile(os.path.join(path, cmd)) and
45+
os.access(os.path.join(path, cmd), os.X_OK)
46+
for path in os.environ["PATH"].split(os.pathsep)]:
47+
return False
48+
return True
49+
50+
51+
class NiftyFitCommand(CommandLine):
52+
"""
53+
Base support interface for NiftyFit commands.
54+
"""
55+
_suffix = '_nf'
56+
57+
def __init__(self, **inputs):
58+
""" Init method calling super. No version to be checked."""
59+
super(NiftyFitCommand, self).__init__(**inputs)
60+
61+
def _gen_fname(self, basename, out_dir=None, suffix=None, ext=None):
62+
if basename == '':
63+
msg = 'Unable to generate filename for command %s. ' % self.cmd
64+
msg += 'basename is not set!'
65+
raise ValueError(msg)
66+
_, final_bn, final_ext = split_filename(basename)
67+
if out_dir is None:
68+
out_dir = os.getcwd()
69+
if ext is not None:
70+
final_ext = ext
71+
if suffix is not None:
72+
final_bn = ''.join((final_bn, suffix))
73+
return os.path.abspath(os.path.join(out_dir, final_bn + final_ext))

0 commit comments

Comments
 (0)