BrainGnomes Postprocessing Walkthrough
Michael Hallquist
Source:vignettes/postprocessing.Rmd
postprocessing.Rmd
Overview
This vignette focuses on the postprocessing features
of the BrainGnomes
package. Postprocessing refers to the
optional steps that can be applied after fMRIPrep has generated
preprocessed BOLD files. These operations include masking, spatial
smoothing, ICA‑AROMA denoising, scrubbing, temporal filtering, intensity
normalisation and confound handling. Configuration of these steps occurs
via setup_project()
when a project is first created or
later through edit_project()
.
To get started, make sure you have created a project configuration
object (here, we will call it scfg
) using
setup_project()
or loaded one with
load_project()
.
library(BrainGnomes)
# scfg <- setup_project() # or load_project("/path/to/project")
Enabling Postprocessing
During setup_project()
you will be asked whether to
enable postprocessing for BOLD data. You can toggle this choice later
with edit_project(scfg)
.
scfg <- edit_project(scfg) # choose "Postprocessing" from the menu
Once enabled, each step described below can be configured interactively.
Postprocessing streams
Postprocessing supports multiple streams, allowing you to postprocess data in multiple ways. For example, perhaps you’d like to compare results for a 5mm versus 6mm spatial smoothing kernel.
Each stream also asks about which files should be entered into the stream. For example, files with ‘rest’ in their name could be postprocessed in one way and files with ‘nback’ could be processed a different way.
The menu for setting up postprocessing streams looks like this:
Current postprocessing streams:
(none defined yet)
Modify postprocessing streams:
1: Add a stream
2: Edit a stream
3: Delete a stream
4: Show stream settings
5: Finish
If you choose to add a stream, you will be guided through major decision points.
Global Options
The first prompts define global postprocessing behavior. You specify
which fMRIPrep outputs to process (input_regex
), the BIDS
description for final files (bids_desc
), whether to keep
intermediate images, and if existing outputs should be overwritten. The
TR of your scans and an optional brain mask file are also collected.
Selecting BOLD files with input_regex
The input_regex
setting controls which preprocessed
NIfTI files are entered into this postprocessing stream. Instead of
writing a full regular expression, you may supply BIDS entity-value
pairs that are converted to a regex at runtime. For example:
selects any file containing task-ridl
and
desc-preproc
with a bold
suffix, producing
".*task-ridl(_[^_]+)*_desc-preproc(_[^_]+)*_bold\\.nii(\\.gz)?$"
.
To provide a custom regular expression, prefix it with
regex:
:
This pattern matches any file containing task-rest
and
ending in _desc-preproc_bold.nii.gz
. You could create
another configuration for task-based data using a different
expression.
Output naming with bids_desc
Every postprocessing stream is also defined by a BIDS
description field. This field (bids_desc
) is used to
label of every postprocessed for the stream. For example, if the input
filename is
sub-01_task-rest_run-1_space-MNI152NLin2009cAsym_desc-preproc_bold.nii.gz
and you choose bids_desc = "clean"
, the output will be
named
sub-01_task-rest_run-1_space-MNI152NLin2009cAsym_desc-clean_bold.nii.gz
Individual Processing Steps
After the global settings, the postprocessing menus walk through a sequence of setup functions for each optional step. You may enable or skip any of them. Below we summarize the purpose of each step, how to enable it, and key options the setup functions will prompt for.
Applying a Brain Mask
setup_apply_mask()
controls whether to apply a binary
brain mask to all BOLD runs. The prompt explains the rationale and lets
you provide a mask file and output prefix.
Applying a brain mask to your fMRI data ensures that only in-brain voxels are retained during analysis.
This step is optional but often recommended for improving efficiency and accuracy in subsequent processing.
The mask will be applied as a binary filter to the 4D functional data, zeroing out signal outside the brain.
You can specify a custom mask file (in the same space and resolution as your fMRI data), or use the default
mask produced by your preprocessing pipeline.
Do you want to apply a brain mask to your fMRI data?
Options include a path to the mask file (mask_file
) and
a prefix for the masked output (prefix
, default
"m"
).【F:R/setup_postprocess.R†L496-L544】
Spatial Smoothing
Spatial smoothing applies a 3D Gaussian kernel to the BOLD fMRI data, which typically increases the signal-to-noise ratio and improves overlap across subjects by reducing high-frequency spatial noise.
You will be asked to specify the size of the smoothing kernel in millimeters (full width at half maximum, or FWHM). Common choices range from 4mm to 8mm. Smoothing is achieved using FSL’s susan command, which applies a nonlinear filtering algorithm that seeks to smooth together only voxels of similar intensities. Technical details are here
-------------------------------------------------------------------------------------------
Spatial smoothing applies a 3D Gaussian kernel to the BOLD fMRI data, which increases
the signal-to-noise ratio and improves overlap across subjects by reducing high-frequency
spatial noise.
You will be asked to specify the size of the smoothing kernel in millimeters
(full width at half maximum, or FWHM). Common choices range from 4mm to 8mm.
Do you want to apply spatial smoothing to the BOLD data as part of postprocessing?
Apply spatial smoothing? (yes/no) > y
Spatial smoothing FWHM (mm) > 5
ICA‑AROMA Denoising
For ICA-AROMA denoising to be applied during postprocessing, you must
enable it in the project configuration and it must have been run at some
point (i.e., Run ICA-AROMA?
in run_project()
).
This will produce a set of output files, including
*_desc-MELODIC_mixing.tsv
(the mixing matrix), which
contains the spatiotemporal components identified both as noise and
signal by the algorithm.
When ICA‑AROMA outputs are available, you can regress out the noise components as a postprocessing step. Building on the original papers (Pruim et al., 2015), we recommend ‘nonaggressive’ removal, which only removes the variance in ‘noise’ components that is not correlated with signal components (i.e., partial effects in multiple regression). As detailed below, if AROMA is applied to the fMRI data, the components will also be regressed out of any confounds. Note that as of 8Aug2025, only nonaggressive denoising is supported, regardless of how you respond to the prompts.
------------------------------------------------------------------------------------------------------------------------
ICA-AROMA identifies motion-related independent components from the fMRI data and outputs
noise regressors (e.g., *_desc-aroma_timeseries.tsv) that can be used to denoise the BOLD signal.
The pipeline asks you decide about whether to run the data through ICA-AROMA, which generates various
files, but does not make any changes to the fMRI data.
Here, you can denoise the data using ICA-AROMA, which step applies the noise regressors
to the data using voxelwise regression, either:
- nonaggressively (recommended), which removes *unique* variance from noise components only, or
- aggressively, which removes all variance associated with the noise components.
Do you want to apply ICA-AROMA denoising during postprocessing?
Apply AROMA denoising? (yes/no) > y
Use nonaggressive denoising? (yes/no; Press enter to accept default: yes) > y
Temporal filtering
Temporal filtering removes low- and/or high-frequency components from the fMRI time series. A high-pass filter (e.g., 0.008 Hz) is commonly used to remove slow scanner drift, while a low-pass filter can remove physiological noise such as respiratory or cardiac fluctuations. Temporal filtering is common in both task-based and resting-state fMRI analyses. In task fMRI, we often apply a gentle high-pass filter (e.g. .008 Hz) to remove low-frequency drift that can weaken GLM analyses that assume a constant baseline. In resting-state analyses, people often bandpass filter.
We support both fslmaths -bptf and a Butterworth temporal filter. The former is more common for task-based fMRI and the latter with resting-state fMRI. If filtering is enabled, you will be asked to choose between these filtering methods.
Per FSL’s documentation, -bptf uses a local fit of a straight line (Gaussian-weighted within the line to give a smooth response) to remove low frequency artifacts. This is preferable to sharp rolloff FIR-based filtering as it does not introduce autocorrelations into the data. These ‘ringing artifacts’ can induce negative autocorrelations, which interfere with GLM software that correects for autocorrelation (e.g., Tukey tapering in FSL feat). Low-pass temporal filtering reduces high frequency noise by Gaussian smoothing, but also reduces the strength of the signal of interest, particularly for single-event experiments.
Butterworth filtering is a common temporal filtering technique used in fMRI postprocessing to attenuate low-frequency drifts and/or high-frequency noise while preserving the passband signal with minimal distortion. Unlike sharp cutoff filters, the Butterworth filter has a maximally flat frequency response in the passband, making it well-suited for applications requiring smooth transitions. In fMRI, it is typically applied as a band-pass or high-pass filter to remove slow scanner drifts and physiological noise outside the frequency range of interest (e.g., 0.01–0.1 Hz for resting-state analyses). To avoid phase distortion, BrainGnomes applies the filter bidirectionally (i.e., using forward and reverse passes). BrainGnomes also uses a second-order Butterworth filter, which has a good balance between frequency precision and smooth rolloff (higher orders give more precise frequency control at the edges, but have bigger risks of artifacts).
One of the virtues of FSL’s Gaussian line smoothing approach is that it has a very gradual rolloff. That said, the frequency response is broad and not sharply defined, which can lead to partial attenuation of signals near cutoff (e.g., if your upper cutoff is .10 Hz, you might still see attenuation at .09 Hz). The Butterworth filter will have more precise control over the frequencies retained in the data but may have a slightly higher risk of messing up the autocorrelation structure (due to ringing).
Here is the order of prompts with example responses. Note that if you skip one of the cutoffs (press enter at the prompt), then that side of the filter will not be applied. For example, if you only enter a low-pass cutoff
Apply temporal filtering? > yes
Low-pass cutoff (Hz) (Press enter to skip) > 0.009
High-pass cutoff (Hz) (Press enter to skip) > 0.08
Filtering method (fslmaths/butterworth) > butterworth
Scrubbing High‑Motion Volumes
‘Scrubbing’ refers to a set of procedures for reducing the impact of high-motion/high-artifact volumes on fMRI analysis. The most typical approach is to flag volumes based on their framewise displacement, a measure of overall head displacement in any direction/rotation, and to remove those volumes from the time series. That said, scrubbing encompasses a wider range of decisions, such as whether to interpolate the bad timepoints and whether to generate spike regressors while leaving the fMRI data intact (e.g., for subsequent use in task-based analysis). Importantly, scrubbing has tricky interactions with other postprocessing steps, especially confound regression and temporal filtering.
Temporal filtering removes specific frequencies from the data, so we cannot simply delete volumes from the time series and hope to have the filtering work as intended. Moreover, if we apply the temporal filter prior to scrubbing, we can induce ‘ringing artifacts’ where large signal changes due to head motion are propagated to adjacent timepoints (Carp, 2012, NeuroImage). One effective way to handle this is to interpolate over bad volumes (cubic spline is common), then apply the temporal filter, and later scrub out the bad (now interpolated) timepoints. This is how nilearn and XCP-D approach the problem. BrainGnomes also allows for spline interpolation as a precursor to temporal filtering.
Another scrubbing interaction occurs with confound regression. If we plan to regress out confound signals from the fMRI, we want to maximize the denoising for the volumes we ultimately wish to analyze, not the volumes we plan to scrub out. Consequently, we could either a) run confound regression after scrubbing or b) optimize the fit of the regression model only for good timepoints. Aligned with XCP-D pipeline, we adopt the latter strategy, fitting the confound model on good timepoints. This allows for (potentially interpolated) high-motion timepoints to be retained in the final fMRI timeseries. Typically, we would remove/scrub these, but if your analysis requires intact timeseries (e.g., for a frequency-based analysis), perhaps you’d like to keep all timepoints.
Defining scrubbed volumes
If you enable scrubbing, you will be asked to define what constitutes
a ‘scrubbed’ volume. This should be a single logical expression in R
syntax that can be evaluated against the confounds TSV file produced by
fmriprep. For example, if you wish to scrub any volume with more than
0.5mm framewise displacement, the expression would be:
framewise_displacement > 0.5
. You can have logical
operators in the expression, too, allowing something like
framewise_displacement > 0.5 | dvars > 5
.
Scrubbing expression(s) > framewise_displacement > 0.9
In addition, your scrubbing expression can specify a range of volumes to scrub using a format like: ‘-1:1; dvars > 1.5’. The part before the semicolon (-1:1) defines a temporal window around the bad timepoint. For example, ‘-1:0’ scrubs the bad timepoint and the volume before it. If omitted, only the bad timepoint (0) is scrubbed. The part after the semicolon (‘dvars > 1.5’) defines the thresholding condition.
Adding scrubbing spike regressors to postprocessed confounds
Next, you will be asked whether you want to add any volumes identified as ‘bad’ by the scrubbing expression to your postprocessed confounds.tsv file. This is only relevant if you enabled to the (preceding) confound calculate step. In this case, BrainGnomes will create spike regressors (1 at the bad timepoint, 0 elsewhere) for each volume flagged, adding them to the final confounds.tsv file. Note that if you ‘apply’ scrubbing – that is, remove the scrubbed timepoints – the spike regressors will not be added to the postprocessed confounds since they would just be all 0 anyhow.
Adding these spike regressors to the confounds.tsv file is useful if you plan to include them as nuisance regressors in a larger (typically GLM) analysis, rather than deleting those volumes entirely during scrubbing.
Add any spike regressors (bad volumes) to postprocessed confounds.tsv file?
Interpolation of scrubbed volumes
As noted above, temporal filtering can be problematic when there are large intensity changes, potentially causing ringing artifacts that spread motion-related contamination into adjacent volumes. One effective way to mitigate this risk is to interpolate the bad timepoints prior to temporal filtering. BrainGnomes achieves this using cubic natural spline interpolation. We recommend interpolation if you are substantially filtering your data (e.g., .009 – .08 Hz) and have evidence of strong intensity artifacts (e.g., on fMRIPrep’s carpet plots).
Interpolate scrubbed values?
Removing scrubbed volumes
The term ‘scrubbing’ traditionally referred to removing bad (high-motion, high-fluctuation) timepoints from the fMRI timeseries prior to analysis, particularly of resting-state functional connectivity. This remains one of its most common uses.
If you would like to remove bad timepoints (based on the scrubbing expression), say ‘yes’ to this question:
Remove scrubbed timepoints?
This will delete the bad volumes as well as corresponding rows in your postprocessed confounds file (if relevant).
Intensity Normalisation
setup_intensity_normalization()
rescales each run so
that the global median intensity matches a user‑specified value (default
10,000). This helps make signal units comparable across subjects.
Confound Calculation and Regression
Two steps handle confounds. setup_confound_calculate()
creates a TSV file of nuisance regressors (motion parameters, CompCor
components, DVARS, global signal, etc.) that may be filtered to match
the BOLD data. Columns to include are selected with patterns that can
contain regular expressions or numeric ranges inside angle brackets. For
example a_comp_cor_<1-6>
expands to
a_comp_cor_1
through a_comp_cor_6
and a
pattern like ^trans_
will match all six motion parameters.
This expansion is implemented in
expand_confound_columns()
.【F:R/postprocess_functions.R†L996-L1036】
Standard motion-confound bundles can also be selected using shortcuts
such as "6p"
, "12p"
, "24p"
, or
"36p"
to request common sets of motion parameters and their
derivatives/squared terms.
These shorthand specifications expand to the following:
6p = { rot_x, rot_y, rot_z, trans_x, trans_y, trans_z }
12p = { rot_x, rot_x_derivative1, rot_y, rot_y_derivative1,
rot_z, rot_z_derivative1, trans_x, trans_x_derivative1,
trans_y, trans_y_derivative1, trans_z, trans_z_derivative1 }
24p = { rot_x, rot_x_derivative1, rot_x_derivative1_power2, rot_x_power2,
rot_y, rot_y_derivative1, rot_y_derivative1_power2, rot_y_power2,
rot_z, rot_z_derivative1, rot_z_derivative1_power2, rot_z_power2,
trans_x, trans_x_derivative1, trans_x_derivative1_power2, trans_x_power2,
trans_y, trans_y_derivative1, trans_y_derivative1_power2, trans_y_power2,
trans_z, trans_z_derivative1, trans_z_derivative1_power2, trans_z_power2 }
36p = { csf, csf_derivative1, csf_derivative1_power2, csf_power2,
global_signal, global_signal_derivative1, global_signal_derivative1_power2,
global_signal_power2,
rot_x, rot_x_derivative1, rot_x_derivative1_power2, rot_x_power2,
rot_y, rot_y_derivative1, rot_y_derivative1_power2, rot_y_power2,
rot_z, rot_z_derivative1, rot_z_derivative1_power2, rot_z_power2,
trans_x, trans_x_derivative1, trans_x_derivative1_power2, trans_x_power2,
trans_y, trans_y_derivative1, trans_y_derivative1_power2, trans_y_power2,
trans_z, trans_z_derivative1, trans_z_derivative1_power2, trans_z_power2,
white_matter, white_matter_derivative1, white_matter_derivative1_power2,
white_matter_power2 }
Filtered confounds (listed in columns
) undergo the same
temporal filtering as the BOLD data, while unfiltered confounds (listed
in noproc_columns
) are appended without processing. Typical
unfiltered regressors include binary spike regressors from
scrubbing.
Confound selection accepts regular expressions and numeric ranges.
For instance ^trans_
matches all six translational motion
parameters and a_comp_cor_<1-6>
expands to the first
six CompCor components. Both filtered and unfiltered sets may use these
patterns, giving fine-grained control over nuisance regressors.
setup_confound_regression()
optionally performs
voxelwise regression of these confounds. If scrubbing is enabled, the
censor file written during setup_scrubbing()
is passed to
the regression step so that model fits are computed using only volumes
marked as “good.”【F:R/postprocess_functions.R†L748-L764】 The residuals
are written to new NIfTI files with a chosen prefix. Because filtered
confounds match the exact temporal treatment of the BOLD data, this
regression step is safe from mismatch artifacts, similar to the strategy
described in the xcp-d documentation.
Ordering Steps
Finally, setup_postproc_steps()
determines the order in
which enabled steps run. By default the order is masking, smoothing,
AROMA, optional scrubbing/interpolation, temporal filtering, confound
regression, and intensity normalisation. You may override this order by
answering “yes” to the prompt shown in the code
below.【F:R/setup_postprocess.R†L321-L358】
Running Postprocessing
After configuration, running run_project()
will execute
the selected postprocessing steps for each subject.
run_project(scfg, steps = "postprocess")
The processing log for each subject is written to the project’s
logs
folder, and the final postprocessed NIfTIs receive the
BIDS description specified earlier.
Naming and use of intermediate files
During the setup of postprocessing, you will be asked whether to keep intermediate files. Generally speaking, you should say ‘no’ since these files only have a subset of postprocessing steps applied to them (e.g., smoothed, but not temporally filtered). That said, if you’d like to inspect intermediate stages of processing, you acn
Summary
Postprocessing in BrainGnomes is fully configurable. Using
setup_project()
or edit_project()
you can
enable or disable each operation, control parameters such as smoothing
kernel and filtering cutoffs, and decide the order in which steps occur.
Running the project then applies these settings consistently across all
subjects.
For more details on individual functions see the package documentation and the help pages referenced above.