|
| 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 | +Change directory to provide relative paths for doctests |
| 8 | + >>> import os |
| 9 | + >>> filepath = os.path.dirname( os.path.realpath( __file__ ) ) |
| 10 | + >>> datadir = os.path.realpath(os.path.join(filepath, '../../testing/\ |
| 11 | +data')) |
| 12 | + >>> os.chdir(datadir) |
| 13 | +""" |
| 14 | + |
| 15 | +from ..base import TraitedSpec, traits, CommandLineInputSpec |
| 16 | +from .base import NiftyFitCommand |
| 17 | +from ..niftyreg.base import get_custom_path |
| 18 | + |
| 19 | + |
| 20 | +class FitAslInputSpec(CommandLineInputSpec): |
| 21 | + """ Input Spec for FitAsl. """ |
| 22 | + desc = 'Filename of the 4D ASL (control/label) source image (mandatory).' |
| 23 | + source_file = traits.File(position=1, |
| 24 | + exists=True, |
| 25 | + argstr='-source %s', |
| 26 | + mandatory=True, |
| 27 | + desc=desc) |
| 28 | + pasl = traits.Bool(desc='Fit PASL ASL data [default]', argstr='-pasl') |
| 29 | + pcasl = traits.Bool(desc='Fit PCASL ASL data', argstr='-pcasl') |
| 30 | + |
| 31 | + # *** Output options: |
| 32 | + desc = 'Filename of the Cerebral Blood Flow map (in ml/100g/min).' |
| 33 | + cbf_file = traits.File(name_source=['source_file'], |
| 34 | + name_template='%s_cbf.nii.gz', |
| 35 | + argstr='-cbf %s', desc=desc) |
| 36 | + error_file = traits.File(name_source=['source_file'], |
| 37 | + name_template='%s_error.nii.gz', |
| 38 | + argstr='-error %s', |
| 39 | + desc='Filename of the CBF error map.') |
| 40 | + syn_file = traits.File(name_source=['source_file'], |
| 41 | + name_template='%s_syn.nii.gz', |
| 42 | + argstr='-syn %s', |
| 43 | + desc='Filename of the synthetic ASL data.') |
| 44 | + |
| 45 | + # *** Input options (see also fit_qt1 for generic T1 fitting): |
| 46 | + desc = 'Filename of the estimated input T1 map (in ms).' |
| 47 | + t1map = traits.File(exists=True, argstr='-t1map %s', desc=desc) |
| 48 | + desc = 'Filename of the estimated input M0 map.' |
| 49 | + m0map = traits.File(exists=True, argstr='-m0map %s', desc=desc) |
| 50 | + desc = 'Filename of the estimated input M0 map error.' |
| 51 | + m0mape = traits.File(exists=True, argstr='-m0mape %s', desc=desc) |
| 52 | + desc = 'Filename of a [1,2,5]s Inversion Recovery volume (T1/M0 fitting \ |
| 53 | +carried out internally).' |
| 54 | + ir_volume = traits.File(exists=True, argstr='-IRvolume %s', desc=desc) |
| 55 | + desc = 'Output of [1,2,5]s Inversion Recovery fitting.' |
| 56 | + ir_output = traits.File(exists=True, argstr='-IRoutput %s', desc=desc) |
| 57 | + |
| 58 | + # *** Experimental options (Choose those suitable for the model!): |
| 59 | + mask = traits.File(position=2, |
| 60 | + exists=True, |
| 61 | + desc='Filename of image mask.', |
| 62 | + argstr='-mask %s') |
| 63 | + t1_art_cmp = traits.Float(desc='T1 of arterial component [1650ms].', |
| 64 | + argstr='-T1a %f') |
| 65 | + desc = 'Single plasma/tissue partition coefficient [0.9ml/g].' |
| 66 | + plasma_coeff = traits.Float(desc=desc, argstr='-L %f') |
| 67 | + desc = 'Labelling efficiency [0.99 (pasl), 0.85 (pcasl)], ensure any \ |
| 68 | +background suppression pulses are included in -eff' |
| 69 | + eff = traits.Float(desc=desc, argstr='-eff %f') |
| 70 | + desc = 'Outlier rejection for multi CL volumes (enter z-score threshold \ |
| 71 | +(e.g. 2.5)) [off].' |
| 72 | + out = traits.Float(desc=desc, argstr='-out %f') |
| 73 | + |
| 74 | + # *** PCASL options (Choose those suitable for the model!): |
| 75 | + pld = traits.Float(desc='Post Labelling Delay [2000ms].', argstr='-PLD %f') |
| 76 | + ldd = traits.Float(desc='Labelling Duration [1800ms].', argstr='-LDD %f') |
| 77 | + desc = 'Difference in labelling delay per slice [0.0 ms/slice.' |
| 78 | + dpld = traits.Float(desc=desc, argstr='-dPLD %f') |
| 79 | + |
| 80 | + # *** PASL options (Choose those suitable for the model!): |
| 81 | + t_inv1 = traits.Float(desc='Saturation pulse time [800ms].', |
| 82 | + argstr='-Tinv1 %f') |
| 83 | + t_inv2 = traits.Float(desc='Inversion time [2000ms].', argstr='-Tinv2 %f') |
| 84 | + desc = 'Difference in inversion time per slice [0ms/slice].' |
| 85 | + dt_inv2 = traits.Float(desc=desc, argstr='-dTinv2 %f') |
| 86 | + |
| 87 | + # *** Other experimental assumptions: |
| 88 | + |
| 89 | + # Not programmed yet |
| 90 | + # desc = 'Slope and intercept for Arterial Transit Time.' |
| 91 | + # ATT = traits.Float(desc=desc, argstr='-ATT %f') |
| 92 | + |
| 93 | + gm_t1 = traits.Float(desc='T1 of GM [1150ms].', argstr='-gmT1 %f') |
| 94 | + gm_plasma = traits.Float(desc='Plasma/GM water partition [0.95ml/g].', |
| 95 | + argstr='-gmL %f') |
| 96 | + gm_ttt = traits.Float(desc='Time to GM [ATT+0ms].', argstr='-gmTTT %f') |
| 97 | + wm_t1 = traits.Float(desc='T1 of WM [800ms].', argstr='-wmT1 %f') |
| 98 | + wm_plasma = traits.Float(desc='Plasma/WM water partition [0.82ml/g].', |
| 99 | + argstr='-wmL %f') |
| 100 | + wm_ttt = traits.Float(desc='Time to WM [ATT+0ms].', argstr='-wmTTT %f') |
| 101 | + |
| 102 | + # *** Segmentation options: |
| 103 | + desc = 'Filename of the 4D segmentation (in ASL space) for L/T1 \ |
| 104 | +estimation and PV correction {WM,GM,CSF}.' |
| 105 | + seg = traits.File(exists=True, argstr='-seg %s', desc=desc) |
| 106 | + desc = 'Use sigmoid to estimate L from T1: L(T1|gmL,wmL) [Off].' |
| 107 | + sig = traits.Bool(desc=desc, argstr='-sig') |
| 108 | + desc = 'Simple PV correction (CBF=vg*CBFg + vw*CBFw, with CBFw=f*CBFg) \ |
| 109 | +[0.25].' |
| 110 | + pv0 = traits.Int(desc=desc, argstr='-pv0 %d') |
| 111 | + pv2 = traits.Int(desc='In plane PV kernel size [3x3].', argstr='-pv2 %d') |
| 112 | + pv3 = traits.Tuple(traits.Int, traits.Int, traits.Int, |
| 113 | + desc='3D kernel size [3x3x1].', |
| 114 | + argstr='-pv3 %d %d %d') |
| 115 | + desc = 'Multiply CBF by this value (e.g. if CL are mislabelled use -1.0).' |
| 116 | + mul = traits.Float(desc=desc, argstr='-mul %f') |
| 117 | + mulgm = traits.Bool(desc='Multiply CBF by segmentation [Off].', |
| 118 | + argstr='-sig') |
| 119 | + desc = 'Set PV threshold for switching off LSQR [O.05].' |
| 120 | + pv_threshold = traits.Bool(desc=desc, argstr='-pvthreshold') |
| 121 | + segstyle = traits.Bool(desc='Set CBF as [gm,wm] not [wm,gm].', |
| 122 | + argstr='-segstyle') |
| 123 | + |
| 124 | + |
| 125 | +class FitAslOutputSpec(TraitedSpec): |
| 126 | + """ Output Spec for FitAsl. """ |
| 127 | + desc = 'Filename of the Cerebral Blood Flow map (in ml/100g/min).' |
| 128 | + cbf_file = traits.File(exists=True, desc=desc) |
| 129 | + desc = 'Filename of the CBF error map.' |
| 130 | + error_file = traits.File(exists=True, desc=desc) |
| 131 | + desc = 'Filename of the synthetic ASL data.' |
| 132 | + syn_file = traits.File(exists=True, desc=desc) |
| 133 | + |
| 134 | + |
| 135 | +class FitAsl(NiftyFitCommand): |
| 136 | + """Interface for executable fit_asl from Niftyfit platform. |
| 137 | +
|
| 138 | + Use NiftyFit to perform ASL fitting. |
| 139 | +
|
| 140 | + ASL fitting routines (following EU Cost Action White Paper recommendations) |
| 141 | + Fits Cerebral Blood Flow maps in the first instance. |
| 142 | +
|
| 143 | + `Source code <https://cmiclab.cs.ucl.ac.uk/CMIC/NiftyFit-Release>`_ |
| 144 | +
|
| 145 | + Examples |
| 146 | + -------- |
| 147 | + >>> from nipype.interfaces import niftyfit |
| 148 | + >>> node = niftyfit.FitAsl() |
| 149 | + >>> node.inputs.source_file = 'asl.nii.gz' |
| 150 | + >>> node.cmdline # doctest: +ALLOW_UNICODE |
| 151 | + 'fit_asl -source asl.nii.gz -cbf asl_cbf.nii.gz -error asl_error.nii.gz \ |
| 152 | +-syn asl_syn.nii.gz' |
| 153 | +
|
| 154 | + """ |
| 155 | + _cmd = get_custom_path('fit_asl', env_dir='NIFTYFITDIR') |
| 156 | + input_spec = FitAslInputSpec |
| 157 | + output_spec = FitAslOutputSpec |
| 158 | + _suffix = '_fit_asl' |
0 commit comments