Common conventions¶
Every nitorch command is written by hand around the same small set of ideas:
how arguments are split into options and values, which file formats are read
and written, how output files are named, how spatial transformations are
stored, and how a command is told to use a GPU. This page describes those
ideas once, so that the command pages can stay short and link here.
Running commands¶
nitorch is a single program that dispatches to a subcommand:
Running nitorch with no argument (or with -h or --help) lists every
available command. There are three equivalent ways to get the help text of a
command:
The same program can be started through the Python interpreter, which is
useful when the nitorch script is not on your PATH (for instance in a
virtual environment that is not activated):
Both forms run the same code. Each command is a separate process and starts by importing PyTorch, which takes a moment even for a small image.
A few things are worth knowing about how commands behave.
- There is no
--versionflag. Print the version of the installed package from Python instead:python -c "import nitorch; print(nitorch.__version__)". - The built-in help is a summary. It is terse, and for commands with many options it can lag behind the code. The reference pages describe what the commands actually do.
- Some commands have several levels of help.
nitorch register -h 2andnitorch register -h 3print progressively more detail than-h. - A start-up warning is normal without the compiled backend. Unless the
compiled backend is installed, every command
prints
nitorch uses its non-compiled backend (TS). Some algorithms may be slow.on standard error. The warning is harmless. Set the environment variableNI_CHECK_BACKEND=0to silence it. - The exit status tells whether a command succeeded. A command that
completes, or that prints its help with
-h, exits with status 0. A command that fails, whether because of its arguments or while working, prints an[ERROR]message on standard error and exits with status 1. This makes commands easy to chain in shell scripts, for examplenitorch reslice ... || echo failed. Called from Python, a command'scli([...])returns 1 instead of raising an exception.
register2
register2 appears in the list of commands. It is a hidden, backward
compatible alias of register, kept
so that older scripts continue to work.
How options are parsed¶
NITorch does not use argparse. Each command has its own parser, and two
parser families exist, but they follow the same rules, which are close to the
ones you know from other command-line tools, with a few differences that are
worth learning.
Files first, then options¶
Most commands take a list of input files followed by options:
The input files (the positional arguments) must come before the first
option. After that point, every argument that does not start a new option is
taken as a value of the option before it. A file name placed after -s 64 64
64 would be read as a fourth size, and fail to convert to a number.
An option takes all the values that follow it¶
An option (a tag) is followed by as many values as it accepts, up to the
next tag. Lists are written as separate, space-separated arguments, never with
commas and never with =:
nitorch crop T1w.nii.gz -s 64 64 64 # three values for -s
nitorch crop T1w.nii.gz --size 64 64 64 # the long spelling of the same option
The following spellings are not understood:
--size=64: there is no--tag=valueform; the whole word is rejected as an unknown option.-s64, or several short flags glued together (-sc): a tag is matched as a whole word.- Abbreviated long options (
--sifor--size): tags must be spelled out in full.
A value that is not valid for its option, such as foo where a number or one
of a fixed set of choices is expected, is an error that names the option and,
when there is a fixed set, lists the choices. A missing value is reported
separately, as "Not enough arguments for option ...".
Short tags may have more than one letter (-dt, -vx, -il); these are
still single-dash tags and must be typed as shown on the command's page.
Where a command accepts both a short and a long spelling, they are
interchangeable.
What counts as a tag, and what counts as a value¶
An argument is a tag when it starts with a dash and is not a number, where
"number" means anything Python's float() accepts. Everything else is a
value. This has three consequences.
- Negative numbers are values.
-2.5,-10,-.5,-1e-3,-infand-nancan be written as is wherever a number is expected, for example-c -12.4 6.4 3.2. - Any other dash-prefixed argument must be a known option. An argument
such as
--nopeis an error, not a file name: the command stops withUnknown option --nope. This includes--tag=valueforms. A file name that starts with a dash must therefore be written with a directory in front of it, as in./-odd-name.nii.gz. autoregis stricter. It still treats any argument that starts with a dash as a tag, so negative numbers cannot be given to it as values.
A lone - is a value, and so is a group tag such as @loss.
Ending option parsing with --¶
As is usual on Unix, a bare -- ends option parsing: every argument after it
is a value, even if it starts with a dash. In most commands, those arguments
are the top-level positional inputs, or are added to the input files. A later
-- is a value too, and -h after -- is a value rather than a request for
help.
Commands differ in where the arguments after -- go. Some, such as register
and affreg, have no positional inputs and report -- as an error; a few
others attach the arguments to the option just before it. The command pages
say so when it matters. Writing the name with a directory in front of it
(./-odd-name.nii.gz) works everywhere.
Keywords among numbers¶
Some options take a list of numbers optionally followed by a keyword that says how to interpret them. The keyword is recognised by its text, wherever it appears among the values, and is usually written last:
The keywords (vox, ras, mm, %, ...) are described in
Coordinates and units.
Flags, optional values and booleans¶
An option that takes no value is a flag: its presence turns the feature on.
Some flags also accept an optional value, either a boolean or a level. For
example, nitorch info -s computes statistics, and nitorch info -s false
leaves them off.
Write booleans as true or false, or as 1 or 0; these spellings are
accepted wherever a command takes a boolean. Some commands also accept yes
and no, or the initials t, y, n, but not all of them do (nitorch
info -s no is an error), so prefer the first two spellings. Take care with
flags that accept an optional value: a flag swallows the next argument if that
argument is not a tag. If you put an input file after such a flag, the flag
tries to read it as its value. Give the flag an explicit value, or put it
after the files and before another tag, where there is nothing for it to
swallow.
Groups¶
Commands that need structured input (a registration made of several loss
terms, a quantitative fit made of several contrasts) divide their arguments
into groups. Depending on the command, a group is opened by an option such
as -c (estatics, greeq) or by a word that starts with @, as in
@loss, @affine and @nonlin for register.
Nested groups start with @@. Everything between the opening word and the
next group belongs to the group. Each command's page explains its own groups.
Errors¶
When a command cannot make sense of its arguments it prints its full help text, followed by a line of the form
on standard error. The other argument errors have the same form:
[ERROR] Invalid value 'foo' for --affine; choose from {rigid, similitude, affine}
[ERROR] Not enough arguments for option -s: expected at least 3 but got 2.
The first is raised when a value cannot be used (the list of choices is left
out for options that are not a choice, such as a number that fails to parse);
the second when values are missing, and it is followed by " Choose from {...}."
if the option has a fixed set of choices. Asking for help with -h always
works, even if the command line also contains unknown options. Commands with
their own hand-written parsers (vopt, autoreg, crop, pad,
extract_patches, affopt, reslice and compose) may word these errors
differently. Errors raised while the command works (a missing file, a
value of the wrong type) are reported in the same [ERROR] ... form, without
a Python traceback. In both cases the command exits with status 1; see
Running commands.
File formats¶
NITorch reads and writes volumes through its own abstraction layer
(nitorch.io), which delegates the parsing of each format to a specialised
library. The format of a file to read is found from its extension; if that
fails, NITorch tries the other readers in turn, so a file with a missing or
unusual extension is often still read correctly. The format of a file to
write is chosen from the extension of the output name. If the extension is
not recognised, the format of the input file (the like file) is used, and
failing that NIfTI-1.
Volumes¶
| Format | Extensions | Read | Write | Needs |
|---|---|---|---|---|
| NIfTI-1, -2 | .nii, .nii.gz |
yes | yes | nibabel |
| MGH / MGZ | .mgh, .mgz |
yes | yes | nibabel |
| TIFF | .tif, .tiff |
yes | see note | tifffile |
The nibabel and tifffile packages are optional dependencies; see
Installation. Without nibabel,
NITorch cannot read neuroimaging volumes at all, so for command-line use you
should always install at least the io extra.
NIfTI and MGH (with optional gzip compression, as in .nii.gz) are the
formats NITorch is developed and tested with. nibabel recognises other
formats as well (Analyze, MINC, PAR/REC, and so on); NITorch may open some of
them, but they are not supported. A TIFF writer exists in the library, but the
convert command documents TIFF as an input
format only.
When a command writes a modified copy of an input volume (cropping, smoothing,
resampling, and so on), it copies the metadata of the input, such as the data
type, intensity scaling and units, and replaces the fields that the operation
changes, namely the shape and the orientation matrix. Use
info to inspect the result and
convert to change format or data type.
Volumes are three-dimensional. Further dimensions (echoes, contrasts, the three components of a displacement field, ...) are treated as channels by the commands that support them, and are stored after the three spatial ones.
Transformations¶
| Format | Extensions | Read | Write | Notes |
|---|---|---|---|---|
| FreeSurfer linear transform | .lta |
yes | yes | The default for affine matrices. |
FreeSurfer register.dat |
.dat |
yes | yes | tkregister-style registration files. |
Dense fields (displacement and velocity fields) are not stored in a dedicated format: they are ordinary volumes, in any of the volume formats above. See Spatial transformations.
Streamlines¶
reslice can also apply transformations to
streamlines stored in TrackVis .trk files.
Output filenames¶
Commands that write a modified copy of an input build the output name from
the input name and write it next to the input, usually with a suffix such
as .crop or .pad, so that the input is left untouched.
convert is the exception
The default output template of convert
is {dir}{sep}{base}{ext}, which is the input name itself. Unless you
change the format (-f) or give an output name (-o), convert
overwrites its input.
Options such as -o, --output or -t, --transform let you
choose another name, and the name you give is a template: it may contain
placeholders in braces, which the command replaces for each input file.
Four placeholders are available in nearly every command:
| Placeholder | Replaced by |
|---|---|
{dir} |
The directory of the input file (. when the input name has no directory). |
{sep} |
The path separator of your operating system (/ on Linux and macOS). |
{base} |
The file name without its extension. |
{ext} |
The extension, including the leading dot and any compression suffix. |
A compression suffix is treated as part of the extension, so that the input
data/sub-01_T1w.nii.gz gives {dir} = data, {base} = sub-01_T1w and
{ext} = .nii.gz. The default template of crop,
{dir}{sep}{base}.crop{ext}, therefore produces data/sub-01_T1w.crop.nii.gz.
You can use the placeholders to put results in another directory, to change
the suffix, or to change the format by changing the extension:
Quote templates that contain braces, so that your shell does not interpret
them, and in particular so that bash does not try to expand {a,b}-style
patterns.
Some commands define additional placeholders:
| Placeholder | Commands | Replaced by |
|---|---|---|
{i} |
chunk, unstack |
The index of the piece, counting from 1. |
{i}, {j}, {k} |
extract_patches |
The index of the patch along each axis, counting from 1. |
{n} |
epic |
The index of the echo, counting from 0. |
{layout} |
orient, reorient |
The orientation of the output, for example RAS. |
{label} |
affopt, vopt |
The label of the image in the groupwise model. |
{name} |
register and its wrappers |
The name of the transformation model. |
{space} |
uniseg |
The space of the output: nat, mni or wrp. |
Not every command supports every placeholder. Each command page lists the placeholders that it accepts and the default template of each output. A few points are worth remembering:
- A template without any placeholder is used as is. When you give a single
literal name to a command that processes several inputs, every result is
written to the same file, and only the last one survives. Use
{base}(or give one name per input) to avoid this. - A literal brace in a file name must be doubled (
{{and}}). - Because results are written next to the inputs by default, the input
directory must be writable. Use
{dir}and a fixed directory in the template, or the output directory option of the command where there is one, to write elsewhere. - A few commands do not supply every one of the four common placeholders;
meanspace, for instance, has no{sep}, and its templates use a literal/.
Spatial transformations¶
Many commands produce, consume or combine spatial transformations: registration
commands estimate them, reslice applies them,
compose chains them. Three kinds exist, and
every command that takes a transformation tells you which kinds it accepts.
Linear transformations¶
A linear (affine) transformation is a 4×4 matrix that acts on
homogeneous world coordinates [x, y, z, 1], in millimetres, in RAS space (see
Coordinates and units). It combines rotations,
translations, zooms and shears.
Matrices are stored as FreeSurfer .lta files, or less often as .dat
files. Whatever the type stored in an LTA file (voxel-to-voxel,
RAS-to-RAS, ...), NITorch converts it to a RAS-to-RAS matrix when it reads it,
using the orientation matrices stored in the file. Most NITorch commands write
RAS-to-RAS transforms. The LTA file also records the shape and orientation
matrix of the source and destination images; crop
uses them when you give it a transform file instead of a size.
Direction. Transformations are applied the way an image is resampled: for
each voxel of the output image, a transformation says where to look in the
input image. A matrix therefore maps coordinates of the output (target) space
to coordinates of the input space. For registration this means that the
matrix written by register and its
wrappers maps world coordinates of the fixed image onto world coordinates of
the moving image. It is the matrix to pass to reslice -l to resample the
moving image into the space of the fixed image.
Displacement fields¶
A displacement field assigns a vector to each voxel of a lattice. It is stored
as a volume whose extra dimension holds the three components (along x, y and
z), and the orientation matrix of the file describes the lattice. Applying
the field to an image means sampling the image at x + d(x), where x is a
location of the lattice and d is the field.
The unit of the displacements is declared by the command that reads the
field, typically with an option named -u/--unit:
vox(the default inresliceandcompose): displacements are measured in voxels of the field's own lattice.mm: displacements are measured in millimetres of world space.
Velocity fields¶
A velocity field is a displacement-like field that is integrated to obtain
a transformation. NITorch commands use stationary velocity fields: the
transformation is the exponential of the field, computed by scaling and
squaring, and its inverse is obtained by negating the velocity. Velocities are
expressed in voxels of the field's lattice. Registrations that use geodesic
shooting also write a small JSON file with the regularisation parameters next
to the velocity field; commands that read the field use that file, when it is
present, to integrate it correctly. vexp
converts a velocity field into a displacement (or transformation) field.
Because a velocity field encodes a transformation that is invertible by
construction, the default non-linear model of
register is a velocity field rather
than a displacement field.
Combining transformations¶
Commands that accept several transformations, such as reslice and compose,
take them as a sequence of options (-l for a linear transformation, -d for a
displacement field, -v for a velocity field). Each one can be inverted by
inserting an i (-il, -id, -iv) or appending -inverse to the long
form. The transformations are applied in the order that the reslicing formula
implies: for an output voxel at world position p in the target space, the
last transformation on the command line is applied first, and the first one
last, and the result is a position in the input space. See the pages of
reslice and
compose for the exact formula and for the
-t, --target option that selects the output space.
Coordinates and units¶
Voxel and world coordinates¶
Every volume carries an orientation matrix (also called the vox2ras or
voxel-to-world matrix): a 4×4 affine matrix that maps voxel indices to
world coordinates. Voxel indices start at zero, and the first voxel is centred
on index 0. The world space is RAS+, in millimetres: the first world axis
increases towards the Right, the second towards the Anterior and the third
towards the Superior. This is the convention of NIfTI and of nibabel. The
columns of the orientation matrix are ordered like the dimensions of the data.
info prints, among other things, the centre of
the volume in world coordinates (mm (RAS)) and its layout.
Layouts¶
The layout of a volume is the order and direction in which its axes are
stored on disk. It is written as three letters, one per stored axis, naming
the direction towards which that axis points: R or L (towards the right or
the left), A or P (anterior or posterior), S or I (superior or
inferior). A volume stored with the first axis running left to right, the
second posterior to anterior and the third inferior to superior has layout
RAS. Commands that act on voxel
axes, such as crop, pad
or orient, pay attention to the layout.
Space keywords¶
Commands that take sizes or positions can usually be told in which space they are given, with one of these keywords placed after the numbers:
| Keyword | Meaning |
|---|---|
vox |
Voxel units: indices and counts along the stored axes of the volume. |
ras |
World units: millimetres along the world axes (R, A, S), whatever the layout of the volume. |
For example, -s 64 64 64 crops a cube of 64 voxels, whereas -s 40 40 40 ras
crops a cube of 40 mm (the number of voxels then depends on the voxel size).
The default is vox. A position given with ras is a point in world space,
and is converted to voxel coordinates with the inverse of the orientation
matrix; a size given with ras is expressed along the world axes and is
re-ordered to match the stored axes.
Physical units and percentages¶
Quantities that are lengths in the physical sense, such as the full width at
half maximum of a smoothing kernel or a voxel size, are accepted with a unit
keyword, most often mm, and in smooth
vox too. Some commands also accept %, to say that a value is a
percentage of a reference quantity; for example, the voxel size of a mean
space can be given as a percentage of the mean voxel size of the inputs
(meanspace). The unit that applies when you
omit the keyword differs between commands and is stated on each command page.
Hardware¶
NITorch commands run on the CPU by default. Most commands that do heavy computation can use a CUDA GPU through PyTorch:
| Option | Meaning |
|---|---|
--cpu |
Run on the CPU. |
--cpu N |
Run on the CPU with N threads. Only where the command's help shows --cpu [THREADS]. |
--gpu |
Run on the GPU. |
--gpu ID |
Run on the GPU with index ID (0 is the first one). |
The double-dash spellings --cpu and --gpu are accepted by every command
that has a hardware option. Some commands, namely reslice, compose, vexp,
pool, inpaint, denoise_mri and autoreg, accept the single-dash
spellings -cpu and -gpu as well, but in these commands --cpu takes no
number of threads. Commands whose help shows --cpu [THREADS] (for example
register, denoise, meanspace and uniseg) pass the number on to
PyTorch, which then uses that many CPU threads.
Which GPU is device 0 is decided by CUDA, so you can use the environment
variable CUDA_VISIBLE_DEVICES to restrict the GPUs that are visible, as with
any PyTorch program. Commands that do not accept these options, such as
info or crop, do
their little work on the CPU.
Without a usable GPU
Many commands notice that CUDA is not available and switch to the CPU,
printing a warning if you asked for the GPU. Others do not check, and
PyTorch stops with an error. If you see a CUDA error, run the command
again without --gpu.
Environment variables¶
A few environment variables change how NITorch itself behaves, independently of the command.
| Variable | Effect |
|---|---|
NI_CHECK_BACKEND |
Set to 0 to silence the start-up warning about the non-compiled backend. |
NI_COMPILED_BACKEND |
Choose the implementation of the resampling routines at run time: C, TS or MONAI. See Compiled backend. |
NI_SHOW_COMPILED_BACKEND |
Set to 1 to print which implementation is loaded. |