|
| 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 |
0 commit comments