10.3.1. Using volumetric (and other) files in AFNI

NB: most of the shell syntax here applies equally to common shells such as tcsh, bash, zsh, etc. We typically default to tcsh syntax, such as for defining variables:

set var = 10

The user can translate these aspects to bash:

var=10

... or to other shells. The AFNI functionality applies equivalently.

‘On-the-Fly’ datasets: general syntax

Most AFNI programs can take as input datasets that are modified in various ways as they are read in. The methods for specifying such datasets are described below.

They all use special characters such as $, ( ... ), [...], and <...>. Within a given set of brackets (and we discuss what each kind does, below), you can specify selector strings with the following syntaxes:

  • comma-separated lists: 0,10,15, 0,2,4,1,3,5, etc.

  • intervals separated with ..: 0..5, 3..15, etc. NB: both boundaries are included in the interval (i.e., closed interval).

  • step-sizes within an interval (default step=1): 0..5(2) (stepsize=2), 3..15(4) (stepsize=4), etc.

  • and combinations of the above: 0..3,5,7..15(3), etc.

Note that the indices all start a 0, which is called zero-base counting, such as is used in the C and Python programming languages (but not in Matlab or R). There is also a special way to specify “the last volume”, using the $ character (though you might have to include the \ as an escape character in some contexts, see below). Thus, 0..$ runs from beginning to end.

Note

You can also use comma-separated lists of brick labels within sub-brick selectors [...] and ROI labels within the sub-range selectors <...>, discussed more below.

Many shells interpret the brackets in ways antithetical to AFNI, so you will have to escape these characters on the command line. This can be done in different ways:

  • You can put the entire dataset plus selection list inside double quotes: "fred+orig[5..7,9]".

    In this case, other special characters inside the single quotes will be recognized by the shell, so you could include a variable like:

    set nn = 32
    afni elvis+orig"[0..${nn}]"
    

    If you want to use the $ character to indicate the last sub-brick in a dataset (instead of to indicate shell variable expansion), then you would have to escape it with the backslash: 'fred+orig[5..7,9..\$]'.

  • You can put the entire dataset plus selection list inside single quotes: 'fred+orig[5..7,9]'.

    In this case, other special characters inside the single quotes will not be recognized by the shell, so you could not use other shell variables as selectors. Here, if you want to use the $ character to indicate the last sub-brick in a dataset you don’t need to escape it: 'fred+orig[5..7,9..$]'.

  • You can put single or double quotes around just the selectors, if you prefer, such as: fred+orig'[5..7,9]' or fred+orig"[5..7,9]". The respective rules of dealing with other special characters inside the given quotes still apply, as described above.

Note

I (who shall remain as nameless as Odysseus in Polyphemus’s cave) prefer the double quotes when I script, because I am often using variables, as well.

PS: yes, I realize I also just used Odysseus’s name there. But he spoiled the whole thing for himself as well, innit?

Sub-brick selection: [..]

You can add a sub-brick selection list after the end of the dataset name. This allows only a subset of the sub-bricks to be read in (by default, all of a dataset’s sub-bricks are input).

Sub-brick indexes start at 0. That is, [0] refers to the first sub-brick in a dset, and [0..20] refers to 21 sub-bricks, not to 20. You can use the character $ to indicate the last sub-brick in a dataset. A sub-brick selection list looks like one of the following forms:

fred+orig'[5]'            # only sub-brick 5
fred+orig'[5,9,17]'       # 5, 9, and 17
fred+orig'[5..8]'         # 5, 6, 7, and 8 (closed interval)
fred+orig'[5..$]'         # 5 through to the end (closed interval)
fred+orig'[5..13(2)]'     # 5, 7, 9, 11, and 13 (closed interval, stepsize=2)
fred+orig'[0..$(3)]'      # every third volume from full range

The sub-bricks are read in the order specified, which may not be the order in the original dataset. For example, using:

fred+orig'[0..$(2),1..$(2)]'

... will cause the sub-bricks in fred+orig to be input into memory in an interleaved fashion.

Using:

fred+orig'[$..0]'

will reverse the order of the sub-bricks. (It is hard to conceive of an application for such an ability, but AFNI gives it to you anyway.)

Instead of the indices, you can also use a list of sub-brick labels within these selectors to pick out specific volumes. Consider the func_slim+orig statistic dset in the Bootcamp data directory AFNI_data6/afni/. If we wanted to make a new dset of just one particular coefficient and statistic pair, we could do the following:

3dcalc                                               \
    -a func_slim+orig.'[Vrel#0_Coef,Vrel#0_Tstat]'   \
    -expr 'a'                                        \
    -prefix dset_stats_Vrel.nii

The new dset has just 2 volumes, but note that the supplementary statistic information stays with the *Tstat brick. In general, when there are sub-brick labels present (e.g., for statistic dsets), it seems wise to use those within the sub-brick selectors, for clarity. (Of course, using indices is still perfectly fine!)

