Skip to content

Commit b52175b

Browse files
committed
Merge remote-tracking branch 'upstream/master' into enh/restingconn
* upstream/master: ENH: Improve the SGE job names fix fixed bug remove duplicated input [Freesurfer] Improved Tkregister2 multiline description changed to tuple sty: white space fix: traits specifications for antsCT tst: revert tests antsCorticalThickness template + test files + merged upstream new interface for antsCorticalThickness + test files antsCorticalThickness interface + example data for testing New interface for antsCorticalThickness.sh script
2 parents f2fd48b + 7afedad commit b52175b

12 files changed

+314
-24
lines changed

nipype/interfaces/ants/segmentation.py

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,3 +334,220 @@ def _list_outputs(self):
334334
if self.inputs.save_bias or isdefined(self.inputs.bias_image):
335335
outputs['bias_image'] = os.path.abspath(self._gen_filename('bias_image'))
336336
return outputs
337+
338+
class antsCorticalThicknessInputSpec(ANTSCommandInputSpec):
339+
dimension=traits.Enum(3, 2, argstr= '-d %d', usedefault=True,
340+
desc='image dimension (2 or 3)')
341+
anatomical_image=File(exists=True,
342+
argstr='-a %s',
343+
desc=('Structural *intensity* image, typically T1.'
344+
'If more than one anatomical image is specified,'
345+
'subsequently specified images are used during the'
346+
'segmentation process. However, only the first'
347+
'image is used in the registration of priors.'
348+
'Our suggestion would be to specify the T1'
349+
'as the first image.'),
350+
mandatory=True)
351+
brain_template=File(exists=True,
352+
argstr='-e %s',
353+
desc=('Anatomical *intensity* template (possibly created using a'
354+
'population data set with buildtemplateparallel.sh in ANTs).'
355+
'This template is *not* skull-stripped.'),
356+
mandatory=True)
357+
brain_probability_mask=File(exists=True,
358+
argstr='-m %s', desc='brain probability mask in template space',
359+
copyfile=False, mandatory=True)
360+
segmentation_priors = InputMultiPath(File(exists=True),
361+
argstr='-p %s', mandatory=True)
362+
out_prefix = traits.Str('antsCT_', argstr='-o %s', usedefault=True,
363+
desc=('Prefix that is prepended to all output'
364+
' files (default = antsCT_)'))
365+
image_suffix=traits.Str('nii.gz', desc=('any of standard ITK formats,'
366+
' nii.gz is default'), argstr='-s %s', usedefault=True)
367+
t1_registration_template = File(exists=True,
368+
desc = ('Anatomical *intensity* template'
369+
'(assumed to be skull-stripped). A common'
370+
'case would be where this would be the same'
371+
'template as specified in the -e option which'
372+
'is not skull stripped.'),
373+
argstr='-t %s',
374+
mandatory=True)
375+
extraction_registration_mask=File(exists=True, argstr='-f %s',
376+
desc=('Mask (defined in the template space) used during'
377+
' registration for brain extraction.'))
378+
keep_temporary_files=traits.Int(argstr='-k %d',
379+
desc='Keep brain extraction/segmentation warps, etc (default = 0).')
380+
max_iterations=traits.Int(argstr='-i %d',
381+
desc=('ANTS registration max iterations'
382+
'(default = 100x100x70x20)'))
383+
prior_segmentation_weight=traits.Float(argstr='-w %f',
384+
desc=('Atropos spatial prior *probability* weight for'
385+
'the segmentation'))
386+
segmentation_iterations=traits.Int(argstr='-n %d',
387+
desc=('N4 -> Atropos -> N4 iterations during segmentation'
388+
'(default = 3)'))
389+
posterior_formulation=traits.Str(argstr='-b %s',
390+
desc=('Atropos posterior formulation and whether or not'
391+
'to use mixture model proportions.'
392+
'''e.g 'Socrates[1]' (default) or 'Aristotle[1]'.'''
393+
'Choose the latter if you'
394+
'want use the distance priors (see also the -l option'
395+
'for label propagation control).'))
396+
use_floatingpoint_precision=traits.Enum(0, 1, argstr='-j %d',
397+
desc='Use floating point precision in registrations (default = 0)')
398+
use_random_seeding=traits.Enum(0, 1, argstr='-u %d',
399+
desc=('Use random number generated from system clock in Atropos'
400+
'(default = 1)'))
401+
b_spline_smoothing=traits.Bool(argstr='-v',
402+
desc=('Use B-spline SyN for registrations and B-spline'
403+
'exponential mapping in DiReCT.'))
404+
cortical_label_image=File(exists=True,
405+
desc='Cortical ROI labels to use as a prior for ATITH.')
406+
label_propagation=traits.Str(argstr='-l %s',
407+
desc=('Incorporate a distance prior one the posterior formulation. Should be'
408+
'''of the form 'label[lambda,boundaryProbability]' where label'''
409+
'is a value of 1,2,3,... denoting label ID. The label'
410+
'probability for anything outside the current label'
411+
' = boundaryProbability * exp( -lambda * distanceFromBoundary )'
412+
'Intuitively, smaller lambda values will increase the spatial capture'
413+
'range of the distance prior. To apply to all label values, simply omit'
414+
'specifying the label, i.e. -l [lambda,boundaryProbability].'))
415+
quick_registration=traits.Bool(argstr='-q 1',
416+
desc=('If = 1, use antsRegistrationSyNQuick.sh as the basis for registration'
417+
'during brain extraction, brain segmentation, and'
418+
'(optional) normalization to a template.'
419+
'Otherwise use antsRegistrationSyN.sh (default = 0).'))
420+
debug=traits.Bool(argstr='-z 1',
421+
desc=('If > 0, runs a faster version of the script.'
422+
'Only for testing. Implies -u 0.'
423+
'Requires single thread computation for complete reproducibility.'))
424+
425+
class antsCorticalThicknessoutputSpec(TraitedSpec):
426+
BrainExtractionMask=File(exists=True,
427+
desc= 'brain extraction mask')
428+
BrainSegmentation=File(exists=True,
429+
desc='brain segmentaion image')
430+
BrainSegmentationN4=File(exists=True,
431+
desc='N4 corrected image')
432+
BrainSegmentationPosteriorsCSF=File(exists=True,
433+
desc='CSF posterior probability image')
434+
BrainSegmentationPosteriorsGM=File(exists=True,
435+
desc='GM posterior probability image')
436+
BrainSegmentationPosteriorsWM=File(exists=True,
437+
desc='WM posterior probability image')
438+
BrainSegmentationPosteriorsDGM=File(exists=True,
439+
desc='DGM posterior probability image')
440+
CorticalThickness=File(exists=True,
441+
desc='cortical thickness file')
442+
TemplateToSubject1GenericAffine=File(exists=True,
443+
desc='Template to subject affine')
444+
TemplateToSubject0Warp=File(exists=True,
445+
desc='Template to subject warp')
446+
SubjectToTemplate1Warp=File(exists=True,
447+
desc='Template to subject inverse warp')
448+
SubjectToTemplate0GenericAffine=File(exists=True,
449+
desc='Template to subject inverse affine')
450+
TemplateToSubjectLogJacobian=File(exists=True,
451+
desc='Template to subject log jacobian')
452+
453+
class antsCorticalThickness(ANTSCommand):
454+
"""
455+
Examples
456+
--------
457+
>>> from nipype.interfaces.ants.segmentation import antsCorticalThickness
458+
>>> corticalthickness = antsCorticalThickness()
459+
>>> corticalthickness.inputs.dimension = 3
460+
>>> corticalthickness.inputs.anatomical_image ='T1.nii.gz'
461+
>>> corticalthickness.inputs.brain_template = 'study_template.nii.gz'
462+
>>> corticalthickness.inputs.brain_probability_mask ='ProbabilityMaskOfStudyTemplate.nii.gz'
463+
>>> corticalthickness.inputs.segmentation_priors = ['BrainSegmentationPrior01.nii.gz', 'BrainSegmentationPrior02.nii.gz', 'BrainSegmentationPrior03.nii.gz', 'BrainSegmentationPrior04.nii.gz']
464+
>>> corticalthickness.inputs.t1_registration_template = 'brain_study_template.nii.gz'
465+
>>> corticalthickness.cmdline
466+
'antsCorticalThickness.sh -a T1.nii.gz -m ProbabilityMaskOfStudyTemplate.nii.gz -e study_template.nii.gz -d 3 -s nii.gz -o antsCT_ -p BrainSegmentationPrior%02d.nii.gz -t brain_study_template.nii.gz'
467+
"""
468+
469+
input_spec = antsCorticalThicknessInputSpec
470+
output_spec = antsCorticalThicknessoutputSpec
471+
_cmd = 'antsCorticalThickness.sh'
472+
def _format_arg(self, opt, spec, val):
473+
if opt == 'anatomical_image':
474+
retval = '-a %s' %(val)
475+
return retval
476+
if opt == 'brain_template':
477+
retval = '-e %s' %(val)
478+
return retval
479+
if opt == 'brain_probability_mask':
480+
retval = '-m %s' %(val)
481+
return retval
482+
if opt == 'out_prefix':
483+
retval = '-o %s' %(val)
484+
return retval
485+
if opt == 't1_registration_template':
486+
retval = '-t %s' %(val)
487+
return retval
488+
if opt == 'segmentation_priors':
489+
_, _, ext = split_filename(self.inputs.segmentation_priors[0])
490+
retval = "-p BrainSegmentationPrior%02d"
491+
retval += ext
492+
return retval
493+
return super(ANTSCommand, self)._format_arg(opt, spec, val)
494+
495+
def _run_interface(self, runtime):
496+
priors_directory = os.path.join(os.getcwd(), "priors")
497+
if not os.path.exists(priors_directory):
498+
os.makedirs(priors_directory)
499+
_, _, ext = split_filename(self.inputs.segmentation_priors[0])
500+
for i, f in enumerate(self.inputs.segmentation_priors):
501+
target = os.path.join(priors_directory,
502+
'BrainSegmentationPrior%02d' % (i + 1) + ext)
503+
if not (os.path.exists(target) and os.path.realpath(target) == os.path.abspath(f)):
504+
copyfile(os.path.abspath(f), os.path.join(priors_directory,
505+
'BrainSegmentationPrior%02d' % (i + 1) + ext))
506+
runtime = super(antsCorticalThickness, self)._run_interface(runtime)
507+
return runtime
508+
509+
def _list_outputs(self):
510+
outputs = self._outputs().get()
511+
outputs['BrainExtractionMask'] = os.path.join(os.getcwd(),
512+
self.inputs.out_prefix +
513+
'BrainExtractionMask.'+
514+
self.inputs.image_suffix)
515+
outputs['BrainSegmentation'] = os.path.join(os.getcwd(),
516+
self.inputs.out_prefix +
517+
'BrainSegmentation.' +
518+
self.inputs.image_suffix)
519+
outputs['BrainSegmentationN4'] = os.path.join(os.getcwd(),
520+
self.inputs.out_prefix +
521+
'BrainSegmentation0N4.' +
522+
self.inputs.image_suffix)
523+
outputs['BrainSegmentationPosteriorsCSF'] = os.path.join(os.getcwd(),
524+
self.inputs.out_prefix +
525+
'BrainSegmentationPosteriors01.' +
526+
self.inputs.image_suffix)
527+
outputs['BrainSegmentationPosteriorsGM'] = os.path.join(os.getcwd(),
528+
'BrainSegmentationPosteriors02.' +
529+
self.inputs.image_suffix)
530+
outputs['BrainSegmentationPosteriorsWM'] = os.path.join(os.getcwd(),
531+
'BrainSegmentationPosteriors03.' +
532+
self.inputs.image_suffix)
533+
outputs['BrainSegmentationPosteriorsDGM'] = os.path.join(os.getcwd(),
534+
'BrainSegmentationPosteriors04.' +
535+
self.inputs.image_suffix)
536+
outputs['CorticalThickness'] = os.path.join(os.getcwd(),
537+
self.inputs.out_prefix + 'CorticalThickness.' +
538+
self.inputs.image_suffix)
539+
outputs['TemplateToSubject1GenericAffine'] = os.path.join(
540+
os.getcwd(),
541+
'TemplateToSubject1GenericAffine.mat')
542+
outputs['TemplateToSubject0Warp'] = os.path.join(os.getcwd(),
543+
self.inputs.out_prefix + 'TemplateToSubject0Warp.'+
544+
self.inputs.image_suffix)
545+
outputs['SubjectToTemplate1Warp'] = os.path.join(os.getcwd(),
546+
self.inputs.out_prefix + 'SubjectToTemplate1Warp' +
547+
self.inputs.image_suffix)
548+
outputs['SubjectToTemplate0GenericAffine'] = os.path.join(os.getcwd(),
549+
self.inputs.out_prefix + 'SubjectToTemplate0GenericAffine.mat')
550+
outputs['TemplateToSubjectLogJacobian'] = os.path.join(os.getcwd(),
551+
self.inputs.out_prefix + 'subjectToTemplateLogJacobian.'+
552+
self.inputs.image_suffix)
553+
return outputs

