Skip to content

Commit 18b3820

Browse files
committed
ENH: Finalize subcortical alignment workflow
1 parent b99491d commit 18b3820

File tree

1 file changed

+44
-109
lines changed

1 file changed

+44
-109
lines changed

nibabies/workflows/bold/alignment.py

Lines changed: 44 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
Subcortical alignment into MNI space
33
"""
44

5-
from nipype.interfaces.fsl.maths import MultiImageMaths
65
from nipype.pipeline import engine as pe
76
from nipype.interfaces import utility as niu, fsl
87
from nipype.interfaces.workbench.cifti import CiftiSmooth
@@ -25,7 +24,8 @@ def gen_subcortical_alignment_wf(repetition_time, name='subcortical_alignment_wf
2524
Align individual subcortical structures into MNI space.
2625
2726
This is a nipype workflow port of the DCAN infant pipeline.
28-
https://github.com/DCAN-Labs/dcan-infant-pipeline/blob/247e19e5441cc814cea2f23720caeeb6c6aeadf8/fMRISurface/scripts/SubcorticalAlign_ROIs.sh
27+
https://github.com/DCAN-Labs/dcan-infant-pipeline/blob/247e19/fMRISurface/scripts/SubcorticalAlign_ROIs.sh
28+
2929
3030
Parameters
3131
----------
@@ -47,77 +47,38 @@ def gen_subcortical_alignment_wf(repetition_time, name='subcortical_alignment_wf
4747
4848
Outputs
4949
-------
50-
50+
subcortical_file : :obj:`str`
51+
The BOLD file in atlas space with each ROI individually aligned.
5152
"""
5253
from niworkflows.engine.workflows import LiterateWorkflow as Workflow
5354

54-
5555
# inputs
56+
# ${VolumefMRI}.nii.gz
5657
# ${ROIFolder}/Atlas_ROIs.${GrayordinatesResolution}.nii.gz
5758
# ${ROIFolder}/ROIs.${GrayordinatesResolution}.nii.gz
5859
# $HCPPIPEDIR_Templates/InfMNI_2AdultMNI_Step2.mat
5960
inputnode = pe.Node(
60-
niu.IdentityInterface(fields=["bold_roi", "atlas_roi", "atlas_xfm"]),
61+
niu.IdentityInterface(fields=["bold_file", "bold_roi", "atlas_roi", "atlas_xfm"]),
6162
name="inputnode",
6263
)
63-
outputnode = pe.Node(niu.IdentityInterface(fields=[]), name='outputnode')
64+
outputnode = pe.Node(niu.IdentityInterface(fields=["subcortical_file"]), name='outputnode')
6465

6566

