Skip to content

Commit a04a25e

Browse files
committed
fix: better handling of multiecho dataset identification
taking into account comments from @tsalo. the single-echo case (when you set ``--echo-idx`` or ``--bids-filter``) is now more nicely handled (as single-echo). Partially addresses: #2182.
1 parent b8fde7b commit a04a25e

File tree

1 file changed

+55
-10
lines changed

1 file changed

+55
-10
lines changed

fmriprep/workflows/bold/base.py

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
from nipype.pipeline import engine as pe
1818
from nipype.interfaces import utility as niu
1919

20-
from niworkflows.utils.connections import pop_file
20+
from niworkflows.utils.connections import pop_file, listify
21+
2122

2223
from ...utils.meepi import combine_meepi_source
2324

@@ -152,23 +153,30 @@ def init_func_preproc_wf(bold_file):
152153
spaces = config.workflow.spaces
153154
output_dir = str(config.execution.output_dir)
154155

155-
# Extract BIDS entities and metadata from bold reference file
156-
ref_file = pop_file(bold_file)
156+
# Extract BIDS entities and metadata from BOLD file(s)
157+
entities = extract_entities(bold_file)
157158
layout = config.execution.layout
158-
entities = layout.parse_file_entities(ref_file)
159+
160+
# Take first file as reference
161+
ref_file = pop_file(bold_file)
159162
metadata = layout.get_metadata(ref_file)
160163

161-
# Drop echo entity for future queries, have a boolean shorthand
162-
multiecho = bool(entities.pop("echo", None))
163-
if multiecho and (isinstance(bold_file, str) or len(bold_file) == 1):
164-
multiecho = False
164+
echo_idxs = listify(entities.get("echo", []))
165+
multiecho = len(echo_idxs) > 2
166+
if len(echo_idxs) == 1:
165167
config.loggers.warning(
166-
f"Only one echo <{ref_file}> found in a seemingly multi-echo dataset. "
167-
"Falling back to single-echo mode."
168+
f"Running a single echo <{ref_file}> from a seemingly multi-echo dataset."
168169
)
169170
bold_file = ref_file # Just in case - drop the list
170171

172+
if len(echo_idxs) == 2:
173+
raise RuntimeError(
174+
"Multi-echo processing requires at least three different echos (found two)."
175+
)
176+
171177
if multiecho:
178+
# Drop echo entity for future queries, have a boolean shorthand
179+
entities.pop("echo", None)
172180
# reorder echoes from shortest to largest
173181
tes, bold_file = zip(*sorted([
174182
(layout.get_metadata(bf)["EchoTime"], bf) for bf in bold_file
@@ -887,3 +895,40 @@ def _to_join(in_file, join_file):
887895
return in_file
888896
res = JoinTSVColumns(in_file=in_file, join_file=join_file).run()
889897
return res.outputs.out_file
898+
899+
900+
def extract_entities(file_list):
901+
"""
902+
Return a dictionary of common entities given a list of files.
903+
904+
Examples
905+
--------
906+
>>> extract_entities('sub-01/anat/sub-01_T1w.nii.gz')
907+
{'subject': '01', 'suffix': 'T1w', 'datatype': 'anat', 'extension': 'nii.gz'}
908+
>>> extract_entities(['sub-01/anat/sub-01_T1w.nii.gz'] * 2)
909+
{'subject': '01', 'suffix': 'T1w', 'datatype': 'anat', 'extension': 'nii.gz'}
910+
>>> extract_entities(['sub-01/anat/sub-01_run-1_T1w.nii.gz',
911+
... 'sub-01/anat/sub-01_run-2_T1w.nii.gz'])
912+
{'subject': '01', 'run': [1, 2], 'suffix': 'T1w', 'datatype': 'anat',
913+
'extension': 'nii.gz'}
914+
915+
"""
916+
from collections import defaultdict
917+
from bids.layout import parse_file_entities
918+
919+
entities = defaultdict(list)
920+
for e, v in [
921+
ev_pair
922+
for f in listify(file_list)
923+
for ev_pair in parse_file_entities(f).items()
924+
]:
925+
entities[e].append(v)
926+
927+
def _unique(inlist):
928+
inlist = sorted(set(inlist))
929+
if len(inlist) == 1:
930+
return inlist[0]
931+
return inlist
932+
return {
933+
k: _unique(v) for k, v in entities.items()
934+
}

0 commit comments

Comments
 (0)