nipype/interfaces/freesurfer/tests/test_auto_Tkregister2.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,42 @@ def test_Tkregister2_inputs():
1010
),
1111
fsl_in_matrix=dict(argstr='--fsl %s',
1212
),
13+
fsl_out=dict(argstr='--fslregout %s',
14+
),
15+
fstal=dict(argstr='--fstal',
16+
xor=['target_image', 'moving_image'],
17+
),
18+
fstarg=dict(argstr='--fstarg',
19+
xor=['target_image'],
20+
),
1321
ignore_exception=dict(nohash=True,
1422
usedefault=True,
1523
),
1624
moving_image=dict(argstr='--mov %s',
1725
mandatory=True,
1826
),
27+
movscale=dict(argstr='--movscale %f',
28+
),
1929
noedit=dict(argstr='--noedit',
2030
usedefault=True,
2131
),
2232
reg_file=dict(argstr='--reg %s',
2333
mandatory=True,
24-
name_source='fsl',
25-
name_template='%s.dat',
34+
usedefault=True,
35+
),
36+
reg_header=dict(argstr='--regheader',
2637
),
2738
subject_id=dict(argstr='--s %s',
28-
mandatory=True,
2939
),
3040
subjects_dir=dict(),
41+
target_image=dict(argstr='--targ %s',
42+
xor=['fstarg'],
43+
),
3144
terminal_output=dict(mandatory=True,
3245
nohash=True,
3346
),
47+
xfm=dict(argstr='--xfm %s',
48+
),
3449
)
3550
inputs = Tkregister2.input_spec()
3651

