Skip to content

Commit 9ab4988

Browse files
committed
ENH: Add MCRIBS interface
1 parent 201662a commit 9ab4988

File tree

1 file changed

+187
-0
lines changed

1 file changed

+187
-0
lines changed

nibabies/interfaces/mcribs.py

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import os
2+
import shutil
3+
from pathlib import Path
4+
5+
from nipype.interfaces.base import (
6+
CommandLine,
7+
CommandLineInputSpec,
8+
Directory,
9+
File,
10+
TraitedSpec,
11+
traits,
12+
)
13+
14+
15+
class MCRIBReconAllInputSpec(CommandLineInputSpec):
16+
# Input structure massaging
17+
subjects_dir = Directory(
18+
exists=True,
19+
hash_files=False,
20+
desc='path to subjects directory',
21+
required=True,
22+
)
23+
subject_id = traits.Str(
24+
# required=True,
25+
argstr='%s',
26+
position=-1,
27+
desc='subject name',
28+
)
29+
t1w_file = File(
30+
exists=True,
31+
copyfile=True,
32+
desc='T1w to be used for deformable (must be registered to T2w image)',
33+
)
34+
t2w_file = File(
35+
exists=True,
36+
required=True,
37+
copyfile=True,
38+
desc='T2w (Isotropic + N4 corrected)',
39+
)
40+
segmentation_file = File(
41+
desc='Segmentation file (skips tissue segmentation)',
42+
)
43+
44+
# MCRIBS options
45+
conform = traits.Bool(
46+
argstr='--conform',
47+
desc='Reorients to radiological, axial slice orientation. Resamples to isotropic voxels',
48+
)
49+
tissueseg = traits.Bool(
50+
argstr='--tissueseg',
51+
desc='Perform tissue type segmentation',
52+
)
53+
surfrecon = traits.Bool(
54+
True,
55+
usedefault=True,
56+
argstr='--surfrecon',
57+
desc='Reconstruct surfaces',
58+
)
59+
surfrecon_method = traits.Enum(
60+
'Deformable',
61+
argstr='--surfreconmethod %s',
62+
usedefault=True,
63+
desc='Surface reconstruction method',
64+
)
65+
join_thresh = traits.Float(
66+
1.0,
67+
argstr='--deformablejointhresh %f',
68+
usedefault=True,
69+
desc='Join threshold parameter for Deformable',
70+
)
71+
fast_collision = traits.Bool(
72+
True,
73+
argstr='--deformablefastcollision',
74+
usedefault=True,
75+
desc='Use Deformable fast collision test',
76+
)
77+
autorecon_after_surf = traits.Bool(
78+
True,
79+
argstr='--autoreconaftersurf',
80+
usedefault=True,
81+
desc='Do all steps after surface reconstruction',
82+
)
83+
segstats = traits.Bool(
84+
True,
85+
argstr='--segstats',
86+
usedefault=True,
87+
desc='Compute statistics on segmented volumes',
88+
)
89+
nthreads = traits.Int(
90+
argstr='-nthreads %d',
91+
desc='Number of threads for multithreading applications',
92+
)
93+
94+
95+
class MCRIBReconAllOutputSpec(TraitedSpec):
96+
mcribs_dir = Directory(desc='MCRIBS output directory')
97+
98+
99+
class MCRIBReconAll(CommandLine):
100+
_cmd = 'MCRIBReconAll'
101+
input_spec = MCRIBReconAllInputSpec
102+
output_spec = MCRIBReconAllOutputSpec
103+
104+
def _setup_directory_structure(self, mcribs_dir: Path) -> None:
105+
'''
106+
Create the required structure for skipping steps.
107+
108+
The directory tree
109+
------------------
110+
111+
<subject_id>/
112+
├── RawT2
113+
│ └── <subject_id>.nii.gz
114+
├── SurfReconDeformable
115+
│ └── <subject_id>
116+
│ └── temp
117+
│ └── t2w-image.nii.gz
118+
├── TissueSeg
119+
│ ├── <subject_id>_all_labels.nii.gz
120+
│ └── <subject_id>_all_labels_manedit.nii.gz
121+
└── TissueSegDrawEM
122+
└── <subject_id>
123+
└── N4
124+
└── <subject_id>.nii.gz
125+
'''
126+
sid = self.inputs.subject_id
127+
mkdir_kw = {'parents': True, 'exist_ok': True}
128+
root = mcribs_dir / sid
129+
root.mkdir(**mkdir_kw)
130+
131+
# T2w operations
132+
t2w = root / 'RawT2' / f'{sid}.nii.gz'
133+
t2w.parent.mkdir(**mkdir_kw)
134+
if not t2w.exists():
135+
shutil.copy(self.inputs.t2w_file, str(t2w))
136+
137+
if not self.inputs.conform:
138+
t2wiso = root / 'RawT2RadiologicalIsotropic' / f'{sid}.nii.gz'
139+
t2wiso.parent.mkdir(**mkdir_kw)
140+
if not t2wiso.exists():
141+
t2wiso.symlink_to(f'../../RawT2/{sid}.nii.gz')
142+
143+
n4 = root / sid / 'N4' / f'{sid}.nii.gz'
144+
n4.parent.mkdir(**mkdir_kw)
145+
if not n4.exists():
146+
n4.symlink_to(f'../../../RawT2/{sid}.nii.gz')
147+
148+
# Segmentation
149+
if self.inputs.segmentation_file:
150+
# TissueSeg directive disabled
151+
tisseg = root / 'TissueSeg' / f'{sid}_all_labels.nii.gz'
152+
tisseg.parent.mkdir(**mkdir_kw)
153+
if not tisseg.exists():
154+
shutil.copy(self.inputs.segmentation, str(tisseg))
155+
manedit = tisseg.parent / f'{sid}_all_labels_manedit.nii.gz'
156+
if not manedit.exists():
157+
manedit.symlink_to(tisseg.name)
158+
159+
if self.inputs.surfrecon:
160+
surfrec = root / 'SurfReconDeformable' / sid / 'temp' / 't2w-image.nii.gz'
161+
surfrec.parent.mkdir(**mkdir_kw)
162+
if not surfrec.exists():
163+
surfrec.symlink_to(f'../../../../RawT2/{sid}.nii.gz')
164+
165+
# TODO: T1w -> <subject_id>/RawT1RadiologicalIsotropic/<subjectid>.nii.gz
166+
return
167+
168+
def _run_interface(self, runtime):
169+
# if users wish to preserve their runs
170+
mcribs_dir = os.getenv('MCRIBS_SUBJECTS')
171+
if mcribs_dir is None or not Path(mcribs_dir).exists():
172+
mcribs_dir = runtime.cwd / 'mcribs'
173+
self._mcribs_dir = mcribs_dir
174+
self._setup_directory_structure(mcribs_dir)
175+
# runs in CWD
176+
os.chdir(mcribs_dir / self.inputs.subject_id)
177+
return super()._run_interface(runtime)
178+
179+
def _list_outputs(self):
180+
outputs = self._outputs().get()
181+
outputs['mcribs_dir'] = self._mcribs_dir
182+
183+
# TODO: Copy freesurfer directory into FS subjects dir
184+
# fs_outputs = self._mcribs_dir / self.inputs.subject_id / 'freesurfer'
185+
# if fs_outputs.exists():
186+
# pass
187+
return outputs

0 commit comments

Comments
 (0)