On a technical note, datasets using sub-brick/sub-range selectors are treated as:

  • 3D+time, if the dataset is 3D+time and more than 1 brick is chosen

  • Otherwise, as bucket datasets (-abuc or -fbuc) (in particular, fico, fitt, etc. datasets are converted to fbuc!)

Sub-range selection: <..>

You may also use the syntax <a..b> after the name of an input dataset to restrict the range of values read in to the numerical values in [a, b], which is inclusive. This may be used with/without the sub-brick selectors. For example:

fred+orig'[5..7]<100..200>'

creates a 3 sub-brick dataset, zeroing out values in the original dset that were less than 100 or greater than 200. If you use the <...> sub-range selection without the [...] sub-brick selection, it is the same as if you had put '[0..$]' in front of the sub-range selection.

This capability was mostly intended to allow easy extraction of a sub-mask from a mask dataset containing multiple values, each value corresponding to a distinct anatomical region. For example, this:

ethel+orig'<4,7>'

... picks out the ROIs with value 4 or 7 from an atlas (and if there wasn’t a value 4 or 7 there, then the output dset will be all zeros).

If you have labels associated with an ROI, then you can select based on those. For example:

ethel+orig'<Left-Inf-Lat-Vent,Left-Thalamus-Proper>'

... picks out the specified ROIs (if they exist in the dset). In general, this provides a much more stable and useful way to select ROIs from atlases, than using number selection!

Calculated datasets

Datasets may also be specified as runtime-generated results from program 3dcalc. This type of dataset specifier is enclosed in quotes, and starts with the string '3dcalc(':

'3dcalc( opt opt ... opt )'

where each opt is an option to program 3dcalc (and opt must not be in quotes). This program is run to generate a dataset in the directory given by environment variable TMPDIR (default = /tmp). This dataset is then read into memory, locked in place, and deleted from disk. For example:

afni -dset '3dcalc( -a r1+orig -b r2+orig -expr a/b )'

... will let you look at the ratio of datasets r1+orig and r2+orig, without pre-computing it into a disk file you’ll have to delete later. And do leave spacing between the opts within the parentheses and the parentheses themselves.

This option can be particularly useful for computing a mask on-the-fly from a master dataset, as in:

3dmaskave                                                   \
    -mask '3dcalc( -a stat+orig[3] -b stat+orig[5] -expr step(a-3.3)*step(b-3.3) )'  \
    fred+orig

NB: using this dataset input method can use lots of memory, depending on the dsets!

Auto-Tcat datasets

Multiple datasets can be combined in the time dimension by putting spaces between them, with the whole ‘dataset_name’ enclosed in quotes on the command line. For example:

3dTstat 'a+orig b+orig c+orig'

will compute the mean of each voxel, across 3 datasets and along the time axis. An alternative way to perform this task would be to create a temporary dataset:

3dTcat -prefix ttt a+orig b+orig c+orig

3dTstat ttt+orig

\rm -f ttt+orig.*

Make a volume of arbitary dimensions

There is special functionality in 3dcalc to generate a dset of arbitrary dimensions that contains random numbers (from the uniform distribution in the range [-1,1]). This is useful for testing purposes, and you can change the random numbers into other things, as well, fairly directly.

If you want to make a \(3\times7\times5\) volume with 46 time points, then run:

3dcalc -a jRandomDataset:3,7,5,46 -expr 'a' -prefix test_00.nii.gz

What properties does it have? Check out 3dinfo on it:

- show header y/n -

Notice that the header has has lots of “default” properties that you would have to set elsewhere, like TR, voxel size, origin, extent, etc. to be meaningful (if you cared about those). These could then be changed or set with 3drefit, say. It will have RAI (= DICOM) dset orientation by default.

If you wanted to make such a dset of constant values (say, approx. \(pi\)), then you could run:

3dcalc -a jRandomDataset:3,7,5,46 -expr '3.14' -prefix test_01.nii.gz

... and even from the 3dinfo on that, you will see the ranges of each brick are [3.14, 3.14].

If you only want a 3D volume, just make the time dimension 1:

3dcalc -a jRandomDataset:3,7,5,1 -expr '3.14' -prefix test_02.nii.gz

The smallest spatial dimension the dset can have is \(2\times2\times1\). That is, it must be at least a “plane” of some kind.

If you have an existing dset and you want to make a copy of the same size (with matching header info), you could use 3dcalc‘s random functions. Consider making a copy of anat+orig.HEAD that is pure Gaussian noise of mean=0 and stdev=3:

3dcalc -a anat+orig.HEAD -expr 'gran(0,3)' -prefix test_04.nii.gz

If you wanted this noise added into the input dset, then you could run:

3dcalc -a anat+orig.HEAD -expr 'a+gran(0,3)' -prefix test_05.nii.gz

See 3dcalc‘s help for more random distributions.

If you want to make a new dset with a size related to an existing dset, such as adding or subtracting a few slices, then consider using 3dZeropad. As the program’s name implies, it can add slices to a dset in various ways (by side, symmetrically, evening out…), but when using negative values of arguments it can also remove slices. Most of the header info should still match the input dset (except for matrix size, obviously), which can be convenient.