@@ -39,7 +54,8 @@ def test_Tkregister2_inputs():
3954
yield assert_equal, getattr(inputs.traits()[key], metakey), value
4055

4156
def test_Tkregister2_outputs():
42-
output_map = dict(reg_file=dict(),
57+
output_map = dict(fsl_file=dict(),
58+
reg_file=dict(),
4359
)
4460
outputs = Tkregister2.output_spec()
4561

nipype/interfaces/freesurfer/utils.py

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,39 +1191,89 @@ class ExtractMainComponent(CommandLine):
11911191

11921192

11931193
class Tkregister2InputSpec(FSTraitedSpec):
1194+
target_image = File(exists=True, argstr="--targ %s",
1195+
xor=['fstarg'],
1196+
desc='target volume')
1197+
fstarg = traits.Bool(False, argstr='--fstarg',
1198+
xor=['target_image'],
1199+
desc='use subject\'s T1 as reference')
1200+
11941201
moving_image = File(exists=True, mandatory=True, argstr="--mov %s",
1195-
desc='moving volume')
1202+
desc='moving volume')
11961203
fsl_in_matrix = File(exists=True, argstr="--fsl %s",
1197-
desc='fsl-style registration input matrix')
1198-
subject_id = traits.String(argstr="--s %s", mandatory=True,
1204+
desc='fsl-style registration input matrix')
1205+
subject_id = traits.String(argstr="--s %s",
11991206
desc='freesurfer subject ID')
1200-
noedit = traits.Bool(True, argstr="--noedit", desc='do not open edit window (exit)', usedefault=True)
1201-
reg_file = File(name_template='%s.dat', name_source='fsl',
1202-
mandatory=True, argstr="--reg %s",
1203-
desc='freesurfer-style registration file')
1207+
noedit = traits.Bool(True, argstr="--noedit", usedefault=True,
1208+
desc='do not open edit window (exit)')
1209+
reg_file = File('register.dat', usedefault=True,
1210+
mandatory=True, argstr='--reg %s',
1211+
desc='freesurfer-style registration file')
1212+
reg_header = traits.Bool(False, argstr='--regheader',
1213+
desc='compute regstration from headers')
1214+
fstal = traits.Bool(False, argstr='--fstal',
1215+
xor=['target_image', 'moving_image'],
1216+
desc='set mov to be tal and reg to be tal xfm')
1217+
movscale = traits.Float(argstr='--movscale %f',
1218+
desc='adjust registration matrix to scale mov')
1219+
xfm = File(exists=True, argstr='--xfm %s',
1220+
desc='use a matrix in MNI coordinates as initial registration')
1221+
fsl_out = File(argstr='--fslregout %s',
1222+
desc='compute an FSL-compatible resgitration matrix')
12041223

