Change fieldmap handling #1

Merged
msz merged 2 commits from fix-dwi-fmap into main 2026-06-12 17:20:57 +00:00

View file

@ -5,7 +5,7 @@ from heudiconv.utils import SeqInfo
# https://heudiconv.readthedocs.io/en/latest/heuristics.html#populate-intended-for-opts # https://heudiconv.readthedocs.io/en/latest/heuristics.html#populate-intended-for-opts
POPULATE_INTENDED_FOR_OPTS = { POPULATE_INTENDED_FOR_OPTS = {
"matching_parameters": ["ImagingVolume", "Shims"], "matching_parameters": ["CustomAcquisitionLabel", "ImagingVolume", "Shims"],
"criterion": "Closest", "criterion": "Closest",
} }
@ -45,19 +45,18 @@ def infotodict(
rest = create_key("{bids_subject_session_dir}/func/{bids_subject_session_prefix}_task-rest_bold") rest = create_key("{bids_subject_session_dir}/func/{bids_subject_session_prefix}_task-rest_bold")
restvida = create_key("{bids_subject_session_dir}/func/{bids_subject_session_prefix}_task-restvida_bold") restvida = create_key("{bids_subject_session_dir}/func/{bids_subject_session_prefix}_task-restvida_bold")
restvidn = create_key("{bids_subject_session_dir}/func/{bids_subject_session_prefix}_task-restvidn_bold") restvidn = create_key("{bids_subject_session_dir}/func/{bids_subject_session_prefix}_task-restvidn_bold")
fmap_ap = create_key("{bids_subject_session_dir}/fmap/{bids_subject_session_prefix}_dir-ap_run-{item}_epi") dwi_b1200 = create_key("{bids_subject_session_dir}/dwi/{bids_subject_session_prefix}_acq-b1200_dir-PA_dwi")
fmap_pa = create_key("{bids_subject_session_dir}/fmap/{bids_subject_session_prefix}_dir-pa_run-{item}_epi") ref_b1200 = create_key("{bids_subject_session_dir}/fmap/{bids_subject_session_prefix}_acq-b1200_dir-AP_epi")
dwi_b1200 = create_key("{bids_subject_session_dir}/dwi/{bids_subject_session_prefix}_acq-b1200_dwi") dwi_mshell = create_key("{bids_subject_session_dir}/dwi/{bids_subject_session_prefix}_acq-mshell_dir-PA_dwi")
ref_b1200 = create_key("{bids_subject_session_dir}/dwi/{bids_subject_session_prefix}_acq-b1200_sbref") ref_mshell = create_key("{bids_subject_session_dir}/fmap/{bids_subject_session_prefix}_acq-mshell_dir-AP_epi")
dwi_mshell = create_key("{bids_subject_session_dir}/dwi/{bids_subject_session_prefix}_acq-mshell_dwi")
ref_mshell = create_key("{bids_subject_session_dir}/dwi/{bids_subject_session_prefix}_acq-mshell_sbref")
# generate fieldmap keys with fixed run labels for explicitly declared runs # generate fieldmap keys with fixed acq labels for explicitly declared runs
run_to_task = {"1": "rest", "2": "restvidn", "3": "restvida"} # that was the intention
fmap_explicit_keys = { fmap_explicit_keys = {
(dirlabel, runlabel): create_key( (dirlabel, tasklabel): create_key(
f"{{bids_subject_session_dir}}/fmap/{{bids_subject_session_prefix}}_dir-{dirlabel}_run-{runlabel}_epi" f"{{bids_subject_session_dir}}/fmap/{{bids_subject_session_prefix}}_acq-{tasklabel}_dir-{dirlabel}_epi"
) )
for dirlabel, runlabel in product(("ap", "pa"), ("1", "2", "3")) for dirlabel, tasklabel in product(("ap", "pa"), ("rest", "restvidn", "restvida"))
} }
info: dict[tuple[str, tuple[str, ...], None], list[str]] = { info: dict[tuple[str, tuple[str, ...], None], list[str]] = {
@ -66,8 +65,6 @@ def infotodict(
rest: [], rest: [],
restvida: [], restvida: [],
restvidn: [], restvidn: [],
fmap_ap: [],
fmap_pa: [],
dwi_b1200: [], dwi_b1200: [],
ref_b1200: [], ref_b1200: [],
dwi_mshell: [], dwi_mshell: [],
@ -77,6 +74,9 @@ def infotodict(
# also include the generated keys # also include the generated keys
info.update({k: [] for k in fmap_explicit_keys.values()}) info.update({k: [] for k in fmap_explicit_keys.values()})
# keep track of last task (potentially for field maps)
last_task = None
for s in seqinfo: for s in seqinfo:
""" """
The namedtuple `s` contains the following fields: The namedtuple `s` contains the following fields:
@ -116,6 +116,7 @@ def infotodict(
info[restvida].append(s.series_id) info[restvida].append(s.series_id)
if m.group("task") == "restvidn": if m.group("task") == "restvidn":
info[restvidn].append(s.series_id) info[restvidn].append(s.series_id)
last_task = m.group("task")
elif (m := dwi_regex.search(s.protocol_name)) is not None: elif (m := dwi_regex.search(s.protocol_name)) is not None:
if "b1200" in m.group("label") and not s.is_derived: if "b1200" in m.group("label") and not s.is_derived:
@ -131,25 +132,20 @@ def infotodict(
elif (m := fmap_regex.search(s.protocol_name)) is not None: elif (m := fmap_regex.search(s.protocol_name)) is not None:
if m.group("run") is None: if m.group("run") is None:
# implicit run numbering # no explicit run numbering
if m.group("dir") == "ap": target_task = last_task if last_task is not None else "unknown"
info[fmap_ap].append(s.series_id)
elif m.group("dir") == "pa":
info[fmap_pa].append(s.series_id)
else: else:
# explicit run numbering # explicit run numbering
key = fmap_explicit_keys.get((m.group("dir"), m.group("run"))) target_task = run_to_task.get(m.group("run"), "unknown")
if key is None: key = fmap_explicit_keys.get((m.group("dir"), target_task))
# unexpected label combination, ignore if key is None:
continue # unexpected label combination, ignore
info[key].append(s.series_id) continue
info[key].append(s.series_id)
# deduplicate # deduplicate
removed = [] removed = []
for key, sid_list in info.items(): for key, sid_list in info.items():
if key not in {fmap_ap, fmap_pa}:
# we allow (expect) run identifiers in field maps
continue
if len(sid_list) > 1: if len(sid_list) > 1:
# all other should be unique, keep last in case of repetitions # all other should be unique, keep last in case of repetitions
removed.extend(sid_list[:-1]) removed.extend(sid_list[:-1])