Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

AFNI Preprocessing

Run this notebook

Author: Monika Doerig

Date: 1 May 2025

License:

MIT License

Note: If this notebook uses neuroimaging tools from Neurocontainers, those tools retain their original licenses. Please see Neurodesk citation guidelines for details.

Citation and Resources:

Tools included in this workflow

AFNI

  • Cox RW (1996). AFNI: software for analysis and visualization of functional magnetic resonance neuroimages. Comput Biomed Res 29(3):162-173. doi:10.1006/cbmr.1996.0014

  • RW Cox, JS Hyde (1997). Software tools for analysis and visualization of FMRI Data. NMR in Biomedicine, 10: 171-178. https://pubmed.ncbi.nlm.nih.gov/9430344/ 15.1.2. Methods: General func

Educational Resources

Andy’s Brain Book:

Dataset

Flanker Dataset from OpenNeuro:

  • Kelly AMC and Uddin LQ and Biswal BB and Castellanos FX and Milham MP (2018). Flanker task (event-related). OpenNeuro Dataset ds000102. [Dataset] doi: null

  • Kelly AM, Uddin LQ, Biswal BB, Castellanos FX, Milham MP. Competition between functional brain networks mediates behavioral variability. Neuroimage. 2008 Jan 1;39(1):527-37. doi: Kelly et al. (2008). Epub 2007 Aug 23. PMID: 17919929.

  • Mennes, M., Kelly, C., Zuo, X.N., Di Martino, A., Biswal, B.B., Castellanos, F.X., Milham, M.P. (2010). Inter-individual differences in resting-state functional connectivity predict task-induced BOLD activity. Neuroimage, 50(4):1690-701. doi: Mennes et al. (2010). Epub 2010 Jan 15. Erratum in: Neuroimage. 2011 Mar 1;55(1):434

  • Mennes, M., Zuo, X.N., Kelly, C., Di Martino, A., Zang, Y.F., Biswal, B., Castellanos, F.X., Milham, M.P. (2011). Linking inter-individual differences in neural activation and behavior to intrinsic brain dynamics. Neuroimage, 54(4):2950-9. doi: Mennes et al. (2011)

Introduction

Single Subject fMRI Preprocessing with AFNI

This notebook demonstrates a single-subject preprocessing pipeline using AFNI (Analysis of Functional NeuroImages). Preprocessing prepares the raw fMRI data for statistical analysis by correcting for acquisition artefacts such as head motion, slice timing differences, and anatomical variability across subjects. The workflow follows the approach outlined in Andy’s Brain Book’s AFNI Tutorial: Commands and Preprocessing. We will work with data from subject sub-08 of the DS000102 dataset (a Flanker task experiment), covering both basic AFNI commands and an automated preprocessing pipeline using afni_proc.py.

By the end of this notebook, you will be able to:

  • Use basic AFNI commands

  • Set up and run an automated preprocessing pipeline with afni_proc.py

  • Understand each preprocessing step and how to inspect neuroimaging data and preprocessing results

Load AFNI

['afni/25.2.03']

Import Python Modules

Data preparation

The data is structured in BIDS format:

Fetching long content....

Inspecting the anatomical and functional images

To use the AFNI graphical user interface, you would type:

afni sub-08_T1w.nii.gz

We will use ipyniivue to look at the data:

Many of the quality checks for the functional images are similar to those for the anatomical images. Look out for excessively bright or dark spots in the grey or white matter, as well as any image distortions, such as abnormal stretching or warping. A common area where slight distortion may occur is in the orbitofrontal region, just above the eyeballs.

Additionally, it’s important to check for excessive motion during the scan.

Loading...
Loading...

AFNI Commands and Preprocessing

We will be working with the data from subject sub-08. First, we’ll introduce basic AFNI commands. Then, we’ll create a processing script using afni_proc.py, which automates the entire preprocessing workflow. AFNI also provides graphical user interfaces (GUIs), such as uber_subject.py and uber_ttest.py (for group analysis), which help build processing scripts by combining many commands in the correct order. However, since GUIs cannot be used within a Jupyter Notebook environment, we will focus on writing the scripts directly.

After setting up the automated script, we’ll walk through each preprocessing step individually and discuss how to check data quality before and after each step.

  • 1: AFNI Commands

  • 2: Preprocessing with afni_prc.py

    • Registration and Normalization

    • Alignment and Motion Correction

    • Smoothing

    • Masking and Scaling

    • Checking Preprocessing

1. AFNI commands

AFNI commands typicallly require at least one argument, or input, and they also usually require you to specify what to call the output of the command.

Let’s take skull-stripping, for example - a common preprocessing step that removes the skull from the brain. The AFNI command to do this step is called 3dSkullStrip. Use the -h flag to print the help page.


