Skip to content

Commit e557058

Browse files
authored
fix: Calculate bold mask and dummy scans in transform-only runs (#3428)
This builds on #3427 to add a couple final things to the transform-only workflow: ``` fmriprep $BIDS $OUT participant --derivatives fmriprep=$MINIMAL ``` BOLD masks were not getting generated from the boldref and dummy scans were not being estimated. This PR factors out the validation and dummy scan detection from `init_raw_boldref_wf` and uses the full workflow in fit mode and the subworkflow in transform mode. Will rebase after #3427 is merged. Can compare now with: c28a615...6c78e54
2 parents 8c58b70 + fdbf0f1 commit e557058

File tree

2 files changed

+116
-19
lines changed

2 files changed

+116
-19
lines changed

fmriprep/workflows/bold/fit.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
init_ds_registration_wf,
5454
init_func_fit_reports_wf,
5555
)
56-
from .reference import init_raw_boldref_wf
56+
from .reference import init_raw_boldref_wf, init_validation_and_dummies_wf
5757
from .registration import init_bold_reg_wf
5858
from .stc import init_bold_stc_wf
5959
from .t2s import init_bold_t2s_wf
@@ -407,15 +407,18 @@ def init_bold_fit_wf(
407407
]) # fmt:skip
408408
else:
409409
config.loggers.workflow.info('Found HMC boldref - skipping Stage 1')
410-
411-
validate_bold = pe.Node(ValidateImage(), name='validate_bold')
412-
validate_bold.inputs.in_file = bold_file
413-
414410
hmcref_buffer.inputs.boldref = precomputed['hmc_boldref']
415411

412+
validation_and_dummies_wf = init_validation_and_dummies_wf(bold_file=bold_file)
413+
416414
workflow.connect([
417-
(validate_bold, hmcref_buffer, [('out_file', 'bold_file')]),
418-
(validate_bold, func_fit_reports_wf, [('out_report', 'inputnode.validation_report')]),
415+
(validation_and_dummies_wf, hmcref_buffer, [
416+
('outputnode.bold_file', 'bold_file'),
417+
('outputnode.skip_vols', 'dummy_scans'),
418+
]),
419+
(validation_and_dummies_wf, func_fit_reports_wf, [
420+
('outputnode.validation_report', 'inputnode.validation_report'),
421+
]),
419422
(hmcref_buffer, hmc_boldref_source_buffer, [('boldref', 'in_file')]),
420423
]) # fmt:skip
421424

