Skip to content

Commit ef48d2a

Browse files
committed
RPT: Re-add summary and validation reports
1 parent 91ded48 commit ef48d2a

File tree

4 files changed

+86
-25
lines changed

4 files changed

+86
-25
lines changed

fmriprep/interfaces/reports.py

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,11 @@
6464
\t\t\t<li>Repetition time (TR): {tr:.03g}s</li>
6565
\t\t\t<li>Phase-encoding (PE) direction: {pedir}</li>
6666
\t\t\t<li>{multiecho}</li>
67-
\t\t\t<li>Slice timing correction: {stc}</li>
6867
\t\t\t<li>Susceptibility distortion correction: {sdc}</li>
6968
\t\t\t<li>Registration: {registration}</li>
7069
\t\t\t<li>Non-steady-state volumes: {dummy_scan_desc}</li>
7170
\t\t</ul>
7271
\t\t</details>
73-
\t\t<details>
74-
\t\t\t<summary>Confounds collected</summary><br />
75-
\t\t\t<p>{confounds}.</p>
76-
\t\t</details>
7772
"""
7873

7974
ABOUT_TEMPLATE = """\t<ul>
@@ -193,10 +188,7 @@ def _generate_segment(self):
193188
)
194189

195190

196-
class FunctionalSummaryInputSpec(BaseInterfaceInputSpec):
197-
slice_timing = traits.Enum(
198-
False, True, 'TooShort', usedefault=True, desc='Slice timing correction used'
199-
)
191+
class FunctionalSummaryInputSpec(TraitedSpec):
200192
distortion_correction = traits.Str(
201193
desc='Susceptibility distortion correction method', mandatory=True
202194
)
@@ -225,11 +217,10 @@ class FunctionalSummaryInputSpec(BaseInterfaceInputSpec):
225217
desc='Whether to initialize registration with the "header"'
226218
' or by centering the volumes ("register")',
227219
)
228-
confounds_file = File(exists=True, desc='Confounds file')
229220
tr = traits.Float(desc='Repetition time', mandatory=True)
230221
dummy_scans = traits.Either(traits.Int(), None, desc='number of dummy scans specified by user')
231222
algo_dummy_scans = traits.Int(desc='number of dummy scans determined by algorithm')
232-
echo_idx = traits.List([], usedefault=True, desc="BIDS echo identifiers")
223+
echo_idx = InputMultiObject(traits.Str, usedefault=True, desc="BIDS echo identifiers")
233224
orientation = traits.Str(mandatory=True, desc='Orientation of the voxel axes')
234225

235226

@@ -238,11 +229,6 @@ class FunctionalSummary(SummaryInterface):
238229