Usage: A program to extract the brain from surrounding.
  tissue from MRI T1-weighted images. 
  The simplest command would be:
        3dSkullStrip <-input DSET>

  Also consider the script @SSwarper, which combines the use of
  3dSkullStrip and nonlinear warping to an MNI template to produce
  a skull-stripped dataset in MNI space, plus the nonlinear warp
  that can used to transform other datasets from the same subject
  (e.g., EPI) to MNI space. (This script only applies to human brain
  images.)

  The fully automated process consists of three steps:
  1- Preprocessing of volume to remove gross spatial image 
  non-uniformity artifacts and reposition the brain in
  a reasonable manner for convenience.
    ** Note that in many cases, using 3dUnifize before **
    ** using 3dSkullStrip will give better results.    **
  2- Expand a spherical surface iteratively until it envelopes
  the brain. This is a modified version of the BET algorithm:
     Fast robust automated brain extraction, 
      by Stephen M. Smith, HBM 2002 v 17:3 pp 143-155
    Modifications include the use of:
     . outer brain surface
     . expansion driven by data inside and outside the surface
     . avoidance of eyes and ventricles
     . a set of operations to avoid the clipping of certain brain
       areas and reduce leakage into the skull in heavily shaded
       data
     . two additional processing stages to ensure convergence and
       reduction of clipped areas.
     . use of 3d edge detection, see Deriche and Monga references
       in 3dedge3 -help.
  3- The creation of various masks and surfaces modeling brain
     and portions of the skull

  Common examples of usage:
  -------------------------
  o 3dSkullStrip -input VOL -prefix VOL_PREFIX
     Vanilla mode, should work for most datasets.
  o 3dSkullStrip -input VOL -prefix VOL_PREFIX -push_to_edge
     Adds an aggressive push to brain edges. Use this option
     when the chunks of gray matter are not included. This option
     might cause the mask to leak into non-brain areas.
  o 3dSkullStrip -input VOL -surface_coil -prefix VOL_PREFIX -monkey
     Vanilla mode, for use with monkey data.
  o 3dSkullStrip -input VOL -prefix VOL_PREFIX -ld 30
     Use a denser mesh, in the cases where you have lots of 
     csf between gyri. Also helps when some of the brain is clipped
     close to regions of high curvature.

  Tips:
  -----
     I ran the program with the default parameters on 200+ datasets.
     The results were quite good in all but a couple of instances, here
     are some tips on fixing trouble spots:

     Clipping in frontal areas, close to the eye balls:
        + Try -push_to_edge option first.
          Can also try -no_avoid_eyes option.
     Clipping in general:
        + Try -push_to_edge option first.
          Can also use lower -shrink_fac, start with 0.5 then 0.4
     Problems down below:
        + Piece of cerebellum missing, reduce -shrink_fac_bot_lim 
          from default value.
        + Leakage in lower areas, increase -shrink_fac_bot_lim 
          from default value.
     Some lobules are not included:
        + Use a denser mesh. Start with -ld 30. If that still fails,
        try even higher density (like -ld 50) and increase iterations 
        (say to -niter 750). 
        Expect the program to take much longer in that case.
        + Instead of using denser meshes, you could try blurring the data 
        before skull stripping. Something like -blur_fwhm 2 did
        wonders for some of my data with the default options of 3dSkullStrip
        Blurring is a lot faster than increasing mesh density.
        + Use also a smaller -shrink_fac is you have lots of CSF between
        gyri.
     Massive chunks missing:
        + If brain has very large ventricles and lots of CSF between gyri,
        the ventricles will keep attracting the surface inwards. 
        This often happens with older brains. In such 
        cases, use the -visual option to see what is happening.
        For example, the options below did the trick in various
        instances. 
            -blur_fwhm 2 -use_skull  
        or for more stubborn cases increase csf avoidance with this cocktail
            -blur_fwhm 2 -use_skull -avoid_vent -avoid_vent -init_radius 75 
        + Too much neck in the volume might throw off the initialization
          step. You can fix this by clipping tissue below the brain with 
                 @clip_volume -below ZZZ -input INPUT  
          where ZZZ is a Z coordinate somewhere below the brain.

     Large regions outside brain included:
       + Usually because noise level is high. Try @NoisySkullStrip.

  Make sure that brain orientation is correct. This means the image in 
  AFNI's axial slice viewer should be close to the brain's axial plane.
  The same goes for the other planes. Otherwise, the program might do a lousy
  job removing the skull.

  Eye Candy Mode: 
  ---------------
  You can run 3dSkullStrip and have it send successive iterations
 to SUMA and AFNI. This is very helpful in following the
 progression of the algorithm and determining the source
 of trouble, if any.
  Example:
     afni -niml -yesplugouts &
     suma -niml &
     3dSkullStrip -input Anat+orig -o_ply anat_brain -visual

  Help section for the intrepid:
  ------------------------------
  3dSkullStrip  < -input VOL >
             [< -o_TYPE PREFIX >] [< -prefix VOL_PREFIX >] 
             [< -spatnorm >] [< -no_spatnorm >] [< -write_spatnorm >]
             [< -niter N_ITER >] [< -ld LD >] 
             [< -shrink_fac SF >] [< -var_shrink_fac >] 
             [< -no_var_shrink_fac >] [< -shrink_fac_bot_lim SFBL >]
             [< -pushout >] [< -no_pushout >] [< -exp_frac FRAC]
             [< -touchup >] [< -no_touchup >]
             [< -fill_hole R >] [< -NN_smooth NN_SM >]
             [< -smooth_final SM >] [< -avoid_vent >] [< -no_avoid_vent >]
             [< -use_skull >] [< -no_use_skull >] 
             [< -avoid_eyes >] [< -no_avoid_eyes >] 
             [< -use_edge >] [< -no_use_edge >] 
             [< -push_to_edge >] [<-no_push_to_edge>]
             [< -perc_int PERC_INT >] 
             [< -max_inter_iter MII >] [-mask_vol | -orig_vol | -norm_vol]
             [< -debug DBG >] [< -node_debug NODE_DBG >]
             [< -demo_pause >]
             [< -monkey >] [< -marmoset >] [<-rat>]

  NOTE: Please report bugs and strange failures
        to saadz@mail.nih.gov

  Mandatory parameters:
     -input VOL: Input AFNI (or AFNI readable) volume.
                 

  Optional Parameters:
     -monkey: the brain of a monkey.
     -marmoset: the brain of a marmoset. 
                this one was tested on one dataset
                and may not work with non default
                options. Check your results!
     -rat: the brain of a rat.
           By default, no_touchup is used with the rat.
     -surface_coil: Data acquired with a surface coil.
     -o_TYPE PREFIX: prefix of output surface.
        where TYPE specifies the format of the surface
        and PREFIX is, well, the prefix.
        TYPE is one of: fs, 1d (or vec), sf, ply.
        More on that below.
     -skulls: Output surface models of the skull.
     -4Tom:   The output surfaces are named based
             on PREFIX following -o_TYPE option below.
     -prefix VOL_PREFIX: prefix of output volume.
        If not specified, the prefix is the same
        as the one used with -o_TYPE.
        The output volume is skull stripped version
        of the input volume. In the earlier version
        of the program, a mask volume was written out.
        You can still get that mask volume instead of the
        skull-stripped volume with the option -mask_vol . 
        NOTE: In the default setting, the output volume does not 
              have values identical to those in the input. 
              In particular, the range might be larger 
              and some low-intensity values are set to 0.
              If you insist on having the same range of values as in
              the input, then either use option -orig_vol, or run:
         3dcalc -nscale -a VOL+VIEW -b VOL_PREFIX+VIEW \
                -expr 'a*step(b)' -prefix VOL_SAME_RANGE
              With the command above, you can preserve the range
              of values of the input but some low-intensity voxels would
              still be masked. If you want to preserve them, then use
              -mask_vol in the 3dSkullStrip command that would produce 
              VOL_MASK_PREFIX+VIEW. Then run 3dcalc masking with voxels
              inside the brain surface envelope:
         3dcalc -nscale -a VOL+VIEW -b VOL_MASK_PREFIX+VIEW \
                -expr 'a*step(b-3.01)' -prefix VOL_SAME_RANGE_KEEP_LOW
     -norm_vol: Output a masked and somewhat intensity normalized and 
                thresholded version of the input. This is the default,
                and you can use -orig_vol to override it.
     -orig_vol: Output a masked version of the input AND do not modify
                the values inside the brain as -norm_vol would.
     -mask_vol: Output a mask volume instead of a skull-stripped
                volume.
                The mask volume contains:
                 0: Voxel outside surface
                 1: Voxel just outside the surface. This means the voxel
                    center is outside the surface but inside the 
                    bounding box of a triangle in the mesh. 
                 2: Voxel intersects the surface (a triangle), but center
                    lies outside.
                 3: Voxel contains a surface node.
                 4: Voxel intersects the surface (a triangle), center lies
                    inside surface. 
                 5: Voxel just inside the surface. This means the voxel
                    center is inside the surface and inside the 
                    bounding box of a triangle in the mesh. 
                 6: Voxel inside the surface. 
     -spat_norm: (Default) Perform spatial normalization first.
                 This is a necessary step unless the volume has
                 been 'spatnormed' already.
     -no_spatnorm: Do not perform spatial normalization.
                   Use this option only when the volume 
                   has been run through the 'spatnorm' process
     -spatnorm_dxyz DXYZ: Use DXY for the spatial resolution of the
                          spatially normalized volume. The default 
                          is the lowest of all three dimensions.
                          For human brains, use DXYZ of 1.0, for
                          primate brain, use the default setting.
     -write_spatnorm: Write the 'spatnormed' volume to disk.
     -niter N_ITER: Number of iterations. Default is 250
        For denser meshes, you need more iterations
        N_ITER of 750 works for LD of 50.
     -ld LD: Parameter to control the density of the surface.
             Default is 20 if -no_use_edge is used,
             30 with -use_edge. See CreateIcosahedron -help
             for details on this option.
     -shrink_fac SF: Parameter controlling the brain vs non-brain
             intensity threshold (tb). Default is 0.6.
              tb = (Imax - t2) SF + t2 
             where t2 is the 2 percentile value and Imax is the local
             maximum, limited to the median intensity value.
             For more information on tb, t2, etc. read the BET paper
             mentioned above. Note that in 3dSkullStrip, SF can vary across 
             iterations and might be automatically clipped in certain areas.
             SF can vary between 0 and 1.
             0: Intensities < median inensity are considered non-brain
             1: Intensities < t2 are considered non-brain
     -var_shrink_fac: Vary the shrink factor with the number of
             iterations. This reduces the likelihood of a surface
             getting stuck on large pools of CSF before reaching
             the outer surface of the brain. (Default)
     -no_var_shrink_fac: Do not use var_shrink_fac.
     -shrink_fac_bot_lim SFBL: Do not allow the varying SF to go
             below SFBL . Default 0.65, 0.4 when edge detection is used. 
             This option helps reduce potential for leakage below 
             the cerebellum.
             In certain cases where you have severe non-uniformity resulting
             in low signal towards the bottom of the brain, you will need to
             reduce this parameter.
     -pushout: Consider values above each node in addition to values
               below the node when deciding on expansion. (Default)
     -no_pushout: Do not use -pushout.
     -exp_frac FRAC: Speed of expansion (see BET paper). Default is 0.1.
     -touchup: Perform touchup operations at end to include
               areas not covered by surface expansion. 
               Use -touchup -touchup for aggressive makeup.
               (Default is -touchup)
     -no_touchup: Do not use -touchup
     -fill_hole R: Fill small holes that can result from small surface
                   intersections caused by the touchup operation.
                   R is the maximum number of pixels on the side of a hole
                   that can be filled. Big holes are not filled.
                   If you use -touchup, the default R is 10. Otherwise 
                   the default is 0.
                   This is a less than elegant solution to the small
                   intersections which are usually eliminated
                   automatically. 
     -NN_smooth NN_SM: Perform Nearest Neighbor coordinate interpolation
                       every few iterations. Default is 72
     -smooth_final SM: Perform final surface smoothing after all iterations.
                       Default is 20 smoothing iterations.
                       Smoothing is done using Taubin's method, 
                       see SurfSmooth -help for detail.
     -avoid_vent: avoid ventricles. Default.
                  Use this option twice to make the avoidance more
                  aggressive. That is at times needed with old brains.
     -no_avoid_vent: Do not use -avoid_vent.
     -init_radius RAD: Use RAD for the initial sphere radius.
                       For the automatic setting, there is an
                       upper limit of 100mm for humans.
                       For older brains with lots of CSF, you
                       might benefit from forcing the radius 
                       to something like 75mm
     -avoid_eyes: avoid eyes. Default
     -no_avoid_eyes: Do not use -avoid_eyes.
     -use_edge: Use edge detection to reduce leakage into meninges and eyes.
                Default.
     -no_use_edge: Do no use edges.
     -push_to_edge: Perform aggressive push to edge at the end.
                    This option might cause leakage.
     -no_push_to_edge: (Default).
     -use_skull: Use outer skull to limit expansion of surface into
                 the skull due to very strong shading artifacts.
                 This option is buggy at the moment, use it only 
                 if you have leakage into skull.
     -no_use_skull: Do not use -use_skull (Default).
     -send_no_skull: Do not send the skull surface to SUMA if you are
                     using  -talk_suma
     -perc_int PERC_INT: Percentage of segments allowed to intersect
                         surface. Ideally this should be 0 (Default). 
                         However, few surfaces might have small stubborn
                         intersections that produce a few holes.
                         PERC_INT should be a small number, typically
                         between 0 and 0.1. A -1 means do not do
                         any testing for intersection.
     -max_inter_iter N_II: Number of iteration to remove intersection
                           problems. With each iteration, the program
                           automatically increases the amount of smoothing
                           to get rid of intersections. Default is 4
     -blur_fwhm FWHM: Blur dset after spatial normalization.
                      Recommended when you have lots of CSF in brain
                      and when you have protruding gyri (finger like)
                      Recommended value is 2..4. 
     -interactive: Make the program stop at various stages in the 
                   segmentation process for a prompt from the user
                   to continue or skip that stage of processing.
                   This option is best used in conjunction with options
                   -talk_suma and -feed_afni
     -demo_pause: Pause at various step in the process to facilitate
                  interactive demo while 3dSkullStrip is communicating
                  with AFNI and SUMA. See 'Eye Candy' mode below and
                  -talk_suma option. 
     -fac FAC: Multiply input dataset by FAC if range of values is too
               small.

     -visual: Equivalent to using -talk_suma -feed_afni -send_kth 5

     -debug DBG: debug levels of 0 (default), 1, 2, 3.
        This is no Rick Reynolds debug, which is oft nicer
        than the results, but it will do.
     -node_debug NODE_DBG: Output lots of parameters for node
                         NODE_DBG for each iteration.
     The next 3 options are for specifying surface coordinates
     to keep the program from having to recompute them.
     The options are only useful for saving time during debugging.
     -brain_contour_xyz_file BRAIN_CONTOUR_XYZ.1D
     -brain_hull_xyz_file BRAIN_HULL_XYZ.1D
     -skull_outer_xyz_file SKULL_OUTER_XYZ.1D
     -help: The help you need