@@ -591,6 +594,14 @@ def init_bold_fit_wf(
591594
config.loggers.workflow.info('Found coregistration reference - skipping Stage 3')
592595
regref_buffer.inputs.boldref = precomputed['coreg_boldref']
593596

597+
# TODO: Allow precomputed bold masks to be passed
598+
# Also needs consideration for how it interacts above
599+
skullstrip_precomp_ref_wf = init_skullstrip_bold_wf(name='skullstrip_precomp_ref_wf')
600+
skullstrip_precomp_ref_wf.inputs.inputnode.in_file = precomputed['coreg_boldref']
601+
workflow.connect([
602+
(skullstrip_precomp_ref_wf, regref_buffer, [('outputnode.mask_file', 'boldmask')])
603+
]) # fmt:skip
604+
594605
if not boldref2anat_xform:
595606
# calculate BOLD registration to T1w
596607
bold_reg_wf = init_bold_reg_wf(

fmriprep/workflows/bold/reference.py

Lines changed: 98 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ def init_raw_boldref_wf(
8080
beginning of ``bold_file``
8181
8282
"""
83-
from niworkflows.interfaces.bold import NonsteadyStatesDetector
8483
from niworkflows.interfaces.images import RobustAverage
8584

8685
workflow = Workflow(name=name)
@@ -106,6 +105,96 @@ def init_raw_boldref_wf(
106105
name='outputnode',
107106
)
108107

108+
# Simplify manually setting input image
109+
if bold_file is not None:
110+
inputnode.inputs.bold_file = bold_file
111+
112+
validation_and_dummies_wf = init_validation_and_dummies_wf()
113+
114+
gen_avg = pe.Node(RobustAverage(), name='gen_avg', mem_gb=1)
115+
116+
workflow.connect([
117+
(inputnode, validation_and_dummies_wf, [
118+
('bold_file', 'inputnode.bold_file'),
119+
('dummy_scans', 'inputnode.dummy_scans'),
120+
]),
121+
(validation_and_dummies_wf, gen_avg, [
122+
('outputnode.bold_file', 'in_file'),
123+
('outputnode.t_mask', 't_mask'),
124+
]),
125+
(validation_and_dummies_wf, outputnode, [
126+
('outputnode.bold_file', 'bold_file'),
127+
('outputnode.skip_vols', 'skip_vols'),
128+
('outputnode.algo_dummy_scans', 'algo_dummy_scans'),
129+
('outputnode.validation_report', 'validation_report'),
130+
]),
131+
(gen_avg, outputnode, [('out_file', 'boldref')]),
132+
]) # fmt:skip
133+
134+
return workflow
135+
136+
137+
def init_validation_and_dummies_wf(
138+
bold_file=None,
139+
name='validation_and_dummies_wf',
140+
):
141+
"""
142+
Build a workflow that validates a BOLD image and detects non-steady-state volumes.
143+
144+
Workflow Graph
145+
.. workflow::
146+
:graph2use: orig
147+
:simple_form: yes
148+
149+
from fmriprep.workflows.bold.reference import init_validation_and_dummies_wf
150+
wf = init_validation_and_dummies_wf()
151+
152+
Parameters
153+
----------
154+
bold_file : :obj:`str`
155+
BOLD series NIfTI file
156+
name : :obj:`str`
157+
Name of workflow (default: ``validation_and_dummies_wf``)
158+
159+
Inputs
160+
------
161+
bold_file : str
162+
BOLD series NIfTI file
163+
dummy_scans : int or None
164+
Number of non-steady-state volumes specified by user at beginning of ``bold_file``
165+
166+
Outputs
167+
-------
168+
bold_file : str
169+
Validated BOLD series NIfTI file
170+
skip_vols : int
171+
Number of non-steady-state volumes selected at beginning of ``bold_file``
172+
algo_dummy_scans : int
173+
Number of non-steady-state volumes agorithmically detected at
174+
beginning of ``bold_file``
175+
176+
"""
177+
from niworkflows.interfaces.bold import NonsteadyStatesDetector
178+
179+
workflow = Workflow(name=name)
180+
181+
inputnode = pe.Node(
182+
niu.IdentityInterface(fields=['bold_file', 'dummy_scans']),
183+
name='inputnode',
184+
)
185+
outputnode = pe.Node(
186+
niu.IdentityInterface(
187+
fields=[
188+
'bold_file',
189+
'skip_vols',
190+
'algo_dummy_scans',
191+
't_mask',
192+
'validation_report',
193+
]
194+
),
195+
name='outputnode',
196+
)
197+
109198
# Simplify manually setting input image
110199
if bold_file is not None:
111200
inputnode.inputs.bold_file = bold_file
@@ -117,7 +206,6 @@ def init_raw_boldref_wf(
117206
)
118207

119208
get_dummy = pe.Node(NonsteadyStatesDetector(), name='get_dummy')
120-
gen_avg = pe.Node(RobustAverage(), name='gen_avg', mem_gb=1)
121209

122210
calc_dummy_scans = pe.Node(
123211
niu.Function(function=pass_dummy_scans, output_names=['skip_vols_num']),
@@ -126,22 +214,20 @@ def init_raw_boldref_wf(
126214
mem_gb=DEFAULT_MEMORY_MIN_GB,
127215
)
128216

129-
# fmt: off
130217
workflow.connect([
131218
(inputnode, val_bold, [('bold_file', 'in_file')]),
132-
(inputnode, get_dummy, [('bold_file', 'in_file')]),
133-
(inputnode, calc_dummy_scans, [('dummy_scans', 'dummy_scans')]),
134-
(val_bold, gen_avg, [('out_file', 'in_file')]),
135-
(get_dummy, gen_avg, [('t_mask', 't_mask')]),
136-
(get_dummy, calc_dummy_scans, [('n_dummy', 'algo_dummy_scans')]),
137219
(val_bold, outputnode, [
138220
('out_file', 'bold_file'),
139221
('out_report', 'validation_report'),
140222
]),
223+
(inputnode, get_dummy, [('bold_file', 'in_file')]),
224+
(inputnode, calc_dummy_scans, [('dummy_scans', 'dummy_scans')]),
225+
(get_dummy, calc_dummy_scans, [('n_dummy', 'algo_dummy_scans')]),
226+
(get_dummy, outputnode, [
227+
('n_dummy', 'algo_dummy_scans'),
228+
('t_mask', 't_mask'),
229+
]),
141230
(calc_dummy_scans, outputnode, [('skip_vols_num', 'skip_vols')]),
142-
(gen_avg, outputnode, [('out_file', 'boldref')]),
143-
(get_dummy, outputnode, [('n_dummy', 'algo_dummy_scans')]),
144-
])
145-
# fmt: on
231+
]) # fmt:skip
146232

147233
return workflow

0 commit comments

Comments
 (0)