66-
#generate altas-roi space fMRI cifti for subcortical data
67-
# flirt -in ${VolumefMRI}.nii.gz -ref ${ROIFolder}/Atlas_ROIs.${GrayordinatesResolution}.nii.gz -applyxfm -init $HCPPIPEDIR_Templates/InfMNI_2AdultMNI_Step2.mat -out ${VolumefMRI}_2MNI.nii.gz
6867
applyxfm_atlas = pe.Node(fsl.FLIRT(), name="applyxfm_atlas")
69-
# output: ${VolumefMRI}_2MNI.nii.gz
70-
71-
# ${CARET7DIR}/wb_command -volume-affine-resample ${ROIFolder}/ROIs.${GrayordinatesResolution}.nii.gz $HCPPIPEDIR_Templates/InfMNI_2AdultMNI_Step2.mat ${VolumefMRI}_2MNI.nii.gz ENCLOSING_VOXEL ${ResultsFolder}/ROIs.${GrayordinatesResolution}.nii.gz -flirt ${ROIFolder}/ROIs.${GrayordinatesResolution}.nii.gz ${VolumefMRI}_2MNI.nii.gz
7268
vol_resample = pe.Node(VolumeAffineResample(method="ENCLOSING_VOXEL"), name="vol_resample")
73-
# output: ${ResultsFolder}/ROIs.${GrayordinatesResolution}.nii.gz
74-
75-
# ${CARET7DIR}/wb_command -cifti-create-dense-timeseries ${WD}/${NameOffMRI}_temp_orig_atlas.dtseries.nii -volume ${VolumefMRI}_2MNI.nii.gz ${ROIFolder}/Atlas_ROIs.${GrayordinatesResolution}.nii.gz
7669
create_dense = pe.Node(CiftiCreateDenseTimeseries(), name="create_dense")
77-
# output: ${WD}/${NameOffMRI}_temp_orig_atlas.dtseries.nii
78-
79-
# #splitting atlas and subject volume label files into individual ROI files for registration
80-
# ${CARET7DIR}/wb_command -volume-all-labels-to-rois ${ROIFolder}/ROIs.${GrayordinatesResolution}.nii.gz 1 ${WD}/sub_allroi.nii.gz
8170
subj_rois = pe.Node(VolumeAllLabelsToROIs(label_map=1), name="subj_rois")
82-
# output: ${WD}/sub_allroi.nii.gz
83-
84-
# fslsplit ${WD}/sub_allroi.nii.gz sub_ROI -t
8571
split_rois = pe.Node(fsl.Split(dimension="t"), name="split_rois")
86-
# output: sub_ROI* list
87-
88-
# ${CARET7DIR}/wb_command -volume-all-labels-to-rois ${ROIFolder}/Atlas_ROIs.${GrayordinatesResolution}.nii.gz 1 ${WD}/atl_allroi.nii.gz
8972
atlas_rois = pe.Node(VolumeAllLabelsToROIs(label_map=1), name="atlas_rois")
90-
# output: ${WD}/atl_allroi.nii.gz
91-
92-
# fslsplit ${WD}/atl_allroi.nii.gz atl_ROI -t
9373
split_atlas_rois = pe.Node(fsl.Split(dimension="t"), name="split_atlas_rois")
94-
# output: atl_ROI* list
95-
96-
# #exporting table to generate independent label files
97-
# ${CARET7DIR}/wb_command -volume-label-export-table ${ROIFolder}/Atlas_ROIs.${GrayordinatesResolution}.nii.gz 1 ${WD}/labelfile.txt
9874
atlas_labels = pe.Node(VolumeLabelExportTable(label_map=1), name="atlas_labels")
99-
# output: ${WD}/labelfile.txt
100-
101-
# #extract information from label file (values on even numbered line preceded by label on odd)
10275
parse_labels = pe.Node(
10376
niu.Function(function=parse_roi_labels, output_names=["structures", "label_id"]),
10477
name="parse_labels",
10578
)
106-
# output: structures / label_ids
107-
108-
# #initialize workbench command for creating dense time series.
109-
# DTSCommand="${CARET7DIR}/wb_command -cifti-create-dense-from-template ${WD}/${NameOffMRI}_temp_orig_atlas.dtseries.nii ${WD}/${NameOffMRI}_temp_atlas.dtseries.nii -series ${TR} 0.0 "
110-
# #initialize command to make sub2atl_label_ROI.2.nii.gz
111-
# Sub2AtlCmd=""
112-
113-
11479