Compile Date:
   Jul  4 2025

       Ziad S. Saad SSCC/NIMH/NIH saadz@mail.nih.gov     

One of AFNI’s major advantages is the quality of its documentation and help resources. Each command’s usage is thoroughly described, and the purpose behind various options is clearly explained. Example commands are provided to illustrate how to handle different situations — for instance, if too much skull remains after skull-stripping, you might be advised to use a flag like -push_to_edge.

The simplest way to run 3dSkullStrip is by using the -input option to specify the anatomical dataset for processing. The -prefix option is also used to output a NIfTI image for visualization with ipyniivue.

anat_ss.nii.gz already exists, skipping skull stripping
Loading...

2. Preprocessing

To automate preprocessing in AFNI, the powerful afni_proc.py tool is used. This command generates a fully customizable tcsh script that includes all necessary preprocessing steps, from slice timing correction to scaling.

The basic idea is:

  • Specify which processing blocks to apply.

  • afni_proc.py writes a script to carry them out in the correct order.

  • The generated script can be reviewed and run, or modified if needed.

Several options are added beyond the defaults to improve alignment quality (-align_unifize_epi, -cost lpc+ZZ, -check_flip), add QC outputs (-radial_correlate_blocks, -volreg_compute_tsnr), and ensure a consistent final voxel size (-volreg_warp_dxyz 3.0).

