Skip to content

Commit 0e1b497

Browse files
committed
added SPM12's new Normalize routine
1 parent 9ee5775 commit 0e1b497

File tree

3 files changed

+160
-4
lines changed

3 files changed

+160
-4
lines changed

nipype/interfaces/spm/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
from .base import (Info, SPMCommand, logger, no_spm, scans_for_fname,
66
scans_for_fnames)
7-
from .preprocess import (SliceTiming, Realign, Coregister, Normalize, Segment,
8-
Smooth, NewSegment, DARTEL, DARTELNorm2MNI,
7+
from .preprocess import (SliceTiming, Realign, Coregister, Normalize, Normalize12,
8+
Segment, Smooth, NewSegment, DARTEL, DARTELNorm2MNI,
99
CreateWarped, VBMSegment)
1010
from .model import (Level1Design, EstimateModel, EstimateContrast, Threshold,
1111
OneSampleTTestDesign, TwoSampleTTestDesign,

nipype/interfaces/spm/preprocess.py

Lines changed: 145 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ class NormalizeInputSpec(SPMCommandInputSpec):
393393
desc='source smoothing (opt)')
394394
template_image_smoothing = traits.Float(field='eoptions.smoref',
395395
desc='template smoothing (opt)')
396-
affine_regularization_type = traits.Enum('mni', 'size', 'none', field='eoptions.regype',
396+
affine_regularization_type = traits.Enum('mni', 'size', 'none', field='eoptions.regtype',
397397
desc='mni, size, none (opt)')
398398
DCT_period_cutoff = traits.Float(field='eoptions.cutoff',
399399
desc='Cutoff of for DCT bases (opt)')
@@ -461,7 +461,7 @@ def _format_arg(self, opt, spec, val):
461461
return super(Normalize, self)._format_arg(opt, spec, val)
462462

463463
def _parse_inputs(self):
464-
"""validate spm realign options if set to None ignore
464+
"""validate spm normalize options if set to None ignore
465465
"""
466466
einputs = super(Normalize, self)._parse_inputs(skip=('jobtype',
467467
'apply_to_files'))
@@ -509,6 +509,149 @@ def _list_outputs(self):
509509
return outputs
510510

511511

512+
class Normalize12InputSpec(SPMCommandInputSpec):
513+
image_to_align = File(exists=True, field='subj.vol',
514+
desc='file to estimate the normalization parameters with',
515+
xor=['deformation_file'],
516+
mandatory=True, copyfile=True)
517+
apply_to_files = InputMultiPath(traits.Either(File(exists=True),
518+
traits.List(File(exists=True))),
519+
field='subj.resample',
520+
desc='files to apply transformation to',
521+
copyfile=True)
522+
deformation_file = File(field='subj.def', mandatory=True,
523+
xor=['image_to_align', 'tpm'],
524+
desc='file y_*.nii containing 3 deformation fields for the deformation in x, y and z dimension',
525+
copyfile=False)
526+
jobtype = traits.Enum('estwrite', 'est', 'write',
527+
desc='one of: est, write, estwrite (opt, estwrite)',
528+
usedefault=True)
529+
bias_regularization = traits.Enum(0, 0.00001, 0.0001, 0.001, 0.01, 0.1, 1, 10,
530+
field='eoptions.biasreg',
531+
desc='no(0) - extremely heavy (10) (opt)')
532+
bias_fwhm = traits.Enum(30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150,
533+
'Inf', field='eoptions.biasfwhm',
534+
desc='FWHM of Gaussian smoothness of bias (opt)')
535+
tpm = File(exists=True, field='eoptions.tpm',
536+
desc='template in form of a tissue probablitiy map to normalize to (opt)',
537+
mandatory=False, xor=['deformation_file'],
538+
copyfile=False)
539+
affine_regularization_type = traits.Enum('mni', 'size', 'none', field='eoptions.affreg',
540+
desc='mni, size, none (opt)')
541+
warping_regularization = traits.List(traits.Float(), field='eoptions.reg',
542+
minlen=5, maxlen=5,
543+
desc='Controls balance between parameters and data (opt)')
544+
smoothness = traits.Float(field='eoptions.fwhm',
545+
desc='value (in mm) to smooth the data before normalization (opt)')
546+
sampling_distance = traits.Float(field='eoptions.samp',
547+
desc='Sampling distance on data for parameter estimation (opt)')
548+
write_bounding_box = traits.List(traits.List(traits.Float(),
549+
minlen=3, maxlen=3),
550+
field='woptions.bb', minlen=2, maxlen=2,
551+
desc='3x2-element list of lists representing the bounding box (in mm) to be written (opt)')
552+
write_voxel_sizes = traits.List(traits.Float(), field='woptions.vox',
553+
minlen=3, maxlen=3,
554+
desc='3-element list representing the voxel sizes (in mm) of the written normalised images (opt)')
555+
write_interp = traits.Range(low=0, high=7, field='woptions.interp',
556+
desc='degree of b-spline used for interpolation (opt)')
557+
558+
559+
class Normalize12OutputSpec(TraitedSpec):
560+
deformation_field = OutputMultiPath(File(exists=True), desc='NIfTI file containing 3 deformation fields for the deformation in x, y and z dimension')
561+
normalized_image = OutputMultiPath(File(exists=True), desc='Normalized file that needed to be aligned')
562+
normalized_files = OutputMultiPath(File(exists=True), desc='Normalized other files')
563+
564+
565+
class Normalize12(SPMCommand):
566+
"""uses SPM12's new Normalise routine for warping an image to a template.
567+
Spatial normalisation is now done via the segmentation routine (which was
568+
known as ``New Segment`` in SPM8). Note that the normalisation in SPM12
569+
is done towards a file containing multiple tissue probability maps, which
570+
was not the cass in SPM8.
571+
572+
http://www.fil.ion.ucl.ac.uk/spm/doc/manual.pdf#page=49
573+
574+
Examples
575+
--------
576+
>>> import nipype.interfaces.spm as spm
577+
>>> norm12 = spm.Normalize12()
578+
>>> norm12.inputs.image_to_align = 'structural.nii'
579+
>>> norm12.inputs.apply_to_files = 'functional.nii'
580+
>>> norm12.run() # doctest: +SKIP
581+
582+
"""
583+
584+
input_spec = Normalize12InputSpec
585+
output_spec = Normalize12OutputSpec
586+
_jobtype = 'spatial'
587+
_jobname = 'normalise'
588+
589+
def _format_arg(self, opt, spec, val):
590+
"""Convert input to appropriate format for spm
591+
"""
592+
if opt == 'tpm':
593+
return scans_for_fname(filename_to_list(val))
594+
if opt == 'image_to_align':
595+
return scans_for_fname(filename_to_list(val))
596+
if opt == 'apply_to_files':
597+
return scans_for_fnames(filename_to_list(val))
598+
if opt == 'deformation_file':
599+
return np.array([list_to_filename(val)], dtype=object)
600+
if opt in ['nonlinear_regularization']:
601+
if len(val) != 5:
602+
raise ValueError('%s must have 5 elements' % opt)
603+
return super(Normalize12, self)._format_arg(opt, spec, val)
604+
605+
def _parse_inputs(self):
606+
"""validate spm normalize options if set to None ignore
607+
"""
608+
einputs = super(Normalize12, self)._parse_inputs(skip=('jobtype',
609+
'apply_to_files'))
610+
if isdefined(self.inputs.apply_to_files):
611+
inputfiles = deepcopy(self.inputs.apply_to_files)
612+
if isdefined(self.inputs.image_to_align):
613+
inputfiles.extend([self.inputs.image_to_align])
614+
einputs[0]['subj']['resample'] = scans_for_fnames(inputfiles)
615+
jobtype = self.inputs.jobtype
616+
if jobtype in ['estwrite', 'write']:
617+
if not isdefined(self.inputs.apply_to_files):
618+
if isdefined(self.inputs.image_to_align):
619+
einputs[0]['subj']['resample'] = scans_for_fname(self.inputs.image_to_align)
620+
return [{'%s' % (jobtype): einputs[0]}]
621+
622+
def _list_outputs(self):
623+
outputs = self._outputs().get()
624+
625+
jobtype = self.inputs.jobtype
626+
if jobtype.startswith('est'):
627+
outputs['deformation_field'] = []
628+
for imgf in filename_to_list(self.inputs.image_to_align):
629+
outputs['deformation_field'].append(fname_presuffix(imgf,prefix='y_'))
630+
outputs['deformation_field'] = list_to_filename(outputs['deformation_field'])
631+
632+
if self.inputs.jobtype == "estimate":
633+
if isdefined(self.inputs.apply_to_files):
634+
outputs['normalized_files'] = self.inputs.apply_to_files
635+
outputs['normalized_image'] = fname_presuffix(self.inputs.image_to_align,
636+
prefix='w')
637+
elif 'write' in self.inputs.jobtype:
638+
outputs['normalized_files'] = []
639+
if isdefined(self.inputs.apply_to_files):
640+
filelist = filename_to_list(self.inputs.apply_to_files)
641+
for f in filelist:
642+
if isinstance(f, list):
643+
run = [fname_presuffix(in_f,prefix='w') for in_f in f]
644+
else:
645+
run = [fname_presuffix(f,prefix='w')]
646+
outputs['normalized_files'].extend(run)
647+
if isdefined(self.inputs.image_to_align):
648+
outputs['normalized_image'] = fname_presuffix(self.inputs.image_to_align,
649+
prefix='w')
650+
651+
return outputs
652+
653+
654+
512655
class SegmentInputSpec(SPMCommandInputSpec):
513656
data = InputMultiPath(File(exists=True), field='data', desc='one scan per subject',
514657
copyfile=False, mandatory=True)

nipype/interfaces/spm/tests/test_preprocess.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,19 @@ def test_normalize_list_outputs():
8989
yield assert_true, norm._list_outputs()['normalized_files'][0].startswith('w')
9090
clean_directory(outdir, cwd)
9191

92+
def test_normalize12():
93+
yield assert_equal, spm.Normalize12._jobtype, 'spatial'
94+
yield assert_equal, spm.Normalize12._jobname, 'normalise'
95+
yield assert_equal, spm.Normalize12().inputs.jobtype, 'estwrite'
96+
97+
def test_normalize12_list_outputs():
98+
filelist, outdir, cwd = create_files_in_directory()
99+
norm12 = spm.Normalize12(image_to_align=filelist[0])
100+
yield assert_true, norm12._list_outputs()['normalized_image'][0].startswith('w')
101+
norm12 = spm.Normalize12(image_to_align=filelist[0],apply_to_files=filelist[1])
102+
yield assert_true, norm12._list_outputs()['normalized_files'][0].startswith('w')
103+
clean_directory(outdir, cwd)
104+
92105
@skipif(no_spm)
93106
def test_segment():
94107
if spm.Info.version()['name'] == "SPM12":

0 commit comments

Comments
 (0)