239230
def _generate_segment(self):
240231
dof = self.inputs.registration_dof
241-
stc = {
242-
True: 'Applied',
243-
False: 'Not applied',
244-
'TooShort': 'Skipped (too few volumes)',
245-
}[self.inputs.slice_timing]
246232
# #TODO: Add a note about registration_init below?
247233
reg = {
248234
'FSL': [
@@ -259,10 +245,6 @@ def _generate_segment(self):
259245

260246
pedir = get_world_pedir(self.inputs.orientation, self.inputs.pe_direction)
261247

262-
if isdefined(self.inputs.confounds_file):
263-
with open(self.inputs.confounds_file) as cfh:
264-
conflist = cfh.readline().strip('\n').strip()
265-
266248
dummy_scan_tmp = "{n_dum}"
267249
if self.inputs.dummy_scans == self.inputs.algo_dummy_scans:
268250
dummy_scan_msg = ' '.join(
@@ -290,10 +272,8 @@ def _generate_segment(self):
290272

291273
return FUNCTIONAL_TEMPLATE.format(
292274
pedir=pedir,
293-
stc=stc,
294275
sdc=self.inputs.distortion_correction,
295276
registration=reg,
296-
confounds=re.sub(r'[\t ]+', ', ', conflist),
297277
tr=self.inputs.tr,
298278
dummy_scan_desc=dummy_scan_msg,
299279
multiecho=multiecho,

fmriprep/workflows/bold/fit.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from sdcflows.workflows.apply.registration import init_coeff2epi_wf
4545

4646
from ... import config
47+
from ...interfaces.reports import FunctionalSummary
4748
from ...utils.bids import extract_entities
4849

4950
# BOLD workflows
@@ -117,6 +118,11 @@ def init_bold_fit_wf(
117118
# This could become more complicated in the future
118119
bold_file = bold_files[0]
119120

121+
# Get metadata from BOLD file(s)
122+
entities = extract_entities(bold_files)
123+
metadata = layout.get_metadata(bold_file)
124+
orientation = "".join(nb.aff2axcodes(nb.load(bold_file).affine))
125+
120126
if os.path.isfile(bold_file):
121127
bold_tlen, mem_gb = _create_mem_gb(bold_file)
122128

@@ -188,6 +194,22 @@ def init_bold_fit_wf(
188194
niu.IdentityInterface(fields=["boldref", "boldmask"]), name="regref_buffer"
189195
)
190196

197+
summary = pe.Node(
198+
FunctionalSummary(
199+
registration=("FSL", "FreeSurfer")[config.workflow.run_reconall],
200+
registration_dof=config.workflow.bold2t1w_dof,
201+
registration_init=config.workflow.bold2t1w_init,
202+
pe_direction=metadata.get("PhaseEncodingDirection"),
203+
echo_idx=entities.get("echo", []),
204+
tr=metadata["RepetitionTime"],
205+
orientation=orientation,
206+
),
207+
name="summary",
208+
mem_gb=config.DEFAULT_MEMORY_MIN_GB,
209+
run_without_submitting=True,
210+
)
211+
summary.inputs.dummy_scans = config.workflow.dummy_scans
212+
191213
func_fit_reports_wf = init_func_fit_reports_wf(
192214
# TODO: Enable sdc report even if we find coregref
193215
sdc_correction=not (have_coregref or fieldmap_id is None),
@@ -214,6 +236,7 @@ def init_bold_fit_wf(
214236
("coreg_boldref", "inputnode.coreg_boldref"),
215237
("boldref2anat_xfm", "inputnode.boldref2anat_xfm"),
216238
]),
239+
(summary, func_fit_reports_wf, [("out_report", "inputnode.summary_report")]),
217240
])
218241
# fmt:on
219242

@@ -240,6 +263,10 @@ def init_bold_fit_wf(
240263
(hmc_boldref_wf, hmcref_buffer, [("outputnode.bold_file", "bold_file")]),
241264
(hmc_boldref_wf, ds_hmc_boldref_wf, [("outputnode.boldref", "inputnode.boldref")]),
242265
(ds_hmc_boldref_wf, hmcref_buffer, [("outputnode.boldref", "boldref")]),
266+
(hmc_boldref_wf, summary, [("outputnode.algo_dummy_scans", "algo_dummy_scans")]),
267+
(hmc_boldref_wf, func_fit_reports_wf, [
268+
("outputnode.validation_report", "inputnode.validation_report"),
269+
]),
243270
])
244271
# fmt:on
245272
else:
@@ -248,9 +275,15 @@ def init_bold_fit_wf(
248275
validate_bold = pe.Node(ValidateImage(), name="validate_bold")
249276
validate_bold.inputs.in_file = bold_file
250277

251-
workflow.connect(validate_bold, "out_file", hmcref_buffer, "bold_file")
252278
hmcref_buffer.inputs.boldref = precomputed["hmc_boldref"]
253279

280+
# fmt:off
281+
workflow.connect([
282+
(validate_bold, hmcref_buffer, [("out_file", "bold_file")]),
283+
(validate_bold, func_fit_reports_wf, [("out_report", "inputnode.validation_report")]),
284+
])
285+
# fmt:on
286+
254287
# Stage 2: Estimate head motion
255288
if not hmc_xforms:
256289
config.loggers.workflow.info("Stage 2: Adding motion correction workflow")
@@ -381,6 +414,7 @@ def init_bold_fit_wf(
381414
('outputnode.corrected_mask', 'boldmask'),
382415
]),
383416
(fmap_select, func_fit_reports_wf, [("fmap_ref", "inputnode.fmap_ref")]),
417+
(fmap_select, summary, [("sdc_method", "distortion_correction")]),
384418
(fmapreg_buffer, func_fit_reports_wf, [
385419
("boldref2fmap_xfm", "inputnode.boldref2fmap_xfm"),
386420
]),
@@ -441,6 +475,7 @@ def init_bold_fit_wf(
441475
(regref_buffer, ds_boldreg_wf, [("boldref", "inputnode.source_files")]),
442476
(bold_reg_wf, ds_boldreg_wf, [("outputnode.itk_bold_to_t1", "inputnode.xform")]),
443477
(ds_boldreg_wf, outputnode, [("outputnode.xform", "boldref2anat_xfm")]),
478+
(bold_reg_wf, summary, [("outputnode.fallback", "fallback")]),
444479
])
445480
# fmt:on
446481
else:

fmriprep/workflows/bold/outputs.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,36 @@ def init_func_fit_reports_wf(
208208
# May be missing
209209
"subject_id",
210210
"subjects_dir",
211+
# Report snippets
212+
"summary_report",
213+
"validation_report",
211214
]
212215
inputnode = pe.Node(niu.IdentityInterface(fields=inputfields), name="inputnode")
213216

217+
ds_summary = pe.Node(
218+
DerivativesDataSink(
219+
base_directory=output_dir,
220+
desc="summary",
221+
datatype="figures",
222+
dismiss_entities=("echo",),
223+
),
224+
name="ds_report_summary",
225+
run_without_submitting=True,
226+
mem_gb=config.DEFAULT_MEMORY_MIN_GB,
227+
)
228+
229+
ds_validation = pe.Node(
230+
DerivativesDataSink(
231+
base_directory=output_dir,
232+
desc="validation",
233+
datatype="figures",
234+
dismiss_entities=("echo",),
235+
),
236+
name="ds_report_validation",
237+
run_without_submitting=True,
238+
mem_gb=config.DEFAULT_MEMORY_MIN_GB,
239+
)
240+
214241
# Resample anatomical references into BOLD space for plotting
215242
t1w_boldref = pe.Node(
216243
ApplyTransforms(
@@ -244,6 +271,14 @@ def init_func_fit_reports_wf(
244271

245272
# fmt:off
246273
workflow.connect([
274+
(inputnode, ds_summary, [
275+
('source_file', 'source_file'),
276+
('summary_report', 'in_file'),
277+
]),
278+
(inputnode, ds_validation, [
279+
('source_file', 'source_file'),
280+
('validation_report', 'in_file'),
281+
]),
247282
(inputnode, t1w_boldref, [
248283
('t1w_preproc', 'input_image'),
249284
('coreg_boldref', 'reference_image'),

fmriprep/workflows/bold/reference.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,15 @@ def init_raw_boldref_wf(
9494
name="inputnode",
9595
)
9696
outputnode = pe.Node(
97-
niu.IdentityInterface(fields=["bold_file", "boldref", "skip_vols", "algo_dummy_scans"]),
97+
niu.IdentityInterface(
98+
fields=[
99+
"bold_file",
100+
"boldref",
101+
"skip_vols",
102+
"algo_dummy_scans",
103+
"validation_report",
104+
]
105+
),
98106
name="outputnode",
99107
)
100108

@@ -126,7 +134,10 @@ def init_raw_boldref_wf(
126134
(val_bold, gen_avg, [("out_file", "in_file")]),
127135
(get_dummy, gen_avg, [("t_mask", "t_mask")]),
128136
(get_dummy, calc_dummy_scans, [("n_dummy", "algo_dummy_scans")]),
129-
(val_bold, outputnode, [("out_file", "bold_file")]),
137+
(val_bold, outputnode, [
138+
("out_file", "bold_file"),
139+
("out_report", "validation_report"),
140+
]),
130141
(calc_dummy_scans, outputnode, [("skip_vols_num", "skip_vols")]),
131142
(gen_avg, outputnode, [("out_file", "boldref")]),
132143
(get_dummy, outputnode, [("n_dummy", "algo_dummy_scans")]),

0 commit comments

Comments
 (0)