The processing blocks include both automatic steps (such as setup and initial time concatenation) and default blocks that you can customize, skip, or reorder. For example:

  • Automatic blocks (the tcsh script will always perform these):

-setup: Set up subject information, create output directory (subj_id, script, out_dir)

-tcat: Remove unwanted initial TRs.

  • Default blocks (the user may skip these, or alter their order):

-tshift: Slice timing correction.

-volreg: Volume registration (motion correction).

-blur: Spatial smoothing.

-mask: Create a brain mask from EPI data.

-scale: Normalize voxel-wise signal intensities.

-regress: (optional) Regression analysis for task-based designs.

  • Optional blocks (the default is to not apply these blocks):

-align: EPI-to-anatomical alignment.

-tlrc: Warp anatomical to MNI standard space.

A full list of options and help can be found in the AFNI documentation.

The following command specifies key preprocessing steps for sub-08, including slice timing correction, EPI-to-anatomical alignment, normalization to MNI space, motion correction, smoothing, and scaling. Several options are added to improve alignment quality (-align_unifize_epi, -cost lpc+ZZ, -check_flip), add QC outputs (-radial_correlate_blocks, -volreg_compute_tsnr), and ensure a consistent final voxel size (-volreg_warp_dxyz 3.0).

Preprocessing Workflow:

➡️ setup ➡️ tcat ➡️ tshift ➡️ align ➡️ tlrc ➡️ volreg ➡️ blur ➡️ mask ➡️ scale ➡️ 🧠

✅ outputs: preprocessed EPI, brain mask, tSNR map, motion parameters, and QC reports.

-- applying input view as +orig
-- template = 'MNI152_2009_template_SSW.nii.gz', exists = 1
-- will use min outlier volume as motion base
-- including default: -find_var_line_blocks tcat
-- tcat: reps is now 146
++ updating polort to 2, from run len 292.0 s
-- volreg: using base dset vr_base_min_outlier+orig
++ volreg: applying volreg/epi2anat/tlrc xforms to isotropic 3 mm tlrc voxels
-- applying anat warps to 1 dataset(s): sub-08_T1w
++ mask: using epi_anat mask in place of EPI one
-- masking: group anat = 'MNI152_2009_template_SSW.nii.gz', exists = 1
-- have 1 ROI dict entries ...
-- no regress block, skipping gen_ss_review_scripts.py
-- using default: will not apply EPI Automask
   (see 'MASKING NOTE' from the -help for details)

--> script is file: proc.sub_08

    to execute via tcsh:
         tcsh -xef proc.sub_08 |& tee output.proc.sub_08

    to execute via bash:
         tcsh -xef proc.sub_08 2>&1 | tee output.proc.sub_08

Run the Preprocessing Script and Inspect the Output

Now that we have generated our preprocessing script proc.sub_08, we can execute it via tcsh and save a full record of the output in a file called output.proc.sub_08.

echo auto-generated by afni_proc.py, Wed Jul  8 07:47:29 2026
auto-generated by afni_proc.py, Wed Jul  8 07:47:29 2026
echo (version 7.93, June 17, 2025)
(version 7.93, June 17, 2025)
echo execution started: `date`
date
execution started: Wed Jul  8 07:47:30 UTC 2026
afni -ver
Precompiled binary linux_ubuntu_24_64: Jul  4 2025 (Version AFNI_25.2.03 'Gordian I')
afni_history -check_date 24 Apr 2025
-- is current: afni_history as new as: 24 Apr 2025
               most recent entry is:   03 Jul 2025
