Skip to content

Commit

Permalink
ENH/FIX: Make BOLD HMC start frame configurable, do not clip BOLD tim…
Browse files Browse the repository at this point in the history
…eseries
  • Loading branch information
mgxd committed Jul 22, 2024
1 parent ae53389 commit 69d3e6e
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 9 deletions.
6 changes: 6 additions & 0 deletions nibabies/cli/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,12 @@ def _slice_time_ref(value, parser):
'Generally, this is determined based on availability, age, and surface '
'reconstruction method.',
)
g_baby.add_argument(
'--hmc-bold-frame',
type=int,
default=16,
help='Frame to start head motion estimation on BOLD.',
)
return parser


Expand Down
2 changes: 2 additions & 0 deletions nibabies/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,8 @@ class workflow(_Config):
"""Run *fieldmap-less* susceptibility-derived distortions estimation."""
hires = None
"""Run FreeSurfer ``recon-all`` with the ``-hires`` flag."""
hmc_bold_frame = 16
"""Frame to start head motion correction estimation on BOLD"""
ignore = None
"""Ignore particular steps for *nibabies*."""
level = None
Expand Down
1 change: 1 addition & 0 deletions nibabies/workflows/bold/fit.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ def init_bold_fit_wf(
name='hmc_boldref_wf',
bold_file=bold_file,
multiecho=multiecho,
ref_frame_start=config.workflow.hmc_bold_frame,
)
hmc_boldref_wf.inputs.inputnode.dummy_scans = config.workflow.dummy_scans

Expand Down
28 changes: 19 additions & 9 deletions nibabies/workflows/bold/reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
def init_raw_boldref_wf(
bold_file: str | None = None,
multiecho: bool = False,
start_frame: int = 17,
ref_frame_start: int = 16,
name: str = 'raw_boldref_wf',
):
"""
Expand All @@ -58,8 +58,8 @@ def init_raw_boldref_wf(
BOLD series NIfTI file
multiecho : :obj:`bool`
If multiecho data was supplied, data from the first echo will be selected
start_frame: :obj:`int`
BOLD frame to start creating the reference map from. Any earlier frames are discarded.
ref_frame_start: :obj:`int`
BOLD frame to start creating the reference map from.
name : :obj:`str`
Name of workflow (default: ``raw_boldref_wf``)
Expand Down Expand Up @@ -124,7 +124,7 @@ def init_raw_boldref_wf(
niu.Function(function=_select_frames, output_names=['start_frame', 't_mask']),
name='select_frames',
)
select_frames.inputs.start_frame = start_frame
select_frames.inputs.ref_frame_start = ref_frame_start

get_dummy = pe.Node(NonsteadyStatesDetector(), name='get_dummy')
gen_avg = pe.Node(RobustAverage(), name='gen_avg', mem_gb=1)
Expand All @@ -149,25 +149,35 @@ def init_raw_boldref_wf(
('out_file', 'bold_file'),
('out_report', 'validation_report'),
]),
(select_frames, outputnode, [('start_frame', 'skip_vols')]),
(calc_dummy_scans, outputnode, [('skip_vols_num', 'skip_vols')]),
(gen_avg, outputnode, [('out_file', 'boldref')]),
(get_dummy, outputnode, [('n_dummy', 'algo_dummy_scans')]),
]) # fmt:skip

return workflow


def _select_frames(in_file: str, start_frame: int, dummy_scans: int | None) -> tuple[int, list]:
def _select_frames(
in_file: str, ref_frame_start: int, dummy_scans: int | None
) -> tuple[int, list]:
import nibabel as nb
import numpy as np

img = nb.load(in_file)
img_len = img.shape[3]
if start_frame >= img_len:
start_frame = img_len - 1

if dummy_scans:
start_frame = dummy_scans
# Ensure start index is the largest of the two
# Will usually be `ref_frame_start`
start_frame = max(ref_frame_start, dummy_scans)
else:
start_frame = ref_frame_start

if start_frame >= img_len:
raise KeyError(
f'Caculating the BOLD reference starting on frame: {start_frame} but not enough BOLD '
f'volumes in {in_file}.'
)

t_mask = np.array([False] * img_len, dtype=bool)
t_mask[start_frame:] = True
Expand Down

0 comments on commit 69d3e6e

Please sign in to comment.