12051224

12061225
class Tkregister2OutputSpec(TraitedSpec):
12071226
reg_file = File(exists=True, desc='freesurfer-style registration file')
1227+
fsl_file = File(desc='FSL-style registration file')
12081228

12091229

12101230
class Tkregister2(FSCommand):
1211-
"""Use tkregister2 without the manual editing stage to convert
1212-
FSL-style registration matrix (.mat) to FreeSurfer-style registration matrix (.dat)
1231+
"""
12131232
12141233
Examples
12151234
--------
12161235
1236+
Get transform matrix between orig (*tkRAS*) and native (*scannerRAS*)
1237+
coordinates in Freesurfer. Implements the first step of mapping surfaces
1238+
to native space in `this guide
1239+
<http://surfer.nmr.mgh.harvard.edu/fswiki/FsAnat-to-NativeAnat>`_.
1240+
1241+
>>> from nipype.interfaces.freesurfer import Tkregister2
1242+
>>> tk2 = Tkregister2(reg_file='T1_to_native.dat')
1243+
>>> tk2.inputs.moving_image = 'T1.mgz'
1244+
>>> tk2.inputs.target_image = 'structural.nii'
1245+
>>> tk2.inputs.reg_header = True
1246+
>>> tk2.cmdline
1247+
'tkregister2 --mov T1.mgz --noedit --reg T1_to_native.dat --regheader \
1248+
--targ structural.nii'
1249+
>>> tk2.run() # doctest: +SKIP
1250+
1251+
The example below uses tkregister2 without the manual editing
1252+
stage to convert FSL-style registration matrix (.mat) to
1253+
FreeSurfer-style registration matrix (.dat)
1254+
12171255
>>> from nipype.interfaces.freesurfer import Tkregister2
1218-
>>> tk2 = Tkregister2(reg_file='register.dat')
1256+
>>> tk2 = Tkregister2()
12191257
>>> tk2.inputs.moving_image = 'epi.nii'
12201258
>>> tk2.inputs.fsl_in_matrix = 'flirt.mat'
1221-
>>> tk2.inputs.subject_id = 'test_subject'
1259+
>>> tk2.cmdline
1260+
'tkregister2 --fsl flirt.mat --mov epi.nii --noedit --reg register.dat'
12221261
>>> tk2.run() # doctest: +SKIP
12231262
"""
12241263
_cmd = "tkregister2"
12251264
input_spec = Tkregister2InputSpec
12261265
output_spec = Tkregister2OutputSpec
1227-
12281266

1229-
1267+
def _list_outputs(self):
1268+
outputs = self._outputs().get()
1269+
outputs['reg_file'] = os.path.abspath(self.inputs.reg_file)
1270+
if isdefined(self.inputs.fsl_out):
1271+
outputs['fsl_file'] = op.abspath(self.inputs.fsl_out)
1272+
return outputs
1273+
1274+
def _gen_outfilename(self):
1275+
if isdefined(self.inputs.out_file):
1276+
return os.path.abspath(self.inputs.out_file)
1277+
else:
1278+
_, name, ext = split_filename(self.inputs.in_file)
1279+
return os.path.abspath(name + '_smoothed' + ext)

0 commit comments

Comments
 (0)