if ( 0 ) then
if ( 0 > 0 ) then
set subj = sub_08
endif
set output_dir = ./afni_processing/sub_08.results
if ( -d ./afni_processing/sub_08.results ) then
echo output dir sub_08.results already exists
output dir sub_08.results already exists
exit

🔔 While running, pay attention to the following warnings and notes:

3dTshift Warning:

+ WARNING: dataset is already aligned in time!
+ WARNING: ==>> output dataset is just a copy of input dataset

✅ No action needed — this simply means no time-shifting was necessary. You could re-analyze this data by omitting both the 3dTcat and 3dTshift preprocessing steps, and it would get the same result.

Visual alignment:

++ ###########################################################
++ #   PLEASE check results VISUALLY for alignment quality   #
++ ###########################################################


*+ WARNING: -cmass was turned off, but might have been needed :(
 +           Please check your results - PLEASE PLEASE PLEASE

⚠️ We will inspect registration quality to confirm acceptable alignment in the results section.

While the preprocessing is running, here’s a brief overview of the key steps. For a more detailed explanation of each preprocessing stage, have another look at Andy’s Brain Book’s excellent tutorial.

Slice Time Correction

Even though slice timing correction is included as a block (tshift), in this dataset the slices were already aligned in time, as noted in the warning above. AFNI detected this and simply created a copy of the input data without modification.

The step is kept in the pipeline for completeness and generalizability to other datasets where slice timing correction would be needed.

Note: Although slice-timing correction is common, there are some general objections: it involves interpolating the data (which is best avoided when possible), it may not significantly improve results when the TR is short (e.g., around 1 second), and timing differences can sometimes be modeled later using a temporal derivative during model fitting.

# =================== tshift ===================  
# time shift data so all slice timing is the same 
foreach run ( $runs )
    3dTshift -tzero 0 -quintic -prefix pb01.$subj.r$run.tshift \
             pb00.$subj.r$run.tcat+orig
end

Registration and Normalization

Registration aligns the functional and anatomical images. It usually begins by assuming the images are roughly in the same space; if not, their outlines are aligned. To fine-tune the alignment, the algorithm uses differences in contrast. AFNI’s improved cost function lpc+ZZ builds on the standard Local Pearson Correlation (LPC) method, which gives greater weight to brighter areas in the functional data, with additional regularization for more robust alignment — particularly useful when initial EPI-to-anatomical overlap is low. Once a good alignment is found, the same transformations can later be applied to warp the functional images to a standard template.

In this example, AFNI’s align_epi_anat.py command is used to perform the registration. Although this tool can also handle slice-timing correction and functional volume alignment, it will be used here only for registration.

The key options used are:

  • 3dLocalUnifize: Applies local intensity uniformization to the EPI base volume before alignment, improving registration quality by reducing signal inhomogeneity.

  • -anat2epi: Aligns the anatomical image to the functional image (not the other way around), minimizing changes to the functional data.

  • -suffix _al_junk: Adds a suffix to intermediate files, useful for later steps.

  • -epi, -epi_base, -epi_strip: Chooses the functional volume with least variability as the reference and strips non-brain tissue using 3dAutomask.

  • -cost lpc+ZZ: Improved cost function for more robust EPI-to-anatomical alignment.

  • -giant_move: Helps find an initial rough alignment if the images are very misaligned.

  • -check_flip: Checks whether the EPI has been accidentally left-right flipped relative to the anatomical.

  • -volreg off, -tshift off: Alignment and slice-timing correction are handled separately, not within this command.

Registration with align_epi_anat.py

# ================================= align ==================================
# run (localized) uniformity correction on EPI base
3dLocalUnifize -input vr_base_min_outlier+orig -prefix \
    vr_base_min_outlier_unif
align_epi_anat.py -anat2epi -anat sub-08_T1w+orig      \
     -save_skullstrip -suffix _al_junk                 \
     -epi vr_base_min_outlier_unif+orig -epi_base 0    \
     -epi_strip 3dAutomask                             \
     -cost lpc+ZZ -giant_move -check_flip              \
     -volreg off -tshift off

Normalization with @auto_tlrc

After registration, the anatomical image is normalized to a standard template (MNI152_2009_template_SSW.nii.gz) using AFNI’s @auto_tlrc command. Since the anatomical image has already been skull-stripped, the -no_ss option is used. Because the centers of the anatomical and template images are far apart, the -init_xform AUTO_CENTER option is included, , which roughly aligns the centers of the two images before the fine registration step.
The transformation needed to align the anatomical image to the template is stored as an affine matrix in the header of the anatomical image. This matrix is then extracted with the cat_matvec command and saved as warp.anat.Xat.1D, so it can later be applied to the functional images as well.

#warp anatomy to standard space
@auto_tlrc -base MNI152_2009_template_SSW.nii.gz -input sub-08_T1w_ns+orig -no_ss \
    -init_xform AUTO_CENTER
#store forward transformation matrix in a text file
cat_matvec sub-08_T1w_ns+tlrc::WARP_DATA -I > warp.anat.Xat.1D

Alignment and Motion Correction

Motion correction is done with AFNI’s 3dvolreg command. The functional volumes are aligned to a reference image, which is selected as the volume with the fewest outliers (identified using 3dToutcount earlier). Motion parameters are saved into a text file (-1Dfile) and the corresponding affine transformation matrices into another file (-1Dmatrix_save).

# =================== volreg ===================
#register and warp
foreach run ( $runs )
    # register each volume to the base image
    3dvolreg -verbose -zpad 1 -base vr_base_min_outlier+orig   \
           -1Dfile dfile.r$run.1D -prefix rm.epi.volreg.r$run  \
           -cubic                                              \
           -1Dmatrix_save mat.r$run.vr.aff12.1D                \
            pb01.$subj.r$run.tshift+orig

These motion correction matrices are then concatenated with the matrices from anatomical registration and normalization into a single combined transform using cat_matvec.

#catenate volreg/epi2anat/tlrc xforms
    cat_matvec -ONELINE                                         \
               sub-08_T1w_ns+tlrc::WARP_DATA -I                 \
               sub-08_T1w_al_junk_mat.aff12.1D -I               \
               mat.r$run.vr.aff12.1D > mat.r$run.warp.aff12.1D

Using 3dAllineate and the -1Dmatrix_apply option, motion correction, EPI-to-anatomical alignment, and normalization to MNI space are all applied to the functional images in a single step, resampling to 3mm isotropic voxels (-mast_dxyz 3). This single-step approach avoids accumulating interpolation errors that would occur if each transformation were applied separately.

#warp the all-1 dataset for extents masking
3dAllineate -base sub-08_T1w_ns+tlrc                            \
            -input pb01.$subj.r$run.tshift+orig                 \
            -1Dmatrix_apply mat.r$run.warp.aff12.1D             \
            -mast_dxyz 3                                        \
            -prefix rm.epi.nomask.r$run

A tSNR (temporal signal-to-noise ratio) map is computed from run 1 by dividing the mean signal by the standard deviation of the detrended noise. This serves as a QC metric for assessing data quality after motion correction — higher tSNR indicates cleaner data.

# create tSNR dataset from run 1 for QC
3dTstat -mean -prefix rm.signal.vreg.r01 pb02.$subj.r01.volreg+tlrc
3dDetrend -polort 2 -prefix rm.noise.det -overwrite pb02.$subj.r01.volreg+tlrc
3dTstat -stdev -prefix rm.noise.vreg.r01 rm.noise.det+tlrc
3dcalc -a rm.signal.vreg.r01+tlrc                               \
       -b rm.noise.vreg.r01+tlrc                                \
       -c mask_epi_extents+tlrc                                 \
       -expr 'c*a/b' -prefix TSNR.vreg.r01.$subj

Radial correlation maps are then generated as an additional QC step, checking for coil artifacts or systematic alignment problems across the brain volume.

# QC: radial correlation maps
@radial_correlate -nfirst 0 -polort 2 -do_clean yes             \
                  -rdir radcor.pb02.volreg                      \
                  pb02.$subj.r*.volreg+tlrc.HEAD

Smoothing

Smoothing is done with AFNI’s 3dmerge command, which can be found under the “blur” header.

The -1blur_fwhm option applies spatial smoothing with a 5mm kernel, and -doall ensures smoothing is done across all volumes. After smoothing, the images are scaled to a mean intensity of 100, allowing changes to be interpreted as percent signal change. Finally, a brain mask, intersection of the EPI brain mask and the anatomical brain mask (-mask_epi_anat yes), is applied to remove non-brain voxels, preparing the data for statistical analysis.

# ==================== blur ====================
#blur each volume of each run   
foreach run ( $runs )
    3dmerge -1blur_fwhm 5.0 -doall -prefix pb03.$subj.r$run.blur \
            pb02.$subj.r$run.volreg+tlrc
end

Masking and Scaling

Masking

fMRI datasets include not only brain voxels but also irrelevant areas like the skull, neck, and air. A mask is applied to focus analysis on brain voxels only.

The masking pipeline creates three masks:

  • EPI union mask: 3dAutomask creates a brain mask per run, combined into a union mask across runs.

  • Anatomical mask: The anatomical image is resampled to the EPI grid and converted to a binary mask with holes filled.

  • EPI+anat intersection mask: The two masks are intersected to produce a tighter final mask, retaining only voxels present in both.

Overlap and Dice coefficients between masks are computed as a QC check on registration quality.

# ==================== mask ====================
# create 'full_mask' dataset (union mask)
foreach run ( $runs )
    3dAutomask -prefix rm.mask_r$run pb03.$subj.r$run.blur+tlrc
end
# create union of inputs, output type is byte
3dmask_tool -inputs rm.mask_r*+tlrc.HEAD -union -prefix full_mask.$subj
# create subject anatomy mask, mask_anat.$subj+tlrc (resampled from tlrc anat)
3dresample -master full_mask.$subj+tlrc -input sub-08_T1w_ns+tlrc \
           -prefix rm.resam.anat
# convert to binary anat mask; fill gaps and holes
3dmask_tool -dilate_input 5 -5 -fill_holes -input rm.resam.anat+tlrc \
            -prefix mask_anat.$subj
# compute tighter EPI mask by intersecting with anat mask + QC
3dmask_tool -input full_mask.$subj+tlrc mask_anat.$subj+tlrc \
            -inter -prefix mask_epi_anat.$subj
3dABoverlap -no_automask full_mask.$subj+tlrc mask_anat.$subj+tlrc \
            |& tee out.mask_ae_overlap.txt
3ddot -dodice full_mask.$subj+tlrc mask_anat.$subj+tlrc \
      |& tee out.mask_ae_dice.txt
# create group mask from MNI template + QC
3dresample -master mask_epi_anat.$subj+tlrc -prefix ./rm.resam.group \
           -input /usr/local/abin/MNI152_2009_template_SSW.nii.gz'[0]'
3dmask_tool -dilate_input 5 -5 -fill_holes -input rm.resam.group+tlrc \
            -prefix mask_group
3ddot -dodice mask_anat.$subj+tlrc mask_group+tlrc \
      |& tee out.mask_at_dice.txt
Scaling

fMRI signal intensities are arbitrary and vary between subjects and runs. To make meaningful comparisons, AFNI scales each voxel’s time series to a mean of 100, allowing for consistent signal intensity contrasts between conditions and across subjects.

# ==================== scale ====================
# scale each voxel time series to have a mean of 100
# (be sure no negatives creep in)
# (subject to a range of [0,200])  
foreach run ( $runs )
    3dTstat -prefix rm.mean_r$run pb03.$subj.r$run.blur+tlrc
    3dcalc -a pb03.$subj.r$run.blur+tlrc -b rm.mean_r$run+tlrc \
           -c mask_epi_extents+tlrc                            \
           -expr 'c * min(200, a/b*100)*step(a)*step(b)'       \
           -prefix pb04.$subj.r$run.scale
end 

Effect of Scaling: Before scaling, time-series values are arbitrary and vary between subjects. After scaling, each subject’s data is normalized to the same mean, enabling consistent comparisons across runs and subjects. To visualize both the original and scaled data with Matplotlib, the AFNI format needs to be converted to NIfTI format using the 3dAFNItonNIFI command. The following plot compares the signal intensity time series from a voxel at the center of the brain, before and after scaling. The red dashed line indicates the mean signal intensity for each time series.

++ 3dAFNItoNIFTI: AFNI version=AFNI_25.2.03 (Jul  4 2025) [64-bit]
*+ WARNING: varying brick factors, writing NIfTI as float
++ 3dAFNItoNIFTI: AFNI version=AFNI_25.2.03 (Jul  4 2025) [64-bit]
*+ WARNING: varying brick factors, writing NIfTI as float
<Figure size 1000x600 with 2 Axes>

Results: Checking Preprocessing

Let’s have a look at the results directory. The files containing the pb string are the preprocessed functional images at each preprocessing step, and the files with the T1w string are the preprocessed anatomical images. Auxiliary functional images are created to assist with specific preprocessing steps, and auxiliary text files contain information about transformation matrices and movement parameter:

@epi_review.sub_08
MNI152_2009_template_SSW.nii.gz
TSNR.vreg.r01.sub_08+tlrc.BRIK
TSNR.vreg.r01.sub_08+tlrc.HEAD
__tt_lr_flipcosts.1D
__tt_lr_noflipcosts.1D
aea_checkflip_results.txt
anat_final.sub_08+tlrc.BRIK
anat_final.sub_08+tlrc.HEAD
anat_w_skull_warped+tlrc.BRIK
anat_w_skull_warped+tlrc.HEAD
dfile.r01.1D
dfile.r02.1D
dfile_rall.1D
final_epi_vr_base_min_outlier+tlrc.BRIK
final_epi_vr_base_min_outlier+tlrc.HEAD
full_mask.sub_08+tlrc.BRIK
full_mask.sub_08+tlrc.HEAD
mask_anat.sub_08+tlrc.BRIK
mask_anat.sub_08+tlrc.HEAD
mask_epi_anat.sub_08+tlrc.BRIK
mask_epi_anat.sub_08+tlrc.HEAD
mask_epi_extents+tlrc.BRIK
mask_epi_extents+tlrc.HEAD
mask_group+tlrc.BRIK
mask_group+tlrc.HEAD
mat.basewarp.aff12.1D
mat.r01.vr.aff12.1D
mat.r01.warp.aff12.1D
mat.r02.vr.aff12.1D
mat.r02.warp.aff12.1D
motion_sub_08_enorm.1D
out.4095_all.txt
out.4095_warn.txt
out.allcostX.txt
out.mask_ae_dice.txt
out.mask_ae_overlap.txt
out.mask_at_dice.txt
out.min_outlier.txt
out.pre_ss_warn.txt
out.vlines.pb00.tcat.txt
outcount.r01.1D
outcount.r02.1D
outcount_rall.1D
pb00.sub_08.r01.tcat+orig.BRIK
pb00.sub_08.r01.tcat+orig.HEAD
pb00.sub_08.r02.tcat+orig.BRIK
pb00.sub_08.r02.tcat+orig.HEAD
pb01.sub_08.r01.tshift+orig.BRIK
pb01.sub_08.r01.tshift+orig.HEAD
pb01.sub_08.r02.tshift+orig.BRIK
pb01.sub_08.r02.tshift+orig.HEAD
pb02.sub_08.r01.volreg+tlrc.BRIK
pb02.sub_08.r01.volreg+tlrc.HEAD
pb02.sub_08.r02.volreg+tlrc.BRIK
pb02.sub_08.r02.volreg+tlrc.HEAD
pb03.sub_08.r01.blur+tlrc.BRIK
pb03.sub_08.r01.blur+tlrc.HEAD
pb03.sub_08.r01.blur.nii.gz
pb03.sub_08.r02.blur+tlrc.BRIK
pb03.sub_08.r02.blur+tlrc.HEAD
pb04.sub_08.r01.scale+tlrc.BRIK
pb04.sub_08.r01.scale+tlrc.HEAD
pb04.sub_08.r01.scale.nii.gz
pb04.sub_08.r02.scale+tlrc.BRIK
pb04.sub_08.r02.scale+tlrc.HEAD
pre.sub-08_T1w_ns+orig.BRIK
pre.sub-08_T1w_ns+orig.HEAD
pre.sub-08_T1w_ns_WarpDrive.log
radcor.pb00.tcat
radcor.pb02.volreg
stimuli
sub-08_T1w+orig.BRIK
sub-08_T1w+orig.HEAD
sub-08_T1w_al_junk+orig.BRIK
sub-08_T1w_al_junk+orig.HEAD
sub-08_T1w_al_junk_mat.aff12.1D
sub-08_T1w_flip__al_junk_mat.aff12.1D
sub-08_T1w_flip_al_junk+orig.BRIK
sub-08_T1w_flip_al_junk+orig.HEAD
sub-08_T1w_ns+orig.BRIK
sub-08_T1w_ns+orig.HEAD
sub-08_T1w_ns+tlrc.BRIK
sub-08_T1w_ns+tlrc.HEAD
sub-08_T1w_ns.Xaff12.1D
sub-08_T1w_ns.Xat.1D
sub-08_T1w_ns.maskwarp.Xat.1D
sub-08_T1w_ns_shft.1D
sub-08_T1w_unflipped+orig.BRIK
sub-08_T1w_unflipped+orig.HEAD
sub-08_T1w_unflipped_ns+orig.BRIK
sub-08_T1w_unflipped_ns+orig.HEAD
sub-08_T1w_unflipped_ns_al_junk_wtal+orig.BRIK
sub-08_T1w_unflipped_ns_al_junk_wtal+orig.HEAD
vlines.pb00.tcat
volreg_example+tlrc.BRIK
volreg_example+tlrc.HEAD
volumized+tlrc.BRIK
volumized+tlrc.HEAD
vr_base_min_outlier+orig.BRIK
vr_base_min_outlier+orig.HEAD
vr_base_min_outlier_unif+orig.BRIK
vr_base_min_outlier_unif+orig.HEAD
vr_base_min_outlier_unif_ts_ns+orig.BRIK
vr_base_min_outlier_unif_ts_ns+orig.HEAD
vr_base_min_outlier_unif_ts_ns_wt+orig.BRIK
vr_base_min_outlier_unif_ts_ns_wt+orig.HEAD
warp.anat.Xat.1D
! Note:

In AFNI, a +tlrc extension (and the "Talairach View") indicates that the image has been normalized. However, this does not necessarily mean the image is in Talairach space. The Talairach label is retained for legacy reasons, ensuring compatibility with older versions of the software. To verify which space the image has been warped to, you can use the 3dinfo command and check the "Template Space" field. The three possibilities are: "ORIG" (the image has not been warped), "TLRC" (normalized to Talairach space), and "MNI" (normalized to MNI space).

Visualizations

Registration QC

First, we will check EPI-to-anatomical registration quality using @snapshot_volreg, which overlays the edges of the EPI on the anatomical image.

 -- trying to start Xvfb :847
[1] 9042
++ Start GUI to snapshot
@snapshot_volreg output image = ./anat_final.sub_08.pb02.sub_08.r01.volreg.jpg
++ 3dAutobox: AFNI version=AFNI_25.2.03 (Jul  4 2025) [64-bit]
++ Auto bbox: x=5..192  y=7..224  z=-3..180
++ 3dAutobox: output dataset = ./zzerm.X847-754922.acrop.nii
++ 3dAllineate: AFNI version=AFNI_25.2.03 (Jul  4 2025) [64-bit]
++ Authored by: Zhark the Registrator
++ Source dataset: ./afni_processing/sub_08.results/pb02.sub_08.r01.volreg+tlrc.HEAD
++ Base dataset:   (not given)
++ Loading datasets into memory
 +        -cmass x y z shifts =    0.000    0.000    0.000
 +  shift search range is +/- =   60.669   72.225   60.669
++ OpenMP thread count = 15
++ ========== Applying transformation to 1 sub-bricks ==========
++ Output dataset ./zzerm.X847-754922.epiR.nii
++ 3dAllineate: total CPU time = 0.0 sec  Elapsed = 0.6
++ ###########################################################
++ 3dMedianFilter: AFNI version=AFNI_25.2.03 (Jul  4 2025) [64-bit]
++ Output dataset ./zzerm.X847-754922.epiRS.nii
++ 3dedge3: AFNI version=AFNI_25.2.03 (Jul  4 2025) [64-bit]
++ 3dAutomask: AFNI version=AFNI_25.2.03 (Jul  4 2025) [64-bit]
++ Authored by: Emperor Zhark
++ 3dcalc: AFNI version=AFNI_25.2.03 (Jul  4 2025) [64-bit]
++ Authored by: A cast of thousands
++ Output dataset ./zzerm.X847-754922.epiEM.nii
++ Writing one 1308x368 image to filter '/usr/local/abin/cjpeg -quality 95 > zzerm.X847-754922.sag.jpg'
++ Writing one 1128x368 image to filter '/usr/local/abin/cjpeg -quality 95 > zzerm.X847-754922.cor.jpg'
++ Writing one 1128x436 image to filter '/usr/local/abin/cjpeg -quality 95 > zzerm.X847-754922.axi.jpg'

AFNI QUITTs!
<IPython.core.display.Image object>

Preprocessing outputs

To visualize the preprocessing outputs with ipyniivue, we will first extract one volume from the functional image. Then, we’ll check whether the functional and anatomical images are properly aligned to MNI space. Misalignments could indicate issues with the preprocessing steps. Smoothed images will generally appear less detailed than the original images due to the blurring effect; this is expected, but they should not be excessively blurry or too sharp. The scaled functional image will have less anatomical definition, as the signal intensity has been normalized across time, making it more uniform across brain voxels. When reviewing the masks, ensure they appropriately cover the brain regions of interest. The full_mask is the union of EPI masks across runs. The mask_epi_anat is a tighter intersection of the EPI and anatomical masks — this is the mask used in the analysis. The mask_group is derived from the MNI template and is typically the most liberal of the three.

++ 3dTcat: AFNI version=AFNI_25.2.03 (Jul  4 2025) [64-bit]
++ elapsed time = 0.3 s
Loading...

⚠️ Note: ipywidgets that rely on Python callbacks (observe, on_click) require a running kernel and do not function in the static HTML version of this notebook. Widgets using client-side trait linking (jslink) remain interactive without a kernel.

Dependencies in Jupyter/Python

  • Using the package watermark to document system environment and software versions used in this notebook, alongside the Neurodesktop version extracted from the JUPYTER_IMAGE or NEURODESKTOP_VERSION (Neurodesk App) environment variable.

Last updated: 2026-07-08T07:48:00.754900+00:00

Python implementation: CPython
Python version       : 3.13.13
IPython version      : 9.12.0

Compiler    : GCC 14.3.0
OS          : Linux
Release     : 6.8.0-111-generic
Machine     : x86_64
Processor   : x86_64
CPU cores   : 16
Architecture: 64bit

IPython   : 9.12.0
ipyniivue : 2.4.4
ipywidgets: 8.1.8
matplotlib: 3.10.9
nibabel   : 5.3.3
nilearn   : 0.13.1
numpy     : 2.3.5

Neurodesktop version: 2026-06-04
References
  1. Kelly, A. M. C., Uddin, L. Q., Biswal, B. B., Castellanos, F. X., & Milham, M. P. (2008). Competition between functional brain networks mediates behavioral variability. NeuroImage, 39(1), 527–537. 10.1016/j.neuroimage.2007.08.008
  2. Mennes, M., Kelly, C., Zuo, X.-N., Di Martino, A., Biswal, B. B., Castellanos, F. X., & Milham, M. P. (2010). Inter-individual differences in resting-state functional connectivity predict task-induced BOLD activity. NeuroImage, 50(4), 1690–1701. 10.1016/j.neuroimage.2010.01.002
  3. Mennes, M., Zuo, X.-N., Kelly, C., Di Martino, A., Zang, Y.-F., Biswal, B., Castellanos, F. X., & Milham, M. P. (2011). Linking inter-individual differences in neural activation and behavior to intrinsic brain dynamics. NeuroImage, 54(4), 2950–2959. 10.1016/j.neuroimage.2010.10.046