Skip to content

Commit b7e3fcc

Browse files
authored
Merge pull request #415 from mgxd/enh/mni-reg
ENH: Add flag for multi-step registration to adult templates
2 parents b9efa6f + 63afae1 commit b7e3fcc

File tree

11 files changed

+652
-58
lines changed

11 files changed

+652
-58
lines changed

nibabies/cli/parser.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,12 @@ def _str_none(val):
758758
action='store_true',
759759
help='Replace low intensity voxels in CSF mask with average',
760760
)
761+
g_baby.add_argument(
762+
'--multi-step-reg',
763+
action='store_true',
764+
help='For certain adult templates (MNI152NLin6Asym), perform two step '
765+
'registrations (native -> MNIInfant -> template) and concatenate into a single xfm',
766+
)
761767
return parser
762768

763769

nibabies/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,9 @@ class workflow(_Config):
578578
"""Run FreeSurfer ``recon-all`` with the ``-logitudinal`` flag."""
579579
medial_surface_nan = None
580580
"""Fill medial surface with :abbr:`NaNs (not-a-number)` when sampling."""
581+
multi_step_reg = False
582+
"""Perform multiple registrations (native -> MNIInfant -> template) and concatenate into a
583+
single transform"""
581584
norm_csf = False
582585
"""Replace low intensity voxels in CSF mask with average."""
583586
project_goodvoxels = False

nibabies/data/xfm_manifest.json

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
{
2+
"from-MNI152NLin6Asym_to-MNIInfant+10_xfm.h5": {
3+
"url": "https://osf.io/download/jf6vz/",
4+
"hash": "md5:df6d40e5bbdca85f083866ad26507796"
5+
},
6+
"from-MNI152NLin6Asym_to-MNIInfant+11_xfm.h5": {
7+
"url": "https://osf.io/download/zmjyn/",
8+
"hash": "md5:ff8435f06c0a44be88e050492b90ffea"
9+
},
10+
"from-MNI152NLin6Asym_to-MNIInfant+1_xfm.h5": {
11+
"url": "https://osf.io/download/kx7ny/",
12+
"hash": "md5:c27d35dff75d59d605c8d786c985594e"
13+
},
14+
"from-MNI152NLin6Asym_to-MNIInfant+2_xfm.h5": {
15+
"url": "https://osf.io/download/6758aa0c67a7782b00f73c77/",
16+
"hash": "md5:81fabdc70c200bf099893bd1377ef0f7"
17+
},
18+
"from-MNI152NLin6Asym_to-MNIInfant+3_xfm.h5": {
19+
"url": "https://osf.io/download/6758aa1c76bfbc22cbf73b0a/",
20+
"hash": "md5:c8f5b79b95f9aa65add5524e88601cc6"
21+
},
22+
"from-MNI152NLin6Asym_to-MNIInfant+4_xfm.h5": {
23+
"url": "https://osf.io/download/6758aa1bb96fd819c41e25a4/",
24+
"hash": "md5:1e4b927115a76b031c46e6180fc76a30"
25+
},
26+
"from-MNI152NLin6Asym_to-MNIInfant+5_xfm.h5": {
27+
"url": "https://osf.io/download/6758aa146e0cd8ca5f563b2b/",
28+
"hash": "md5:25bfd0837a88db267762974c0a530535"
29+
},
30+
"from-MNI152NLin6Asym_to-MNIInfant+6_xfm.h5": {
31+
"url": "https://osf.io/download/6758ab50b95a2e75b11e23f0/",
32+
"hash": "md5:7ed4732832ed6dd45dd2259d8b4454e7"
33+
},
34+
"from-MNI152NLin6Asym_to-MNIInfant+7_xfm.h5": {
35+
"url": "https://osf.io/download/6758ab5667a7782b00f73cfa/",
36+
"hash": "md5:a1244c38b7b4825abefc5834d0398b08"
37+
},
38+
"from-MNI152NLin6Asym_to-MNIInfant+8_xfm.h5": {
39+
"url": "https://osf.io/download/rq2an/",
40+
"hash": "md5:50d11fdac22c6589af8a7f61e4b3e41a"
41+
},
42+
"from-MNI152NLin6Asym_to-MNIInfant+9_xfm.h5": {
43+
"url": "https://osf.io/download/6758ab67eacdd8b34803d991/",
44+
"hash": "md5:3184d91f8b3a386ca3ec913c365651d8"
45+
},
46+
"from-MNIInfant+10_to-MNI152NLin6Asym_xfm.h5": {
47+
"url": "https://osf.io/download/4xh9q/",
48+
"hash": "md5:a1f3dd3c0ac8b05efbaf893cca6f9641"
49+
},
50+
"from-MNIInfant+11_to-MNI152NLin6Asym_xfm.h5": {
51+
"url": "https://osf.io/download/6758a5026e0cd8ca5f56380d/",
52+
"hash": "md5:0d1aadef884574e54065d4e2cdb8e398"
53+
},
54+
"from-MNIInfant+1_to-MNI152NLin6Asym_xfm.h5": {
55+
"url": "https://osf.io/download/7ge2b/",
56+
"hash": "md5:d5e4272140c6f582f64b7f39b31ca837"
57+
},
58+
"from-MNIInfant+2_to-MNI152NLin6Asym_xfm.h5": {
59+
"url": "https://osf.io/download/6758a3c1a678894ad71e2422/",
60+
"hash": "md5:b03651dae4d378410c44f1c6c63dbea0"
61+
},
62+
"from-MNIInfant+3_to-MNI152NLin6Asym_xfm.h5": {
63+
"url": "https://osf.io/download/6758a3c3bc61ce5912662055/",
64+
"hash": "md5:7cc099e26647e670c8e75ead2cfe39a6"
65+
},
66+
"from-MNIInfant+4_to-MNI152NLin6Asym_xfm.h5": {
67+
"url": "https://osf.io/download/6758a3bc040c053b58f73b47/",
68+
"hash": "md5:e92e9150f2ad4d2730f005aa9750438d"
69+
},
70+
"from-MNIInfant+5_to-MNI152NLin6Asym_xfm.h5": {
71+
"url": "https://osf.io/download/6758a3bdea7294dbdd66161a/",
72+
"hash": "md5:9cf6cf3fb500c229da15490c9080201a"
73+
},
74+
"from-MNIInfant+6_to-MNI152NLin6Asym_xfm.h5": {
75+
"url": "https://osf.io/download/6758a3bf040c053b58f73b4b/",
76+
"hash": "md5:2212fdb57b85e8a0f7fa9feea5b0dd1b"
77+
},
78+
"from-MNIInfant+7_to-MNI152NLin6Asym_xfm.h5": {
79+
"url": "https://osf.io/download/6758a4f78af26d0a97661ca9/",
80+
"hash": "md5:6913f8191201350311ff61525fae8a21"
81+
},
82+
"from-MNIInfant+8_to-MNI152NLin6Asym_xfm.h5": {
83+
"url": "https://osf.io/download/6758a500f82c189df71e256f/",
84+
"hash": "md5:809455af8416cd61c1693b5c7eafbd13"
85+
},
86+
"from-MNIInfant+9_to-MNI152NLin6Asym_xfm.h5": {
87+
"url": "https://osf.io/download/6758a4ff040c053b58f73bd1/",
88+
"hash": "md5:49317cbb038c399d4df7428f07d36983"
89+
}
90+
}

