Skip to content

Commit d378af3

Browse files
committed
ENH: Restore resampling to FreeSurfer surfaces
1 parent c4d1a00 commit d378af3

File tree

3 files changed

+54
-105
lines changed

3 files changed

+54
-105
lines changed

fmriprep/workflows/bold/base.py

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,26 @@ def init_bold_wf(
442442
(bold_std_wf, ds_bold_std_wf, [('outputnode.bold_file', 'inputnode.bold')]),
443443
]) # fmt:skip
444444

445+
if config.workflow.run_reconall and freesurfer_spaces:
446+
config.loggers.workflow.debug("Creating BOLD surface-sampling workflow.")
447+
bold_surf_wf = init_bold_surf_wf(
448+
mem_gb=mem_gb["resampled"],
449+
surface_spaces=freesurfer_spaces,
450+
medial_surface_nan=config.workflow.medial_surface_nan,
451+
metadata=all_metadata[0],
452+
output_dir=fmriprep_dir,
453+
name="bold_surf_wf",
454+
)
455+
bold_surf_wf.inputs.inputnode.source_file = bold_file
456+
workflow.connect([
457+
(inputnode, bold_surf_wf, [
458+
("subjects_dir", "inputnode.subjects_dir"),
459+
("subject_id", "inputnode.subject_id"),
460+
("fsnative2t1w_xfm", "inputnode.fsnative2t1w_xfm"),
461+
]),
462+
(bold_anat_wf, bold_surf_wf, [("outputnode.bold_file", "inputnode.bold_t1w")]),
463+
]) # fmt:skip
464+
445465
bold_confounds_wf = init_bold_confs_wf(
446466
mem_gb=mem_gb["largemem"],
447467
metadata=all_metadata[0],
@@ -723,27 +743,6 @@ def init_func_preproc_wf(bold_file, has_fieldmap=False):
723743
)
724744

725745
# SURFACES ##################################################################################
726-
# Freesurfer
727-
if freesurfer and freesurfer_spaces:
728-
config.loggers.workflow.debug("Creating BOLD surface-sampling workflow.")
729-
bold_surf_wf = init_bold_surf_wf(
730-
mem_gb=mem_gb["resampled"],
731-
surface_spaces=freesurfer_spaces,
732-
medial_surface_nan=config.workflow.medial_surface_nan,
733-
name="bold_surf_wf",
734-
)
735-
# fmt:off
736-
workflow.connect([
737-
(inputnode, bold_surf_wf, [
738-
("subjects_dir", "inputnode.subjects_dir"),
739-
("subject_id", "inputnode.subject_id"),
740-
("fsnative2t1w_xfm", "inputnode.fsnative2t1w_xfm"),
741-
]),
742-
(bold_t1_trans_wf, bold_surf_wf, [("outputnode.bold_t1", "inputnode.source_file")]),
743-
(bold_surf_wf, outputnode, [("outputnode.surfaces", "surfaces")]),
744-
(bold_surf_wf, func_derivatives_wf, [("outputnode.target", "inputnode.surf_refs")]),
745-
])
746-
# fmt:on
747746

748747
# CIFTI output
749748
if config.workflow.cifti_output:

fmriprep/workflows/bold/outputs.py

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -907,15 +907,6 @@ def init_func_derivatives_wf(
907907
nonstd_spaces = set(spaces.get_nonstandard())
908908
workflow = Workflow(name=name)
909909

910-
# BOLD series will generally be unmasked unless multiecho,
911-
# as the optimal combination is undefined outside a bounded mask
912-
masked = multiecho
913-
t2star_meta = {
914-
'Units': 's',
915-
'EstimationReference': 'doi:10.1002/mrm.20900',
916-
'EstimationAlgorithm': 'monoexponential decay model',
917-
}
918-
919910
inputnode = pe.Node(
920911
niu.IdentityInterface(
921912
fields=[
@@ -967,51 +958,6 @@ def init_func_derivatives_wf(
967958
])
968959
# fmt:on
969960

970-
fs_outputs = spaces.cached.get_fs_spaces()
971-
if freesurfer and fs_outputs:
972-
from niworkflows.interfaces.surf import Path2BIDS
973-
974-
select_fs_surf = pe.Node(
975-
KeySelect(fields=['surfaces', 'surf_kwargs']),
976-
name='select_fs_surf',
977-
run_without_submitting=True,
978-
mem_gb=DEFAULT_MEMORY_MIN_GB,
979-
)
980-
select_fs_surf.iterables = [('key', fs_outputs)]
981-
select_fs_surf.inputs.surf_kwargs = [{'space': s} for s in fs_outputs]
982-
983-
name_surfs = pe.MapNode(
984-
Path2BIDS(pattern=r'(?P<hemi>[lr])h.\w+'),
985-
iterfield='in_file',
986-
name='name_surfs',
987-
run_without_submitting=True,
988-
)
989-
990-
ds_bold_surfs = pe.MapNode(
991-
DerivativesDataSink(
992-
base_directory=output_dir,
993-
extension=".func.gii",
994-
TaskName=metadata.get('TaskName'),
995-
**timing_parameters,
996-
),
997-
iterfield=['in_file', 'hemi'],
998-
name='ds_bold_surfs',
999-
run_without_submitting=True,
1000-
mem_gb=DEFAULT_MEMORY_MIN_GB,
1001-
)
1002-
# fmt:off
1003-
workflow.connect([
1004-
(inputnode, select_fs_surf, [
1005-
('surf_files', 'surfaces'),
1006-
('surf_refs', 'keys')]),
1007-
(select_fs_surf, name_surfs, [('surfaces', 'in_file')]),
1008-
(inputnode, ds_bold_surfs, [('source_file', 'source_file')]),
1009-
(select_fs_surf, ds_bold_surfs, [('surfaces', 'in_file'),
1010-
('key', 'space')]),
1011-
(name_surfs, ds_bold_surfs, [('hemi', 'hemi')]),
1012-
])
1013-
# fmt:on
1014-
1015961
if freesurfer and project_goodvoxels:
1016962
ds_goodvoxels_mask = pe.Node(
1017963
DerivativesDataSink(

fmriprep/workflows/bold/resampling.py

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646

4747
from ...config import DEFAULT_MEMORY_MIN_GB
4848
from ...interfaces.workbench import MetricDilate, MetricMask, MetricResample
49+
from .outputs import prepare_timing_parameters
4950

5051
if ty.TYPE_CHECKING:
5152
from niworkflows.utils.spaces import SpatialReferences
@@ -56,6 +57,8 @@ def init_bold_surf_wf(
5657
mem_gb: float,
5758
surface_spaces: ty.List[str],
5859
medial_surface_nan: bool,
60+
metadata: dict,
61+
output_dir: str,
5962
name: str = "bold_surf_wf",
6063
):
6164
"""
@@ -90,6 +93,8 @@ def init_bold_surf_wf(
9093
Inputs
9194
------
9295
source_file
96+
Original BOLD series
97+
bold_t1w
9398
Motion-corrected BOLD series in T1 space
9499
subjects_dir
95100
FreeSurfer SUBJECTS_DIR
@@ -109,6 +114,10 @@ def init_bold_surf_wf(
109114
from niworkflows.interfaces.nitransforms import ConcatenateXFMs
110115
from niworkflows.interfaces.surf import GiftiSetAnatomicalStructure
111116

117+
from fmriprep.interfaces import DerivativesDataSink
118+
119+
timing_parameters = prepare_timing_parameters(metadata)
120+
112121
workflow = Workflow(name=name)
113122
workflow.__desc__ = """\
114123
The BOLD time-series were resampled onto the following surfaces
@@ -120,7 +129,13 @@ def init_bold_surf_wf(
120129

121130
inputnode = pe.Node(
122131
niu.IdentityInterface(
123-
fields=["source_file", "subject_id", "subjects_dir", "fsnative2t1w_xfm"]
132+
fields=[
133+
"source_file",
134+
"bold_t1w",
135+
"subject_id",
136+
"subjects_dir",
137+
"fsnative2t1w_xfm",
138+
]
124139
),
125140
name="inputnode",
126141
)
@@ -140,13 +155,6 @@ def select_target(subject_id, space):
140155
mem_gb=DEFAULT_MEMORY_MIN_GB,
141156
)
142157

143-
# Rename the source file to the output space to simplify naming later
144-
rename_src = pe.Node(
145-
niu.Rename(format_string="%(subject)s", keep_ext=True),
146-
name="rename_src",
147-
run_without_submitting=True,
148-
mem_gb=DEFAULT_MEMORY_MIN_GB,
149-
)
150158
itk2lta = pe.Node(
151159
ConcatenateXFMs(out_fmt="fs", inverse=True), name="itk2lta", run_without_submitting=True
152160
)
@@ -172,47 +180,43 @@ def select_target(subject_id, space):
172180
mem_gb=DEFAULT_MEMORY_MIN_GB,
173181
)
174182

175-
joinnode = pe.JoinNode(
176-
niu.IdentityInterface(fields=["surfaces", "target"]),
177-
joinsource="itersource",
178-
name="joinnode",
179-
)
180-
181-
outputnode = pe.Node(
182-
niu.IdentityInterface(fields=["surfaces", "target"]),
183-
name="outputnode",
183+
ds_bold_surfs = pe.MapNode(
184+
DerivativesDataSink(
185+
base_directory=output_dir,
186+
extension=".func.gii",
187+
TaskName=metadata.get('TaskName'),
188+
**timing_parameters,
189+
),
190+
iterfield=['in_file', 'hemi'],
191+
name='ds_bold_surfs',
192+
run_without_submitting=True,
193+
mem_gb=DEFAULT_MEMORY_MIN_GB,
184194
)
195+
ds_bold_surfs.inputs.hemi = ["L", "R"]
185196

186-
# fmt: off
187197
workflow.connect([
188198
(inputnode, get_fsnative, [
189199
("subject_id", "subject_id"),
190200
("subjects_dir", "subjects_dir")
191201
]),
192202
(inputnode, targets, [("subject_id", "subject_id")]),
193203
(inputnode, itk2lta, [
194-
("source_file", "reference"),
204+
("bold_t1w", "reference"),
195205
("fsnative2t1w_xfm", "in_xfms"),
196206
]),
197207
(get_fsnative, itk2lta, [("T1", "moving")]),
198208
(inputnode, sampler, [
199209
("subjects_dir", "subjects_dir"),
200210
("subject_id", "subject_id"),
211+
("bold_t1w", "source_file"),
201212
]),
202213
(itersource, targets, [("target", "space")]),
203-
(inputnode, rename_src, [("source_file", "in_file")]),
204-
(itersource, rename_src, [("target", "subject")]),
205-
(rename_src, sampler, [("out_file", "source_file")]),
206214
(itk2lta, sampler, [("out_inv", "reg_file")]),
207215
(targets, sampler, [("out", "target_subject")]),
208-
(update_metadata, joinnode, [("out_file", "surfaces")]),
209-
(itersource, joinnode, [("target", "target")]),
210-
(joinnode, outputnode, [
211-
("surfaces", "surfaces"),
212-
("target", "target"),
213-
]),
214-
])
215-
# fmt: on
216+
(inputnode, ds_bold_surfs, [("source_file", "source_file")]),
217+
(itersource, ds_bold_surfs, [("target", "space")]),
218+
(update_metadata, ds_bold_surfs, [("out_file", "in_file")]),
219+
]) # fmt:skip
216220

217221
# Refine if medial vertices should be NaNs
218222
medial_nans = pe.MapNode(

0 commit comments

Comments
 (0)