@@ -81,7 +81,8 @@ class FunctionalOutputs(NamedTuple):
8181 template_bold: BOLD resampled to template space.
8282 regressed_bold: Nuisance-regressed & non-bandpassed BOLD.
8383 cleaned_bold: Nuisance-regressed & bandpass-filtered BOLD.
84- regressor_file: Bandpass-filtered nuisance regressor ``.1D`` file.
84+ regressor_file: Nuisance regressor ``.1D`` files.
85+ bpf_regressor_file: Bandpass-filtered nuisance regressor ``.1D`` files.
8586 template_brain_mask: Brain mask warped to template space.
8687 """
8788
@@ -105,6 +106,7 @@ class FunctionalOutputs(NamedTuple):
105106 regressed_bold : dict [str , Path ]
106107 cleaned_bold : dict [str , Path ]
107108 regressor_file : dict [str , Path ]
109+ bpf_regressor_file : dict [str , Path ]
108110 template_brain_mask : Path
109111
110112
@@ -378,7 +380,8 @@ def single_session_preprocess(
378380 template_bold = template_bold ,
379381 regressed_bold = {r : regression [r ].regressed_bold for r in regressor_set },
380382 cleaned_bold = {r : cleaned [r ].regressed_bold for r in regressor_set },
381- regressor_file = filtered_regressors ,
383+ regressor_file = {r : regressors [r ].regressor_file for r in regressor_set },
384+ bpf_regressor_file = filtered_regressors ,
382385 template_brain_mask = tmpl_brain ,
383386 )
384387
@@ -392,12 +395,18 @@ class FunctionalLongOutputs(NamedTuple):
392395 bold: Preprocessed BOLD warped to longitudinal template space.
393396 bold_mask: Brain mask warped to longitudinal template space,
394397 or *None* if no mask was provided.
398+ regressed_bold: Nuisance-regressed (non-bandpassed) BOLD in longitudinal
399+ template space. Suitable for ALFF/fALFF.
400+ cleaned_bold: Nuisance-regressed + bandpass-filtered BOLD in longitudinal
401+ template space (Hallquist 2013).
395402 """
396403
397404 forward_xfm : Path
398405 sbref : Path
399406 bold : Path
400- bold_mask : Path | None = None
407+ bold_mask : Path
408+ regressed_bold : dict [str , Path ]
409+ cleaned_bold : dict [str , Path ]
401410
402411
403412def longitudinal_process (
@@ -407,41 +416,83 @@ def longitudinal_process(
407416 bold_to_anat_itk : Path ,
408417 sbref : Path ,
409418 bold : Path ,
410- bold_mask : Path | None ,
419+ bold_mask : Path ,
420+ regressor_files : dict [str , Path ],
411421) -> FunctionalLongOutputs :
412422 """Transform preprocessed functional outputs to longitudinal template space.
413423
414424 Assumes a longitudinal template has been generated, the subject-to-template
415425 composite warp is available, and anatomical data has already been processed
416426 to longitudinal template space.
417427
428+ Regressors are computed once during cross-sectional preprocessing and passed
429+ in via ``regressor_files``. Only the regression steps are re-run against the
430+ longitudinal space BOLD.
431+
432+ Steps:
433+ 1. Compose BOLD-to-anatomical + anatomical-to-longitudinal-template transforms.
434+ 2. Warp sbref (3D) and preproc BOLD (4D) to longitudinal template space.
435+ 3. Warp brain mask to longitudinal template space.
436+ 4. Nuisance regression without bandpass on longitudinal-space BOLD
437+ (for ALFF/fALFF).
438+ 5. Nuisance regression with simultaneous bandpass filtering on longitudinal-space
439+ BOLD (Hallquist 2013).
440+
418441 Args:
419442 template: Longitudinal template image.
420443 anat_to_template_xfm: T1w-to-longitudinal-template composite warp.
421444 bold_to_anat_itk: BOLD-to-T1w affine in ITK format.
422445 sbref: Motion reference (single-band reference) volume.
423446 bold: Preprocessed bold image.
424- bold_mask: Bold brain mask, if available.
447+ bold_mask: Bold brain mask in native space.
448+ regressor_files: Per-regressor nuisance regressor ``.1D`` files.
449+
425450
426451 Returns:
427452 :class:`FunctionalLongOutputs` with all non-null inputs transformed to template
428453 space.
429454 """
455+ # 1. Compose full BOLD -> longitudinal template transform
430456 bold_to_tpl_xfm = compose_transform (
431457 ref = template ,
432458 bold_to_anat_itk = bold_to_anat_itk ,
433459 anat_to_tpl_xfm = anat_to_template_xfm ,
434460 )
435461
462+ # 2. Warp sbref & bold to longitudinal space
463+ warped_sbref = func_transform (
464+ in_file = sbref , template = template , xfm = bold_to_tpl_xfm , strategy = "single"
465+ )
466+ warped_bold = func_transform (
467+ in_file = bold , template = template , xfm = bold_to_tpl_xfm , strategy = "chunked"
468+ )
469+
470+ # 3. Warp bold mask to longitudinal space
471+ warped_mask = mask_transform (mask = bold_mask , template = template , xfm = bold_to_tpl_xfm )
472+
473+ regression : dict [str , ApplyRegressionOutputs ] = {}
474+ cleaned : dict [str , ApplyRegressionOutputs ] = {}
475+ for reg , reg_file in regressor_files .items ():
476+ # 4. Nuisance regression without bandpass
477+ _logger .info ("%s nuisance regression (no bandpass)" , reg )
478+ regression [reg ] = apply_regression (
479+ bold_file = warped_bold ,
480+ brain_mask_file = warped_mask ,
481+ regressor_file = reg_file ,
482+ )
483+ # 5. Simultaneous regression + bandpass filtering (Hallquist 2013)
484+ _logger .info ("%s nuisance regression + bandpass filtering" , reg )
485+ cleaned [reg ] = apply_regression_bandpass (
486+ bold_file = warped_bold ,
487+ brain_mask_file = warped_mask ,
488+ regressor_file = reg_file ,
489+ )
490+
436491 return FunctionalLongOutputs (
437- sbref = func_transform ( # 3D volume
438- in_file = sbref , template = template , xfm = bold_to_tpl_xfm , strategy = "single"
439- ),
440- bold = func_transform (
441- in_file = bold , template = template , xfm = bold_to_tpl_xfm , strategy = "chunked"
442- ),
443- bold_mask = mask_transform (mask = bold_mask , template = template , xfm = bold_to_tpl_xfm )
444- if bold_mask
445- else None ,
446492 forward_xfm = bold_to_tpl_xfm ,
493+ sbref = warped_sbref ,
494+ bold = warped_bold ,
495+ bold_mask = warped_mask ,
496+ regressed_bold = {r : regression [r ].regressed_bold for r in regressor_files },
497+ cleaned_bold = {r : cleaned [r ].regressed_bold for r in regressor_files },
447498 )
0 commit comments