diff --git a/fmriprep/data/reports-spec.yml b/fmriprep/data/reports-spec.yml
index af0b3f698..4552a20e3 100644
--- a/fmriprep/data/reports-spec.yml
+++ b/fmriprep/data/reports-spec.yml
@@ -85,11 +85,14 @@ sections:
subtitle: Alignment of functional and anatomical MRI data (surface driven)
- bids: {datatype: figures, desc: rois, suffix: bold}
caption: Brain mask calculated on the BOLD signal (red contour), along with the
- masks used for a/tCompCor.
The aCompCor mask (magenta contour) is a conservative
- CSF and white-matter mask for extracting physiological and movement confounds.
-
The fCompCor mask (blue contour) contains the top 5% most variable voxels
- within a heavily-eroded brain-mask.
- subtitle: Brain mask and (temporal/anatomical) CompCor ROIs
+ regions of interest (ROIs) used in a/tCompCor for extracting
+ physiological and movement confounding components.
+ The anatomical CompCor ROI (magenta contour) is a mask combining
+ CSF and WM (white-matter), where voxels containing a minimal partial volume
+ of GM have been removed.
+ The temporal CompCor ROI (blue contour) contains the top 2% most
+ variable voxels within the brain mask.
+ subtitle: Brain mask and (anatomical/temporal) CompCor ROIs
- bids:
datatype: figures
desc: '[at]compcor'
@@ -107,10 +110,11 @@ sections:
in the BOLD data. Global signals calculated within the whole-brain (GS), within
the white-matter (WM) and within cerebro-spinal fluid (CSF) show the mean BOLD
signal in their corresponding masks. DVARS and FD show the standardized DVARS
- and framewise-displacement measures for each time point.
A carpet plot
- shows the time series for all voxels within the brain mask, or if ``--cifti-output``
- was enabled, all grayordinates. Voxels are grouped into cortical (dark/light blue),
- and subcortical (orange) gray matter, cerebellum (green) and white matter and CSF
+ and framewise-displacement measures for each time point.
+ A carpet plot shows the time series for all voxels within the brain mask,
+ or if --cifti-output was enabled, all grayordinates.
+ Voxels are grouped into cortical (dark/light blue), and subcortical (orange)
+ gray matter, cerebellum (green) and white matter and CSF
(red), indicated by the color map on the left-hand side.
subtitle: BOLD Summary
- bids: {datatype: figures, desc: 'confoundcorr', suffix: bold}
diff --git a/fmriprep/interfaces/confounds.py b/fmriprep/interfaces/confounds.py
index a4c8b863c..2a8a68cfe 100644
--- a/fmriprep/interfaces/confounds.py
+++ b/fmriprep/interfaces/confounds.py
@@ -18,12 +18,41 @@
from nipype.utils.filemanip import fname_presuffix
from nipype.interfaces.base import (
traits, TraitedSpec, BaseInterfaceInputSpec, File, Directory, isdefined,
- SimpleInterface
+ SimpleInterface, InputMultiObject, OutputMultiObject
)
LOGGER = logging.getLogger('nipype.interface')
+class _aCompCorMasksInputSpec(BaseInterfaceInputSpec):
+ in_vfs = InputMultiObject(File(exists=True), desc="Input volume fractions.")
+ is_aseg = traits.Bool(False, usedefault=True,
+ desc="Whether the input volume fractions come from FS' aseg.")
+ bold_zooms = traits.Tuple(traits.Float, traits.Float, traits.Float, mandatory=True,
+ desc="BOLD series zooms")
+
+
+class _aCompCorMasksOutputSpec(TraitedSpec):
+ out_masks = OutputMultiObject(File(exists=True),
+ desc="CSF, WM and combined masks, respectively")
+
+
+class aCompCorMasks(SimpleInterface):
+ """Generate masks in T1w space for aCompCor."""
+
+ input_spec = _aCompCorMasksInputSpec
+ output_spec = _aCompCorMasksOutputSpec
+
+ def _run_interface(self, runtime):
+ from ..utils.confounds import acompcor_masks
+ self._results["out_masks"] = acompcor_masks(
+ self.inputs.in_vfs,
+ self.inputs.is_aseg,
+ self.inputs.bold_zooms,
+ )
+ return runtime
+
+
class GatherConfoundsInputSpec(BaseInterfaceInputSpec):
signals = File(exists=True, desc='input signals')
dvars = File(exists=True, desc='file containing DVARS')
diff --git a/fmriprep/utils/confounds.py b/fmriprep/utils/confounds.py
new file mode 100644
index 000000000..25a19980c
--- /dev/null
+++ b/fmriprep/utils/confounds.py
@@ -0,0 +1,136 @@
+"""Utilities for confounds manipulation."""
+
+
+def mask2vf(in_file, zooms=None, out_file=None):
+ """
+ Convert a binary mask on a volume fraction map.
+
+ The algorithm simply applies a Gaussian filter with the kernel size scaled
+ by the zooms given as argument.
+
+ """
+ import numpy as np
+ import nibabel as nb
+ from scipy.ndimage import gaussian_filter
+
+ img = nb.load(in_file)
+ imgzooms = np.array(img.header.get_zooms()[:3], dtype=float)
+ if zooms is None:
+ zooms = imgzooms
+
+ zooms = np.array(zooms, dtype=float)
+ sigma = 0.5 * (zooms / imgzooms)
+
+ data = gaussian_filter(img.get_fdata(dtype=np.float32), sigma=sigma)
+
+ max_data = np.percentile(data[data > 0], 99)
+ data = np.clip(data / max_data, a_min=0, a_max=1)
+
+ if out_file is None:
+ return data
+
+ hdr = img.header.copy()
+ hdr.set_data_dtype(np.float32)
+ nb.Nifti1Image(data.astype(np.float32), img.affine, hdr).to_filename(out_file)
+ return out_file
+
+
+def acompcor_masks(in_files, is_aseg=False, zooms=None):
+ """
+ Generate aCompCor masks.
+
+ This function selects the CSF partial volume map from the input,
+ and generates the WM and combined CSF+WM masks for aCompCor.
+
+ The implementation deviates from Behzadi et al.
+ Their original implementation thresholded the CSF and the WM partial-volume
+ masks at 0.99 (i.e., 99% of the voxel volume is filled with a particular tissue),
+ and then binary eroded that 2 voxels:
+
+ > Anatomical data were segmented into gray matter, white matter,
+ > and CSF partial volume maps using the FAST algorithm available
+ > in the FSL software package (Smith et al., 2004). Tissue partial
+ > volume maps were linearly interpolated to the resolution of the
+ > functional data series using AFNI (Cox, 1996). In order to form
+ > white matter ROIs, the white matter partial volume maps were
+ > thresholded at a partial volume fraction of 0.99 and then eroded by
+ > two voxels in each direction to further minimize partial voluming
+ > with gray matter. CSF voxels were determined by first thresholding
+ > the CSF partial volume maps at 0.99 and then applying a threedimensional
+ > nearest neighbor criteria to minimize multiple tissue
+ > partial voluming. Since CSF regions are typically small compared
+ > to white matter regions mask, erosion was not applied.
+
+ This particular procedure is not generalizable to BOLD data with different voxel zooms
+ as the mathematical morphology operations will be scaled by those.
+ Also, from reading the excerpt above and the tCompCor description, I (@oesteban)
+ believe that they always operated slice-wise given the large slice-thickness of
+ their functional data.
+
+ Instead, *fMRIPrep*'s implementation deviates from Behzadi's implementation on two
+ aspects:
+
+ * the masks are prepared in high-resolution, anatomical space and then
+ projected into BOLD space; and,
+ * instead of using binary erosion, a dilated GM map is generated -- thresholding
+ the corresponding PV map at 0.05 (i.e., pixels containing at least 5% of GM tissue)
+ and then subtracting that map from the CSF, WM and CSF+WM (combined) masks.
+ This should be equivalent to eroding the masks, except that the erosion
+ only happens at direct interfaces with GM.
+
+ When the probseg maps provene from FreeSurfer's ``recon-all`` (i.e., they are
+ discrete), binary maps are *transformed* into some sort of partial volume maps
+ by means of a Gaussian smoothing filter with sigma adjusted by the size of the
+ BOLD data.
+
+ """
+ from pathlib import Path
+ import numpy as np
+ import nibabel as nb
+ from scipy.ndimage import binary_dilation
+ from skimage.morphology import ball
+
+ csf_file = in_files[2] # BIDS labeling (CSF=2; last of list)
+ # Load PV maps (fast) or segments (recon-all)
+ gm_vf = nb.load(in_files[0])
+ wm_vf = nb.load(in_files[1])
+ csf_vf = nb.load(csf_file)
+
+ # Prepare target zooms
+ imgzooms = np.array(gm_vf.header.get_zooms()[:3], dtype=float)
+ if zooms is None:
+ zooms = imgzooms
+ zooms = np.array(zooms, dtype=float)
+
+ if not is_aseg:
+ gm_data = gm_vf.get_fdata() > 0.05
+ wm_data = wm_vf.get_fdata()
+ csf_data = csf_vf.get_fdata()
+ else:
+ csf_file = mask2vf(
+ csf_file,
+ zooms=zooms,
+ out_file=str(Path("acompcor_csf.nii.gz").absolute()),
+ )
+ csf_data = nb.load(csf_file).get_fdata()
+ wm_data = mask2vf(in_files[1], zooms=zooms)
+
+ # We do not have partial volume maps (recon-all route)
+ gm_data = np.asanyarray(gm_vf.dataobj, np.uint8) > 0
+
+ # Dilate the GM mask
+ gm_data = binary_dilation(gm_data, structure=ball(3))
+
+ # Output filenames
+ wm_file = str(Path("acompcor_wm.nii.gz").absolute())
+ combined_file = str(Path("acompcor_wmcsf.nii.gz").absolute())
+
+ # Prepare WM mask
+ wm_data[gm_data] = 0 # Make sure voxel does not contain GM
+ nb.Nifti1Image(wm_data, gm_vf.affine, gm_vf.header).to_filename(wm_file)
+
+ # Prepare combined CSF+WM mask
+ comb_data = csf_data + wm_data
+ comb_data[gm_data] = 0 # Make sure voxel does not contain GM
+ nb.Nifti1Image(comb_data, gm_vf.affine, gm_vf.header).to_filename(combined_file)
+ return [csf_file, wm_file, combined_file]
diff --git a/fmriprep/workflows/bold/base.py b/fmriprep/workflows/bold/base.py
index a8494ff74..fa11d60b5 100644
--- a/fmriprep/workflows/bold/base.py
+++ b/fmriprep/workflows/bold/base.py
@@ -349,6 +349,7 @@ def init_func_preproc_wf(bold_file):
bold_confounds_wf = init_bold_confs_wf(
mem_gb=mem_gb['largemem'],
metadata=metadata,
+ freesurfer=freesurfer,
regressors_all_comps=config.workflow.regressors_all_comps,
regressors_fd_th=config.workflow.regressors_fd_th,
regressors_dvars_th=config.workflow.regressors_dvars_th,
diff --git a/fmriprep/workflows/bold/confounds.py b/fmriprep/workflows/bold/confounds.py
index 3780248d6..2416ab567 100644
--- a/fmriprep/workflows/bold/confounds.py
+++ b/fmriprep/workflows/bold/confounds.py
@@ -27,6 +27,7 @@ def init_bold_confs_wf(
regressors_all_comps,
regressors_dvars_th,
regressors_fd_th,
+ freesurfer=False,
name="bold_confs_wf",
):
"""
@@ -126,6 +127,7 @@ def init_bold_confs_wf(
from niworkflows.interfaces.fixes import FixHeaderApplyTransforms as ApplyTransforms
from niworkflows.interfaces.images import SignalExtraction
from niworkflows.interfaces.masks import ROIsPlot
+ from niworkflows.interfaces.nibabel import ApplyMask, Binarize
from niworkflows.interfaces.patches import (
RobustACompCor as ACompCor,
RobustTCompCor as TCompCor,
@@ -134,11 +136,17 @@ def init_bold_confs_wf(
CompCorVariancePlot, ConfoundsCorrelationPlot
)
from niworkflows.interfaces.utils import (
- TPM2ROI, AddTPMs, AddTSVHeader, TSV2JSON, DictMerge
+ AddTSVHeader, TSV2JSON, DictMerge
+ )
+ from ...interfaces.confounds import aCompCorMasks
+
+ gm_desc = (
+ "dilating a GM mask extracted from the FreeSurfer's *aseg* segmentation" if freesurfer
+ else "thresholding the corresponding partial volume map at 0.05"
)
workflow = Workflow(name=name)
- workflow.__desc__ = """\
+ workflow.__desc__ = f"""\
Several confounding time-series were calculated based on the
*preprocessed BOLD*: framewise displacement (FD), DVARS and
three region-wise global signals.
@@ -155,15 +163,18 @@ def init_bold_confs_wf(
*preprocessed BOLD* time-series (using a discrete cosine filter with
128s cut-off) for the two *CompCor* variants: temporal (tCompCor)
and anatomical (aCompCor).
-tCompCor components are then calculated from the top 5% variable
-voxels within a mask covering the subcortical regions.
-This subcortical mask is obtained by heavily eroding the brain mask,
-which ensures it does not include cortical GM regions.
-For aCompCor, components are calculated within the intersection of
-the aforementioned mask and the union of CSF and WM masks calculated
-in T1w space, after their projection to the native space of each
-functional run (using the inverse BOLD-to-T1w transformation). Components
-are also calculated separately within the WM and CSF masks.
+tCompCor components are then calculated from the top 2% variable
+voxels within the brain mask.
+For aCompCor, three probabilistic masks (CSF, WM and combined CSF+WM)
+are generated in anatomical space.
+The implementation differs from that of Behzadi et al. in that instead
+of eroding the masks by 2 pixels on BOLD space, the aCompCor masks are
+subtracted a mask of pixels that likely contain a volume fraction of GM.
+This mask is obtained by {gm_desc}, and it ensures components are not extracted
+from voxels containing a minimal fraction of GM.
+Finally, these masks are resampled into BOLD space and binarized by
+thresholding at 0.99 (as in the original implementation).
+Components are also calculated separately within the WM and CSF masks.
For each CompCor decomposition, the *k* components with the largest singular
values are retained, such that the retained components' time series are
sufficient to explain 50 percent of variance across the nuisance mask (CSF,
@@ -174,9 +185,9 @@ def init_bold_confs_wf(
The confound time series derived from head motion estimates and global
signals were expanded with the inclusion of temporal derivatives and
quadratic terms for each [@confounds_satterthwaite_2013].
-Frames that exceeded a threshold of {fd} mm FD or {dv} standardised DVARS
-were annotated as motion outliers.
-""".format(fd=regressors_fd_th, dv=regressors_dvars_th)
+Frames that exceeded a threshold of {regressors_fd_th} mm FD or
+{regressors_dvars_th} standardised DVARS were annotated as motion outliers.
+"""
inputnode = pe.Node(niu.IdentityInterface(
fields=['bold', 'bold_mask', 'movpar_file', 'rmsd_file',
'skip_vols', 't1w_mask', 't1w_tpms', 't1_bold_xform']),
@@ -185,33 +196,6 @@ def init_bold_confs_wf(
fields=['confounds_file', 'confounds_metadata']),
name='outputnode')
- # Get masks ready in T1w space
- acc_tpm = pe.Node(AddTPMs(indices=[1, 2]), # BIDS convention (WM=1, CSF=2)
- name='acc_tpm') # acc stands for aCompCor
- csf_roi = pe.Node(TPM2ROI(erode_mm=0, mask_erode_mm=30), name='csf_roi')
- wm_roi = pe.Node(TPM2ROI(
- erode_prop=0.6, mask_erode_prop=0.6**3), # 0.6 = radius; 0.6^3 = volume
- name='wm_roi')
- acc_roi = pe.Node(TPM2ROI(
- erode_prop=0.6, mask_erode_prop=0.6**3), # 0.6 = radius; 0.6^3 = volume
- name='acc_roi')
-
- # Map ROIs in T1w space into BOLD space
- csf_tfm = pe.Node(ApplyTransforms(interpolation='NearestNeighbor', float=True),
- name='csf_tfm', mem_gb=0.1)
- wm_tfm = pe.Node(ApplyTransforms(interpolation='NearestNeighbor', float=True),
- name='wm_tfm', mem_gb=0.1)
- acc_tfm = pe.Node(ApplyTransforms(interpolation='NearestNeighbor', float=True),
- name='acc_tfm', mem_gb=0.1)
- tcc_tfm = pe.Node(ApplyTransforms(interpolation='NearestNeighbor', float=True),
- name='tcc_tfm', mem_gb=0.1)
-
- # Ensure ROIs don't go off-limits (reduced FoV)
- csf_msk = pe.Node(niu.Function(function=_maskroi), name='csf_msk')
- wm_msk = pe.Node(niu.Function(function=_maskroi), name='wm_msk')
- acc_msk = pe.Node(niu.Function(function=_maskroi), name='acc_msk')
- tcc_msk = pe.Node(niu.Function(function=_maskroi), name='tcc_msk')
-
# DVARS
dvars = pe.Node(nac.ComputeDVARS(save_nstd=True, save_std=True, remove_zerovariance=True),
name="dvars", mem_gb=mem_gb)
@@ -220,21 +204,29 @@ def init_bold_confs_wf(
fdisp = pe.Node(nac.FramewiseDisplacement(parameter_source="SPM"),
name="fdisp", mem_gb=mem_gb)
- # a/t-CompCor
- mrg_lbl_cc = pe.Node(niu.Merge(3), name='merge_rois_cc', run_without_submitting=True)
+ # Generate aCompCor probseg maps
+ acc_masks = pe.Node(aCompCorMasks(is_aseg=freesurfer), name="acc_masks")
+
+ # Resample probseg maps in BOLD space via T1w-to-BOLD transform
+ acc_msk_tfm = pe.MapNode(ApplyTransforms(
+ interpolation='Gaussian', float=False), iterfield=["input_image"],
+ name='acc_msk_tfm', mem_gb=0.1)
+ acc_msk_brain = pe.MapNode(ApplyMask(), name="acc_msk_brain",
+ iterfield=["in_file"])
+ acc_msk_bin = pe.MapNode(Binarize(thresh_low=0.99), name='acc_msk_bin',
+ iterfield=["in_file"])
+ acompcor = pe.Node(
+ ACompCor(components_file='acompcor.tsv', header_prefix='a_comp_cor_', pre_filter='cosine',
+ save_pre_filter=True, save_metadata=True, mask_names=['CSF', 'WM', 'combined'],
+ merge_method='none', failure_mode='NaN'),
+ name="acompcor", mem_gb=mem_gb)
tcompcor = pe.Node(
TCompCor(components_file='tcompcor.tsv', header_prefix='t_comp_cor_', pre_filter='cosine',
- save_pre_filter=True, save_metadata=True, percentile_threshold=.05,
+ save_pre_filter=True, save_metadata=True, percentile_threshold=.02,
failure_mode='NaN'),
name="tcompcor", mem_gb=mem_gb)
- acompcor = pe.Node(
- ACompCor(components_file='acompcor.tsv', header_prefix='a_comp_cor_', pre_filter='cosine',
- save_pre_filter=True, save_metadata=True, mask_names=['combined', 'CSF', 'WM'],
- merge_method='none', failure_mode='NaN'),
- name="acompcor", mem_gb=mem_gb)
-
# Set number of components
if regressors_all_comps:
acompcor.inputs.num_components = 'all'
@@ -249,8 +241,11 @@ def init_bold_confs_wf(
acompcor.inputs.repetition_time = metadata['RepetitionTime']
# Global and segment regressors
+ gs_select = pe.Node(niu.Select(index=[0, 1]), name="gs_select",
+ run_without_submitting=True)
signals_class_labels = ["csf", "white_matter", "global_signal"]
- mrg_lbl = pe.Node(niu.Merge(3), name='merge_rois', run_without_submitting=True)
+ merge_rois = pe.Node(niu.Merge(2, ravel_inputs=True), name='merge_rois',
+ run_without_submitting=True)
signals = pe.Node(SignalExtraction(class_labels=signals_class_labels),
name="signals", mem_gb=mem_gb)
@@ -297,7 +292,8 @@ def init_bold_confs_wf(
name='spike_regressors')
# Generate reportlet (ROIs)
- mrg_compcor = pe.Node(niu.Merge(2), name='merge_compcor', run_without_submitting=True)
+ mrg_compcor = pe.Node(niu.Merge(2, ravel_inputs=True),
+ name='mrg_compcor', run_without_submitting=True)
rois_plot = pe.Node(ROIsPlot(colors=['b', 'magenta'], generate_report=True),
name='rois_plot', mem_gb=mem_gb)
@@ -327,67 +323,38 @@ def init_bold_confs_wf(
name='ds_report_conf_corr', run_without_submitting=True,
mem_gb=DEFAULT_MEMORY_MIN_GB)
- def _pick_csf(files):
- return files[2] # after smriprep#189, this is BIDS-compliant.
-
- def _pick_wm(files):
- return files[1] # after smriprep#189, this is BIDS-compliant.
+ def _last(inlist):
+ return inlist[-1]
workflow.connect([
- # Massage ROIs (in T1w space)
- (inputnode, acc_tpm, [('t1w_tpms', 'in_files')]),
- (inputnode, csf_roi, [(('t1w_tpms', _pick_csf), 'in_tpm'),
- ('t1w_mask', 'in_mask')]),
- (inputnode, wm_roi, [(('t1w_tpms', _pick_wm), 'in_tpm'),
- ('t1w_mask', 'in_mask')]),
- (inputnode, acc_roi, [('t1w_mask', 'in_mask')]),
- (acc_tpm, acc_roi, [('out_file', 'in_tpm')]),
- # Map ROIs to BOLD
- (inputnode, csf_tfm, [('bold_mask', 'reference_image'),
- ('t1_bold_xform', 'transforms')]),
- (csf_roi, csf_tfm, [('roi_file', 'input_image')]),
- (inputnode, wm_tfm, [('bold_mask', 'reference_image'),
- ('t1_bold_xform', 'transforms')]),
- (wm_roi, wm_tfm, [('roi_file', 'input_image')]),
- (inputnode, acc_tfm, [('bold_mask', 'reference_image'),
- ('t1_bold_xform', 'transforms')]),
- (acc_roi, acc_tfm, [('roi_file', 'input_image')]),
- (inputnode, tcc_tfm, [('bold_mask', 'reference_image'),
- ('t1_bold_xform', 'transforms')]),
- (csf_roi, tcc_tfm, [('eroded_mask', 'input_image')]),
- # Mask ROIs with bold_mask
- (inputnode, csf_msk, [('bold_mask', 'in_mask')]),
- (inputnode, wm_msk, [('bold_mask', 'in_mask')]),
- (inputnode, acc_msk, [('bold_mask', 'in_mask')]),
- (inputnode, tcc_msk, [('bold_mask', 'in_mask')]),
# connect inputnode to each non-anatomical confound node
(inputnode, dvars, [('bold', 'in_file'),
('bold_mask', 'in_mask')]),
(inputnode, fdisp, [('movpar_file', 'in_file')]),
- # tCompCor
- (inputnode, tcompcor, [('bold', 'realigned_file')]),
- (inputnode, tcompcor, [('skip_vols', 'ignore_initial_volumes')]),
- (tcc_tfm, tcc_msk, [('output_image', 'roi_file')]),
- (tcc_msk, tcompcor, [('out', 'mask_files')]),
-
# aCompCor
- (inputnode, acompcor, [('bold', 'realigned_file')]),
- (inputnode, acompcor, [('skip_vols', 'ignore_initial_volumes')]),
- (acc_tfm, acc_msk, [('output_image', 'roi_file')]),
- (acc_msk, mrg_lbl_cc, [('out', 'in1')]),
- (csf_msk, mrg_lbl_cc, [('out', 'in2')]),
- (wm_msk, mrg_lbl_cc, [('out', 'in3')]),
- (mrg_lbl_cc, acompcor, [('out', 'mask_files')]),
+ (inputnode, acompcor, [("bold", "realigned_file"),
+ ("skip_vols", "ignore_initial_volumes")]),
+ (inputnode, acc_masks, [("t1w_tpms", "in_vfs"),
+ (("bold", _get_zooms), "bold_zooms")]),
+ (inputnode, acc_msk_tfm, [("t1_bold_xform", "transforms"),
+ ("bold_mask", "reference_image")]),
+ (inputnode, acc_msk_brain, [("bold_mask", "in_mask")]),
+ (acc_masks, acc_msk_tfm, [("out_masks", "input_image")]),
+ (acc_msk_tfm, acc_msk_brain, [("output_image", "in_file")]),
+ (acc_msk_brain, acc_msk_bin, [("out_file", "in_file")]),
+ (acc_msk_bin, acompcor, [("out_file", "mask_files")]),
+ # tCompCor
+ (inputnode, tcompcor, [("bold", "realigned_file"),
+ ("skip_vols", "ignore_initial_volumes"),
+ ("bold_mask", "mask_files")]),
# Global signals extraction (constrained by anatomy)
(inputnode, signals, [('bold', 'in_file')]),
- (csf_tfm, csf_msk, [('output_image', 'roi_file')]),
- (csf_msk, mrg_lbl, [('out', 'in1')]),
- (wm_tfm, wm_msk, [('output_image', 'roi_file')]),
- (wm_msk, mrg_lbl, [('out', 'in2')]),
- (inputnode, mrg_lbl, [('bold_mask', 'in3')]),
- (mrg_lbl, signals, [('out', 'label_files')]),
+ (acc_msk_bin, gs_select, [('out_file', 'inlist')]),
+ (gs_select, merge_rois, [('out', 'in1')]),
+ (inputnode, merge_rois, [('bold_mask', 'in2')]),
+ (merge_rois, signals, [('out', 'label_files')]),
# Collate computed confounds together
(inputnode, add_motion_headers, [('movpar_file', 'in_file')]),
@@ -421,7 +388,7 @@ def _pick_wm(files):
(inputnode, rois_plot, [('bold', 'in_file'),
('bold_mask', 'in_mask')]),
(tcompcor, mrg_compcor, [('high_variance_masks', 'in1')]),
- (acc_msk, mrg_compcor, [('out', 'in2')]),
+ (acc_msk_bin, mrg_compcor, [(('out_file', _last), 'in2')]),
(mrg_compcor, rois_plot, [('out', 'in_rois')]),
(rois_plot, ds_report_bold_rois, [('out_report', 'in_file')]),
(tcompcor, mrg_cc_metadata, [('metadata_file', 'in1')]),
@@ -792,7 +759,6 @@ def _remove_volumes(bold_file, skip_vols):
bold_img = nb.load(bold_file)
bold_img.__class__(bold_img.dataobj[..., skip_vols:],
bold_img.affine, bold_img.header).to_filename(out)
-
return out
@@ -813,21 +779,9 @@ def _add_volumes(bold_file, bold_cut_file, skip_vols):
out = fname_presuffix(bold_cut_file, suffix='_addnonsteady')
bold_img.__class__(bold_data, bold_img.affine, bold_img.header).to_filename(out)
-
return out
-def _maskroi(in_mask, roi_file):
- import numpy as np
+def _get_zooms(in_file):
import nibabel as nb
- from nipype.utils.filemanip import fname_presuffix
-
- roi = nb.load(roi_file)
- roidata = roi.get_data().astype(np.uint8)
- msk = nb.load(in_mask).get_data().astype(bool)
- roidata[~msk] = 0
- roi.set_data_dtype(np.uint8)
-
- out = fname_presuffix(roi_file, suffix='_boldmsk')
- roi.__class__(roidata, roi.affine, roi.header).to_filename(out)
- return out
+ return tuple(nb.load(in_file).header.get_zooms()[:3])