|
| 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 | + Change directory to provide relative paths for doctests |
| 5 | + >>> import os |
| 6 | + >>> filepath = os.path.dirname( os.path.realpath( __file__ ) ) |
| 7 | + >>> datadir = os.path.realpath(os.path.join(filepath, '../testing/data')) |
| 8 | + >>> os.chdir(datadir) |
| 9 | +
|
| 10 | +Nipype interface for PETPVC. |
| 11 | +
|
| 12 | +PETPVC is a software from the Nuclear Medicine Department |
| 13 | +of the UCL University Hospital, London, UK. |
| 14 | +
|
| 15 | +Its source code is here: https://github.com/UCL/PETPVC |
| 16 | +
|
| 17 | +The methods that it implement are explained here: |
| 18 | +K. Erlandsson, I. Buvat, P. H. Pretorius, B. A. Thomas, and B. F. Hutton, |
| 19 | +"A review of partial volume correction techniques for emission tomography |
| 20 | +and their applications in neurology, cardiology and oncology," Phys. Med. |
| 21 | +Biol., vol. 57, no. 21, p. R119, 2012. |
| 22 | +
|
| 23 | +There is a publication waiting to be accepted for this software tool. |
| 24 | +
|
| 25 | +
|
| 26 | +Its command line help shows this: |
| 27 | +
|
| 28 | + -i --input < filename > |
| 29 | + = PET image file |
| 30 | + -o --output < filename > |
| 31 | + = Output file |
| 32 | + [ -m --mask < filename > ] |
| 33 | + = Mask image file |
| 34 | + -p --pvc < keyword > |
| 35 | + = Desired PVC method |
| 36 | + -x < X > |
| 37 | + = The full-width at half maximum in mm along x-axis |
| 38 | + -y < Y > |
| 39 | + = The full-width at half maximum in mm along y-axis |
| 40 | + -z < Z > |
| 41 | + = The full-width at half maximum in mm along z-axis |
| 42 | + [ -d --debug ] |
| 43 | + = Prints debug information |
| 44 | + [ -n --iter [ Val ] ] |
| 45 | + = Number of iterations |
| 46 | + With: Val (Default = 10) |
| 47 | + [ -k [ Val ] ] |
| 48 | + = Number of deconvolution iterations |
| 49 | + With: Val (Default = 10) |
| 50 | + [ -a --alpha [ aval ] ] |
| 51 | + = Alpha value |
| 52 | + With: aval (Default = 1.5) |
| 53 | + [ -s --stop [ stopval ] ] |
| 54 | + = Stopping criterion |
| 55 | + With: stopval (Default = 0.01) |
| 56 | +
|
| 57 | +---------------------------------------------- |
| 58 | +Technique - keyword |
| 59 | +
|
| 60 | +Geometric transfer matrix - "GTM" |
| 61 | +Labbe approach - "LABBE" |
| 62 | +Richardson-Lucy - "RL" |
| 63 | +Van-Cittert - "VC" |
| 64 | +Region-based voxel-wise correction - "RBV" |
| 65 | +RBV with Labbe - "LABBE+RBV" |
| 66 | +RBV with Van-Cittert - "RBV+VC" |
| 67 | +RBV with Richardson-Lucy - "RBV+RL" |
| 68 | +RBV with Labbe and Van-Cittert - "LABBE+RBV+VC" |
| 69 | +RBV with Labbe and Richardson-Lucy- "LABBE+RBV+RL" |
| 70 | +Multi-target correction - "MTC" |
| 71 | +MTC with Labbe - "LABBE+MTC" |
| 72 | +MTC with Van-Cittert - "MTC+VC" |
| 73 | +MTC with Richardson-Lucy - "MTC+RL" |
| 74 | +MTC with Labbe and Van-Cittert - "LABBE+MTC+VC" |
| 75 | +MTC with Labbe and Richardson-Lucy- "LABBE+MTC+RL" |
| 76 | +Iterative Yang - "IY" |
| 77 | +Iterative Yang with Van-Cittert - "IY+VC" |
| 78 | +Iterative Yang with Richardson-Lucy - "IY+RL" |
| 79 | +Muller Gartner - "MG" |
| 80 | +Muller Gartner with Van-Cittert - "MG+VC" |
| 81 | +Muller Gartner with Richardson-Lucy - "MG+RL" |
| 82 | +
|
| 83 | +""" |
| 84 | +from __future__ import print_function |
| 85 | +from __future__ import division |
| 86 | + |
| 87 | +import os |
| 88 | +import warnings |
| 89 | + |
| 90 | +from nipype.interfaces.base import ( |
| 91 | + TraitedSpec, |
| 92 | + CommandLineInputSpec, |
| 93 | + CommandLine, |
| 94 | + File, |
| 95 | + isdefined, |
| 96 | + traits, |
| 97 | +) |
| 98 | + |
| 99 | +warn = warnings.warn |
| 100 | + |
| 101 | +pvc_methods = ['GTM', |
| 102 | + 'IY', |
| 103 | + 'IY+RL', |
| 104 | + 'IY+VC', |
| 105 | + 'LABBE', |
| 106 | + 'LABBE+MTC', |
| 107 | + 'LABBE+MTC+RL', |
| 108 | + 'LABBE+MTC+VC', |
| 109 | + 'LABBE+RBV', |
| 110 | + 'LABBE+RBV+RL', |
| 111 | + 'LABBE+RBV+VC', |
| 112 | + 'MG', |
| 113 | + 'MG+RL', |
| 114 | + 'MG+VC', |
| 115 | + 'MTC', |
| 116 | + 'MTC+RL', |
| 117 | + 'MTC+VC', |
| 118 | + 'RBV', |
| 119 | + 'RBV+RL', |
| 120 | + 'RBV+VC', |
| 121 | + 'RL', |
| 122 | + 'VC'] |
| 123 | + |
| 124 | + |
| 125 | +class PETPVCInputSpec(CommandLineInputSpec): |
| 126 | + in_file = File(desc="PET image file", exists=True, mandatory=True, argstr="-i %s") |
| 127 | + out_file = File(desc="Output file", genfile=True, hash_files=False, argstr="-o %s") |
| 128 | + mask_file = File(desc="Mask image file", exists=True, mandatory=True, argstr="-m %s") |
| 129 | + pvc = traits.Enum(pvc_methods, desc="Desired PVC method", mandatory=True, argstr="-p %s") |
| 130 | + fwhm_x = traits.Float(desc="The full-width at half maximum in mm along x-axis", mandatory=True, argstr="-x %.4f") |
| 131 | + fwhm_y = traits.Float(desc="The full-width at half maximum in mm along y-axis", mandatory=True, argstr="-y %.4f") |
| 132 | + fwhm_z = traits.Float(desc="The full-width at half maximum in mm along z-axis", mandatory=True, argstr="-z %.4f") |
| 133 | + debug = traits.Bool (desc="Prints debug information", usedefault=True, default_value=False, argstr="-d") |
| 134 | + n_iter = traits.Int (desc="Number of iterations", default_value=10, argstr="-n %d") |
| 135 | + n_deconv = traits.Int (desc="Number of deconvolution iterations", default_value=10, argstr="-k %d") |
| 136 | + alpha = traits.Float(desc="Alpha value", default_value=1.5, argstr="-a %.4f") |
| 137 | + stop_crit = traits.Float(desc="Stopping criterion", default_value=0.01, argstr="-a %.4f") |
| 138 | + |
| 139 | + |
| 140 | +class PETPVCOutputSpec(TraitedSpec): |
| 141 | + out_file = File(desc = "Output file") |
| 142 | + |
| 143 | + |
| 144 | +class PETPVC(CommandLine): |
| 145 | + """ Use PETPVC for partial volume correction of PET images. |
| 146 | +
|
| 147 | + Examples |
| 148 | + -------- |
| 149 | + >>> from ..testing import example_data |
| 150 | + >>> #TODO get data for PETPVC |
| 151 | + >>> pvc = PETPVC() |
| 152 | + >>> pvc.inputs.in_file = 'pet.nii.gz' |
| 153 | + >>> pvc.inputs.mask_file = 'tissues.nii.gz' |
| 154 | + >>> pvc.inputs.out_file = 'pet_pvc_rbv.nii.gz' |
| 155 | + >>> pvc.inputs.pvc = 'RBV' |
| 156 | + >>> pvc.inputs.fwhm_x = 2.0 |
| 157 | + >>> pvc.inputs.fwhm_y = 2.0 |
| 158 | + >>> pvc.inputs.fwhm_z = 2.0 |
| 159 | + >>> outs = pvc.run() #doctest: +SKIP |
| 160 | + """ |
| 161 | + input_spec = PETPVCInputSpec |
| 162 | + output_spec = PETPVCOutputSpec |
| 163 | + _cmd = 'petpvc' |
| 164 | + |
| 165 | + def _list_outputs(self): |
| 166 | + outputs = self.output_spec().get() |
| 167 | + outputs['out_file'] = self.inputs.out_file |
| 168 | + if not isdefined(outputs['out_file']): |
| 169 | + method_name = self.inputs.pvc.lower() |
| 170 | + outputs['out_file'] = self._gen_fname(self.inputs.in_file, |
| 171 | + suffix='_{}_pvc'.format(method_name)) |
| 172 | + |
| 173 | + outputs['out_file'] = os.path.abspath(outputs['out_file']) |
| 174 | + return outputs |
| 175 | + |
| 176 | + def _gen_fname(self, basename, cwd=None, suffix=None, change_ext=True, |
| 177 | + ext='.nii.gz'): |
| 178 | + """Generate a filename based on the given parameters. |
| 179 | +
|
| 180 | + The filename will take the form: cwd/basename<suffix><ext>. |
| 181 | + If change_ext is True, it will use the extentions specified in |
| 182 | + <instance>intputs.output_type. |
| 183 | +
|
| 184 | + Parameters |
| 185 | + ---------- |
| 186 | + basename : str |
| 187 | + Filename to base the new filename on. |
| 188 | + cwd : str |
| 189 | + Path to prefix to the new filename. (default is os.getcwd()) |
| 190 | + suffix : str |
| 191 | + Suffix to add to the `basename`. (defaults is '' ) |
| 192 | + change_ext : bool |
| 193 | + Flag to change the filename extension to the given `ext`. |
| 194 | + (Default is False) |
| 195 | +
|
| 196 | + Returns |
| 197 | + ------- |
| 198 | + fname : str |
| 199 | + New filename based on given parameters. |
| 200 | +
|
| 201 | + """ |
| 202 | + from nipype.utils.filemanip import fname_presuffix |
| 203 | + |
| 204 | + if basename == '': |
| 205 | + msg = 'Unable to generate filename for command %s. ' % self.cmd |
| 206 | + msg += 'basename is not set!' |
| 207 | + raise ValueError(msg) |
| 208 | + if cwd is None: |
| 209 | + cwd = os.getcwd() |
| 210 | + if change_ext: |
| 211 | + if suffix: |
| 212 | + suffix = ''.join((suffix, ext)) |
| 213 | + else: |
| 214 | + suffix = ext |
| 215 | + if suffix is None: |
| 216 | + suffix = '' |
| 217 | + fname = fname_presuffix(basename, suffix=suffix, |
| 218 | + use_ext=False, newpath=cwd) |
| 219 | + return fname |
| 220 | + |
| 221 | + def _gen_filename(self, name): |
| 222 | + if name == 'out_file': |
| 223 | + return self._list_outputs()['out_file'] |
| 224 | + return None |
0 commit comments