nibabies/interfaces/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ def _chdir(path):
3333
'sub-01_run-01_echo-1_bold.nii.gz',
3434
'sub-01_run-01_echo-2_bold.nii.gz',
3535
'sub-01_run-01_echo-3_bold.nii.gz',
36+
'xfm0.h5',
37+
'xfm1.h5',
3638
)
3739

3840

nibabies/interfaces/patches.py

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
import nipype.interfaces.freesurfer as fs
2-
from nipype.interfaces.base import File, traits
1+
from pathlib import Path
2+
3+
from nipype.interfaces import (
4+
freesurfer as fs,
5+
)
6+
from nipype.interfaces.ants.base import ANTSCommand, ANTSCommandInputSpec
7+
from nipype.interfaces.base import File, InputMultiObject, TraitedSpec, traits
38

49

510
class _MRICoregInputSpec(fs.registration.MRICoregInputSpec):
@@ -23,3 +28,80 @@ class MRICoreg(fs.MRICoreg):
2328
"""
2429

2530
input_spec = _MRICoregInputSpec
31+
32+
33+
class ConcatXFMInputSpec(ANTSCommandInputSpec):
34+
transforms = InputMultiObject(
35+
traits.Either(File(exists=True), 'identity'),
36+
argstr='%s',
37+
mandatory=True,
38+
desc='transform files: will be applied in reverse order. For '
39+
'example, the last specified transform will be applied first.',
40+
)
41+
out_xfm = traits.File(
42+
'concat_xfm.h5',
43+
usedefault=True,
44+
argstr='--output [ %s, 1 ]',
45+
desc='output file name',
46+
)
47+
reference_image = File(
48+
argstr='--reference-image %s',
49+
mandatory=True,
50+
desc='reference image space that you wish to warp INTO',
51+
exists=True,
52+
)
53+
invert_transform_flags = InputMultiObject(traits.Bool())
54+
55+
56+
class ConcatXFMOutputSpec(TraitedSpec):
57+
out_xfm = File(desc='Combined transform')
58+
59+
60+
class ConcatXFM(ANTSCommand):
61+
"""
62+
Streamed use of antsApplyTransforms to combine multiple xfms into a single file
63+
64+
Examples
65+
--------
66+
67+
>>> from nibabies.interfaces.patches import ConcatXFM
68+
>>> cxfm = ConcatXFM()
69+
>>> cxfm.inputs.transforms = [testdir / 'xfm0.h5', testdir / 'xfm1.h5']
70+
>>> cxfm.inputs.reference_image = testdir / 'anatomical.nii'
71+
>>> cxfm.cmdline # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
72+
'antsApplyTransforms --output [ concat_xfm.h5, 1 ] --reference-image .../anatomical.nii \
73+
--transform .../xfm0.h5 --transform .../xfm1.h5'
74+
75+
"""
76+
77+
_cmd = 'antsApplyTransforms'
78+
input_spec = ConcatXFMInputSpec
79+
output_spec = ConcatXFMOutputSpec
80+
81+
def _get_transform_filenames(self):
82+
retval = []
83+
invert_flags = self.inputs.invert_transform_flags
84+
if not invert_flags:
85+
invert_flags = [False] * len(self.inputs.transforms)
86+
elif len(self.inputs.transforms) != len(invert_flags):
87+
raise ValueError(
88+
'ERROR: The invert_transform_flags list must have the same number '
89+
'of entries as the transforms list.'
90+
)
91+
92+
for transform, invert in zip(self.inputs.transforms, invert_flags, strict=False):
93+
if invert:
94+
retval.append(f'--transform [ {transform}, 1 ]')
95+
else:
96+
retval.append(f'--transform {transform}')
97+
return ' '.join(retval)
98+
99+
def _format_arg(self, opt, spec, val):
100+
if opt == 'transforms':
101+
return self._get_transform_filenames()
102+
return super()._format_arg(opt, spec, val)
103+
104+
def _list_outputs(self):
105+
outputs = self._outputs().get()
106+
outputs['out_xfm'] = Path(self.inputs.out_xfm).absolute()
107+
return outputs

0 commit comments

Comments
 (0)