11580
### The following is wrapped in a for-loop, iterating across each roi
116-
## sub_{ROIname} is the roi
117-
118-
119-
#perform linear mapping from subject to atlas ROI
120-
# flirt -in ${WD}/sub_${ROIname} -ref ${WD}/atl_${ROIname} -searchrx -20 20 -searchry -20 20 -searchrz -20 20 -o sub2atl_${ROIname} -interp nearestneighbour -omat ${WD}/sub2atl_${ROInum}.mat
81+
### Instead, we will use MapNodes and iter across the varying inputs
12182
roi2atlas = pe.MapNode(
12283
fsl.FLIRT(
12384
searchr_x=[-20, 20],
@@ -128,89 +89,51 @@ def gen_subcortical_alignment_wf(repetition_time, name='subcortical_alignment_wf
12889
name="roi2atlas",
12990
iterfield=["in_file", "reference"],
13091
)
131-
# output: sub2atl_${ROIname} ${WD}/sub2atl_${ROInum}.mat
132-
133-
# flirt -in ${VolumefMRI} -ref ${WD}/atl_${ROIname} -applyxfm -init ${WD}/sub2atl_${ROInum}.mat -o sub2atl_vol_${ROIname} -interp spline
13492
applyxfm_roi = pe.MapNode(
13593
fsl.ApplyXFM(interp="spline"),
13694
iterfield=["reference"],
13795
name='applyxfm_roi',
13896
)
139-
# output: sub2atl_vol_${ROIname}
140-
141-
# #masking BOLD volumetric data to ROI only
142-
# fslmaths sub2atl_vol_${ROIname} -mas sub2atl_${ROIname} sub2atl_vol_masked_${ROIname}
14397
bold_mask_roi = pe.MapNode(
14498
fsl.ApplyMask(),
14599
iterfield=["in_file", "operand_file"],
146100
name='bold_mask_roi',
147101
)
148-
# output: sub2atl_vol_masked_${ROIname}
149-
150-
# #multiply ROI volume by value -- needed for creating a volume label file
151-
# fslmaths ${WD}/sub2atl_${ROIname} -mul $roi_value ${WD}/sub2atl_label_${ROIname}
152102
mul_roi = pe.MapNode(
153103
fsl.BinaryMaths(operation="mul"),
154104
iterfield=["in_file", "operand_file"],
155105
name='mul_roi',
156106
)
157-
# output: ${WD}/sub2atl_label_${ROIname}
158-
159-
# fslmaths ${WD}/atl_${ROIname} -mul $roi_value ${WD}/atl_label_${ROIname}
160107
mul_atlas_roi = pe.MapNode(
161108
fsl.BinaryMaths(operation="mul"),
162109
iterfield=["in_file", "operand_file"],
163110
name='mul_atlas_roi',
164111
)
165-
# output: ${WD}/atl_label_${ROIname}
166-
167-
# #use wb_command to create the volume label file, needed for creating the individual dtseries
168-
# ${CARET7DIR}/wb_command -volume-label-import ${WD}/sub2atl_label_${ROIname} ${WD}/labelfile.txt sub2atl_vol_label_${ROIname} -drop-unused-labels
169112
vol_label = pe.MapNode(
170113
VolumeLabelImport(drop_unused_labels=True),
171114
iterfield=["in_file"],
172115
name='vol_label',
173116
)
174-
# output: sub2atl_vol_label_${ROIname}
175-
176-
# ${CARET7DIR}/wb_command -volume-label-import ${WD}/atl_label_${ROIname} ${WD}/labelfile.txt atl_vol_label_${ROIname} -drop-unused-labels
177117
vol_atlas_label = pe.MapNode(
178118
VolumeLabelImport(drop_unused_labels=True),
179119
iterfield=["in_file"],
180120
name='vol_atlas_label',
181121
)
182-
# output: atl_vol_label_${ROIname}
183-
184-
# #create the individual dtseries
185-
# ${CARET7DIR}/wb_command -cifti-create-dense-timeseries ${WD}/${NameOffMRI}_temp_subject_${ROInum}.dtseries.nii -volume sub2atl_vol_masked_${ROIname} ${WD}/sub2atl_vol_label_${ROIname}
186-
# # Maybe here, too.
187122
create_dtseries = pe.MapNode(
188123
CiftiCreateDenseTimeseries(),
189124
iterfield=["volume_data", "volume_structure_labels"],
190125
name='create_dtseries'
191126
)
192-
# output: ${WD}/${NameOffMRI}_temp_subject_${ROInum}.dtseries.nii
193-
194-
# #create the cifti label file from the volume label file (why?????)
195-
# ${CARET7DIR}/wb_command -cifti-create-label ${WD}/atl_${NameOffMRI}_temp_template_${ROInum}.dlabel.nii -volume ${WD}/atl_vol_label_${ROIname} ${WD}/atl_vol_label_${ROIname}
196127
create_label = pe.MapNode(
197128
CiftiCreateLabel(),
198129
iterfield=["volume_label", "structure_label_volume"],
199130
name='create_label',
200131
)
201-
# output: ${WD}/sub_${NameOffMRI}_temp_template_${ROInum}.dlabel.nii
202-
203-
# #dilate the timeseries
204-
# ${CARET7DIR}/wb_command -cifti-dilate ${WD}/${NameOffMRI}_temp_subject_${ROInum}.dtseries.nii COLUMN 0 10 ${WD}/${NameOffMRI}_temp_subject_${ROInum}_dilate.dtseries.nii
205132
dilate = pe.MapNode(
206133
CiftiDilate(direction="COLUMN", surface_distance=0, volume_distance=10),
207134
iterfield=["in_file"],
208135
name="dilate"
209136
)
210-
# output: ${WD}/${NameOffMRI}_temp_subject_${ROInum}_dilate.dtseries.nii
211-
212-
# #perform resampling - resample into Atlas space, not subject space.
213-
# ${CARET7DIR}/wb_command -cifti-resample ${WD}/${NameOffMRI}_temp_subject_${ROInum}_dilate.dtseries.nii COLUMN ${WD}/atl_${NameOffMRI}_temp_template_${ROInum}.dlabel.nii COLUMN ADAP_BARY_AREA CUBIC ${WD}/${NameOffMRI}_temp_atlas_${ROInum}.dtseries.nii -volume-predilate 10
214137
resample = pe.MapNode(
215138
CiftiResample(
216139
direction="COLUMN",
@@ -222,42 +145,34 @@ def gen_subcortical_alignment_wf(repetition_time, name='subcortical_alignment_wf
222145
iterfield=["in_file", "template"],
223146
name='resample',
224147
)
225-
# output: ${WD}/${NameOffMRI}_temp_atlas_${ROInum}.dtseries.nii
226-
227-
# #perform smoothing
228-
# ${CARET7DIR}/wb_command -cifti-smoothing ${WD}/${NameOffMRI}_temp_atlas_${ROInum}.dtseries.nii 0 ${Sigma} COLUMN ${WD}/${NameOffMRI}_temp_subject_dilate_resample_smooth_${ROInum}.dtseries.nii -fix-zeros-volume
229148
smooth = pe.MapNode(
230149
CiftiSmooth(direction="COLUMN", fix_zeros_vol=True),
231150
iterfield=["in_file"],
232151
name="smooth"
233152
)
234-
# output: ${WD}/${NameOffMRI}_temp_subject_dilate_resample_smooth_${ROInum}.dtseries.nii
235-
236-
# #split back into a volumetric timeseries file
237-
# ${CARET7DIR}/wb_command -cifti-separate ${WD}/${NameOffMRI}_temp_subject_dilate_resample_smooth_${ROInum}.dtseries.nii COLUMN -volume-all ${ResultsFolder}/${NameOffMRI}_${ROInum}.nii.gz
238153
separate = pe.MapNode(
239154
CiftiSeparate(direction="COLUMN", volume_all=True),
240155
iterfield=["in_file"],
241156
name="separate"
242157
)
243-
# output: ${ResultsFolder}/${NameOffMRI}_${ROInum}.nii.gz
244158

245159
fmt_vols = pe.Node(niu.Function(function=format_volume_rois), name='fmt_vols')
246-
# #add input to wb_command to grow iteratively
247-
# DTSCommand="${CARET7DIR}/wb_command -cifti-create-dense-from-template ${WD}/${NameOffMRI}_temp_orig_atlas.dtseries.nii ${WD}/${NameOffMRI}_temp_atlas.dtseries.nii -series ${TR} 0.0"
248-
# DTSCommand="${DTSCommand} -volume ${roi_name} ${ResultsFolder}/${NameOffMRI}_${ROInum}.nii.gz"
249160
create_dtseries = pe.Node(
250161
CiftiCreateDenseFromTemplate(series=True, series_step=repetition_time, series_start=0),
251162
name='create_dtseries',
252163
)
253-
# output: ${WD}/${NameOffMRI}_temp_atlas.dtseries.nii
254-
255-
# fslmaths <file> -add <sub2atl_label_
256-
# Sub2AtlCmd="${Sub2AtlCmd}-add ${WD}/sub2atl_label_${ROIname} "
257-
operations = "-add %s " * len(rois) - 1
258-
agg_rois = pe.MapNode(fsl.MultiImageMaths(op_string=operations.strip()), name='agg_rois')
164+
fmt_agg_rois = pe.Node(
165+
niu.Function(
166+
function=format_agg_rois,
167+
output_names=["first_image", "op_files", "op_string"],
168+
),
169+
name='fmt_agg_rois',
170+
)
171+
agg_rois = pe.MapNode(fsl.MultiImageMaths(), name='agg_rois')
172+
final_vol = pe.Node(CiftiSeparate(direction="COLUMN", volume_all=True), name="final_vol")
259173

260174
workflow = Workflow(name=name)
175+
# fmt: off
261176
workflow.connect([
262177
(inputnode, applyxfm_atlas, [
263178
("bold_file", "in_file"),
@@ -272,7 +187,6 @@ def gen_subcortical_alignment_wf(repetition_time, name='subcortical_alignment_wf
272187
(atlas_rois, split_atlas_rois, [("out_file", "in_file")]),
273188
(inputnode, atlas_labels, [("atlas_roi", "in_file")]),
274189
(atlas_labels, parse_labels, [("out_file", "label_file")]),
275-
276190
# for loop across ROIs
277191
(split_rois, roi2atlas, [("out_files", "in_file")]),
278192
(inputnode, applyxfm_roi, [("bold_file", "in_file")]),
@@ -297,13 +211,21 @@ def gen_subcortical_alignment_wf(repetition_time, name='subcortical_alignment_wf
297211
(create_label, resample, [("out_file", "template")]),
298212
(resample, smooth, [("out_file", "in_file")]),
299213
(smooth, separate, [("out_file", "in_file")]),
300-
214+
# end loop
301215
(parse_labels, fmt_vols, [("structures", "structs")]),
302216
(separate, fmt_vols, [("volume_all_file", "rois")]),
303217
(create_dense, create_dtseries, [("out_file", "in_file")]),
304218
(fmt_vols, create_dtseries, [("out", "volume")]),
305-
219+
(mul_roi, fmt_agg_rois, [("out_file", "rois")]),
220+
(fmt_agg_rois, agg_rois, [
221+
("first_image", "in_file"),
222+
("op_files", "operand_files"),
223+
("op_string", "op_string")]),
224+
(create_dtseries, final_vol, [("out_file", "in_file")]),
225+
(final_vol, outputnode, [("out_file", "subcortical_file")]),
306226
])
227+
# fmt: on
228+
307229

308230
def parse_roi_labels(label_file):
309231
"""
@@ -338,10 +260,23 @@ def parse_roi_labels(label_file):
338260

339261
def format_volume_rois(structs, rois):
340262
"""Format volume arguments for CiftiCreateDenseFromTemplate."""
341-
342263
return [(struct, roi) for struct, roi in zip(structs, rois)]
343264

344265

345266
def format_agg_rois(rois):
346-
"""Separate list of ROIs into first ROI and string of add commands"""
347-
return rois[0], "-add %s " * len(rois) - 1
267+
"""
268+
Helper function to format MultiImageMaths command.
269+
270+
Parameters
271+
----------
272+
rois : `list` of `str`s
273+
List of files
274+
275+
Returns
276+
-------
277+
first_image
278+
op_files
279+
op_string
280+
281+
"""
282+
return rois[0], rois[1:], "-add %s " * (len(rois) - 1).strip()

0 commit comments

Comments
 (0)