Skip to content

MMORF - FSL's MultiMOdal Registration Framework

MMORF is the new nonlinear registration tool from FSL. What sets it apart from the previous tool, FNIRT, is the ability to simultaneously align multiple modalities, as well as a unique regularisation method.

If you have used FNIRT in the past, then many of MMORF's options may be familiar to you, however the difference are significant enough that you will need to read this user guide before using it.

Version Changes

See CHANGELOG.md for important version updates.

As MMORF is still in Beta, always check for compatibility breaking changes in new versions, particularly when it comes to config files.

Usable Modalities

MMORF is capable of registering both scalar and tensor image pairs. Tensor images must be in the FSL FDT format, i.e.:

  • Upper-triangular volumes ares stored
  • Diffusivity in the x-direction is defined radiologically

If you have used FSL to preprocess your data then you shouldn't need to worry about this.

Parameter Options

Here I will describe the parameters for which arguments must be provided when running MMORF. Parameters described as "scalar" accept a single input argument. Parameters described as "vector" accept multiple input arguments separated by spaces. Parameters are either defined once per registration, or once per image pair.

All arguments passed via the command line (i.e., not via a config file) must follow the format:

--parameter_name <argument>

Warp Options

warp_res_init

  • Initial warp resolution in mm (isotropic)
  • float
  • scalar
  • defined once per registration

This defines the coarsest warp resolution in a multi-iteration registration. For human data, 32 is a reasonable choice.

warp_scaling

  • Warp resolution scaling at each iteration
  • int
  • vector
  • defined for all iterations once per registration

This defines how the warp resolution should be increased at each iteration. For example, if:

warp_res_init = 32 warp_scaling = 1 2 2

then the registration will consist of 3 iterations with warp resolutions of 32mm, 16mm and 8mm respectively.

img_warp_space

  • 3D NIfTI volume defining the space in which the warp field will be calculated
  • string
  • scalar
  • defined once per registration

Can be provided with or without a suffix (i.e., both T1.nii.gz and simply T1 are acceptable). The final warp field will be a 4D volume with the same extents and voxel dimensions as this image.

Scalar Image Options

img_ref_scalar

  • 3D NIfTI volume for a scalar reference image
  • string
  • scalar
  • defined once per image pair

A scalar modality (T1, T2, etc.) belonging to the reference subject. Can be provided with or without a suffix (i.e., both T1.nii.gz and simply T1 are acceptable).

img_mov_scalar

  • 3D NIfTI volume for a scalar reference image
  • string
  • scalar
  • defined once per image pair

A scalar modality (T1, T2, etc.) belonging to the moving subject. Can be provided with or without a suffix (i.e., both T1.nii.gz and simply T1 are acceptable).

aff_ref_scalar

  • Affine transform in FSL FLIRT .mat format for a scalar reference image
  • string
  • scalar
  • defined once per image pair

Must be provided with the file extension. Should be the output of registering img_ref_scalar to img_warp_space.

aff_mov_scalar

  • Affine transform in FSL FLIRT .mat format for a scalar moving image
  • string
  • scalar
  • defined once per image pair

Must be provided with the file extension. Should be the output of registering img_mov_scalar to img_warp_space.

lambda_scalar

  • Lambda for weighting scalar image cost function at each iteration.
  • float
  • vector
  • defined for all iterations once per image pair

Can be set to 1 as a good default. If using N highly-correlated modalities (e.g., T1 & T2), consider setting to 1/N for each modality (e.g., 0.5, 0.5).

fwhm_ref_scalar

  • FWHM for Gaussian smoothing of scalar reference image at each iteration
  • float
  • vector
  • defined for all iterations once per image pair

A good default is a quarter of the current warp resolution. For example:

warp_res_init = 32 warp_scaling = 1 2 2 fwhm_ref_scalar = 8 4 2

fwhm_mov_scalar

  • FWHM for Gaussian smoothing of scalar moving image at each iteration
  • float
  • vector
  • defined for all iterations once per image pair

A good default is a quarter of the current warp resolution.

Tensor Image Options

img_ref_tensor

  • 3D NIfTI volume for a tensor reference image
  • string
  • scalar
  • defined once per image pair

A tensor modality (specifically a DTI) belonging to the reference subject. Can be provided with or without a suffix (i.e., both T1.nii.gz and simply T1 are acceptable).

img_mov_tensor

  • 3D NIfTI volume for a tensor reference image
  • string
  • scalar
  • defined once per image pair

A tensor modality (specifically a DTI) belonging to the moving subject. Can be provided with or without a suffix (i.e., both T1.nii.gz and simply T1 are acceptable).

aff_ref_tensor

  • Affine transform in FSL FLIRT .mat format for a tensor reference image
  • string
  • scalar
  • defined once per image pair

Must be provided with the file extension. Should be the output of registering img_ref_tensor to img_warp_space.

aff_mov_tensor

  • Affine transform in FSL FLIRT .mat format for a tensor moving image
  • string
  • scalar
  • defined once per image pair

Must be provided with the file extension. Should be the output of registering img_mov_tensor to img_warp_space.

lambda_tensor

  • Lambda for weighting tensor image cost function at each iteration.
  • float
  • vector
  • defined for all iterations once per image pair

Can be set to 1 as a good default. If using N highly-correlated modalities (e.g., T1 & T2), consider setting to 1/N for each modality (e.g., 0.5, 0.5).

fwhm_ref_tensor

  • FWHM for Gaussian smoothing of tensor reference image at each iteration
  • float
  • vector
  • defined for all iterations once per image pair

A good default is a quarter of the current warp resolution. For example:

warp_res_init = 32 warp_scaling = 1 2 2 fwhm_ref_tensor = 8 4 2

fwhm_mov_tensor

  • FWHM for Gaussian smoothing of tensor moving image at each iteration
  • float
  • vector
  • defined for all iterations once per image pair

A good default is a quarter of the current warp resolution.

Mask Options

use_implicit_mask

  • Treat zeros in images as missing data during smoothing
  • int: 0 = False, 1 = True
  • scalar
  • defined once per registration

Prevents darkening of the edges of images. My experience is that in most cases this should just be set to 0.

mask_ref_scalar

  • 3D NIfTI volume for a soft mask in scalar reference image space
  • string
  • scalar
  • defined once per image pair

The cost function for the associated image pair is multiplied voxelwise by this mask. Can be provided with or without a suffix (i.e., both mask.nii.gz and simply mask are acceptable). If you do not with to supply a mask image, use the keyword NULL

mask_mov_scalar

  • 3D NIfTI volume for a soft mask in scalar moving image space
  • string
  • scalar
  • defined once per image pair

The cost function for the associated image pair is multiplied voxelwise by this mask. Can be provided with or without a suffix (i.e., both mask.nii.gz and simply mask are acceptable). If you do not with to supply a mask image, use the keyword NULL

mask_ref_tensor

  • 3D NIfTI volume for a soft mask in tensor reference image space
  • string
  • scalar
  • defined once per image pair

The cost function for the associated image pair is multiplied voxelwise by this mask. Can be provided with or without a suffix (i.e., both mask.nii.gz and simply mask are acceptable). If you do not with to supply a mask image, use the keyword NULL

mask_mov_tensor

  • 3D NIfTI volume for a soft mask in tensor moving image space
  • string
  • scalar
  • defined once per image pair

The cost function for the associated image pair is multiplied voxelwise by this mask. Can be provided with or without a suffix (i.e., both mask.nii.gz and simply mask are acceptable). If you do not with to supply a mask image, use the keyword NULL

use_mask_ref_scalar

  • Apply mask_ref_scalar at each iteration
  • int: 0 = False, 1 = True
  • vector
  • defined for all iterations once per image pair

Whether or not the mask_ref_scalar should be used at a particular iteration.

use_mask_mov_scalar

  • Apply mask_mov_scalar at each iteration
  • int: 0 = False, 1 = True
  • vector
  • defined for all iterations once per registration

Whether or not the mask_ref_scalar should be used at a particular iteration.

use_mask_ref_tensor

  • Apply mask_ref_tensor at each iteration
  • int: 0 = False, 1 = True
  • vector
  • defined for all iterations once per image pair

Whether or not the mask_ref_tensor should be used at a particular iteration.

use_mask_mov_tensor

  • Apply mask_mov_tensor at each iteration
  • int: 0 = False, 1 = True
  • vector
  • defined for all iterations once per registration

Whether or not the mask_ref_tensor should be used at a particular iteration.

Bias Field Options

estimate_bias

  • Estimate a multiplicative bias field for a scalar image pair
  • int: 0 = False, 1 = True
  • vector
  • defined for all iterations once per image pair

Whether or not to update the bias field estimate at the start of a particular iteration. Note that this is only applicable to scalar image pairs.

bias_res_init

  • Initial bias field resolution in mm (isotropic)
  • float
  • scalar
  • defined once per registration

This defines the resolution of the estimated multiplicative bias field. For human data, 32 is a reasonable choice.

lambda_bias_reg

  • Lambda for weighting the Bending Energy regularisation.
  • float
  • vector
  • defined for all iterations once per image pair

Control how smoothly the estimated bias field varies. A sensible default for this is a constant 1e9 for all iterations.

Regularisation Options

lambda_reg

  • Lambda for weighting the warp regularisation.
  • float
  • vector
  • defined for all iterations once per registration

This sets the regularisation at each iteration, and is independent of the number of modalities used. The regularisation method is almost always the SPRED penalty, which penalises the log of the singular values of the local Jacobian field. I say almost always because of a slight quirk - for a transformation of exactly zero, the Hessian of the SPRED penalty is undefined. Therefore, for the first iteration of the registration, MMORF is always run with Bending Energy regularisation.

NB!!! The upshot of this is that the recommendation is to always run two iterations at your coarsest resolution, which will result in one iteration with Bending Energy that acts as an initialisation for one iteration with SPRED regularisation.

For example:

warp_res_init = 32 warp_scaling = 1 1 2 lambda_reg = 4.0e5 3.7e-1 3.1e-1

would run one iteration at 32mm warp resolution with a Bending Energy regularisation of 4.0e5, one iteration at 32mm warp resolution with a SPRED regularisation of 3.7e-1, and one iteration at 16mm warp resolution with a SPRED regularisation of 3.1e-1. Note that the regularisation scale is very different between the two methods and they are therefore not directly comparable. This behaviour is likely to change in the future, and such changes will be indicated prominently.

Output Options

warp_out

  • basename of final warp field
  • string
  • scalar
  • defined once per registration

Must be supplied without a suffix. The final warp field will be a 4D volume with the same extents and voxel dimensions as img_warp_space.

jac_det_out

  • basename of Jacobian determinant of final warp field
  • string
  • scalar
  • defined once per registration

Must be supplied without a suffix. Will output a 3D volume with the same extents and voxel dimensions as img_warp_space.

bias_out

  • basename of bias field for scalar image pairs
  • string
  • scalar
  • defined once per registration

Must be supplied without a suffix. Will output a 3D volume with the same extents and voxel dimensions as img_warp_space. One volume will be output for each scalar image pair. Argument may be NULL if not required.

Optimiser Options

At each iteration of the registration, a number of optimisation iterations are performed.

hires

  • warp resolution (in mm isotropic) below which a low-memory optimisation strategy will be used
  • float
  • scalar
  • defined once per registration

As warp resolution increases, memory requirements for Levenberg Marquardt (LM) optimisation increases. Beyond a certain resolution, the memory requirements may exceed the GPU's maximum available RAM. At this point, MMORF switches to an optimisation strategy which requires more iterations for convergence, but has much lower memory requirements, such as Majorise Minimisation (MM) or Scaled Conjugate Gradient (SCG).

For example, with:

warp_res_init = 32 warp_scaling = 1 1 2 2 2 hires = 6

the warp resolution at each iteration is 32, 32, 16, 8 and 4mm respectively. With this hires setting, only the 4mm iteration will use the low memory optimisation method.

What setting to use will depend on the field of view (FOV) of img_warp_space, and the amount of RAM on your GPU. For GPUs with 12GB or more of RAM, a good default to use for human data is 4mm for an FOV similar to the MNI-152 template, or 8mm for larger FOVs.

optimiser_lowres

  • which optimiser to use for coarser warp resolutions (> hires)
  • enum: in {LM, MM, SCG} -> {0, 1, 2}
  • scalar
  • defined once per registration

Choose between Levenberg Marquardt (LM), Majorise Minimisation (MM) or Scaled Conjugate Gradient for coarse resolution warps with resolution larger than the hires setting. LM is recommended.

optimiser_hires

  • which optimiser to use for finer warp resolutions (< hires)
  • enum: in {LM, MM, SCG} -> {0, 1, 2}
  • scalar
  • defined once per registration

Choose between Levenberg Marquardt (LM), Majorise Minimisation (MM) or Scaled Conjugate Gradient for fine resolution warps with resolution smaller than the hires setting. MM is recommended.

optimiser_max_it_lowres

  • number of optimisation iterations to run for each coarse resolution warp iteration
  • int
  • scalar
  • defined once per registration

Applies to warp resolutions larger than hires. If using the recommended optimiser_lowres = LM is used, then a good default is between 5 and 10.

optimiser_max_it_hires

  • number of optimisation iterations to run for each fine resolution warp iteration
  • int
  • scalar
  • defined once per registration

Applies to warp resolutions smaller than hires. If using the recommended optimiser_hires = MM is used, then a good default is between 5 and 10. If using optimiser_hires = SCG then a value between 10 and 20 is advised.

optimiser_rel_tol_lowres

  • relative tolerance for testing convergence of the optimiser for each coarse resolution warp iteration
  • float
  • scalar
  • defined once per registration

Applies to warp resolutions larger than hires. Allows early stopping of optimisation before reaching optimiser_max_it_lowres if the decrease in the cost function penalty is less than this value relative to the previous cost. A good default is 1e-3.

optimiser_rel_tol_hires

  • relative tolerance for testing convergence of the optimiser for each fine resolution warp iteration
  • float
  • scalar
  • defined once per registration

Applies to warp resolutions smaller than hires. Allows early stopping of optimisation before reaching optimiser_max_it_hires if the decrease in the cost function penalty is less than this value relative to the previous cost. A good default is 1e-3.

Solver Options

Each iteration of the optimisation makes use of an iterative linear solver to calculate the update to the warp field.

solver_max_it_lowres

  • number of solver iterations to run for each coarse resolution optimiser iteration
  • int
  • scalar
  • defined once per registration

Applies to warp resolutions larger than hires. A good default is 100.

solver_max_it_hires

  • number of solver iterations to run for each fine resolution optimiser iteration
  • int
  • scalar
  • defined once per registration

Applies to warp resolutions smaller than hires. A good default is 100

solver_rel_tol_lowres

  • relative tolerance for testing convergence of the solver for each coarse resolution optimiser iteration
  • float
  • scalar
  • defined once per registration

Applies to warp resolutions larger than hires. Allows early stopping of the solver before reaching solver_max_it_lowres if the decrease in the solver error is less than this value relative to the previous error. A good default is 1e-3.

solver_rel_tol_hires

  • relative tolerance for testing convergence of the solver for each fine resolution optimiser iteration
  • float
  • scalar
  • defined once per registration

Applies to warp resolutions smaller than hires. Allows early stopping of the solver before reaching solver_max_it_hires if the decrease in the solver error is less than this value relative to the previous error. A good default is 1e-3.

Miscellaneous Options

config

  • config file in .ini format containing some or all of the options required to run MMORF
  • string
  • scalar
  • defined once per registration

Must be the full filename, and must have the .ini suffix. A handy way of configuring MMORF without having to specify all arguments on the command line.

Config Files

By far the most convenient and reproducible method of running MMORF is through the use of config files. Config files use the .ini suffix and each line is either a command line option or a comment. Comment lines begin with a semicolon (;) symbol. Command lines are of the form:

option_name = option_value_1 option_value_2 option_value_3

The order in which options are entered is important, and should correspond to the order in which the volumes for each modality is entered. The easiest way of understanding this is probably with a few simple examples. In each case the warp space is the T1 image for the reference subject. These config files assume that affine matrices (in FSL FLIRT format) are available for each reference and moving image.

Unimodal Scalar Config Example

; This is a comment at the start of the config file
; Parameters defining the overall registration:

warp_res_init           = 32
warp_scaling            = 1 1 2 2 2
img_warp_space          = ../data/vols/ref/t1/t1
lambda_reg              = 4.0e5 3.7e-1 3.1e-1 2.6e-1 2.2e-1
hires                   = 6
optimiser_max_it_lowres = 5
optimiser_max_it_hires  = 5

; Parameters relating to first scalar image pair

img_ref_scalar      = ../data/vols/ref/t1/t1
img_mov_scalar      = ../data/vols/mov/t1/t1
aff_ref_scalar      = ../data/mats/identity.mat
aff_mov_scalar      = ../data/mats/mov_to_ref_t1.mat
use_implicit_mask   = 0
use_mask_ref_scalar = 0 0 0 0 0
use_mask_mov_scalar = 0 0 0 0 0
mask_ref_scalar     = NULL
mask_mov_scalar     = NULL
fwhm_ref_scalar     = 8.0 8.0 4.0 2.0 1.0
fwhm_mov_scalar     = 8.0 8.0 4.0 2.0 1.0
lambda_scalar       = 1 1 1 1 1
estimate_bias       = 0
bias_res_init       = 32
lambda_bias_reg     = 1e9 1e9 1e9 1e9 1e9

Multimodal Scalar Only Config Example

; This is a comment at the start of the config file
; Parameters defining the overall registration:

warp_res_init           = 32
warp_scaling            = 1 1 2 2 2
img_warp_space          = ../data/vols/ref/t1/t1
lambda_reg              = 4.0e5 3.7e-1 3.1e-1 2.6e-1 2.2e-1
hires                   = 6
optimiser_max_it_lowres = 5
optimiser_max_it_hires  = 5

; Parameters relating to first scalar image pair

img_ref_scalar      = ../data/vols/ref/t1/t1
img_mov_scalar      = ../data/vols/mov/t1/t1
aff_ref_scalar      = ../data/mats/identity.mat
aff_mov_scalar      = ../data/mats/mov_t1_to_ref_t1.mat
use_implicit_mask   = 0
use_mask_ref_scalar = 0 0 0 0 0
use_mask_mov_scalar = 0 0 0 0 0
mask_ref_scalar     = NULL
mask_mov_scalar     = NULL
fwhm_ref_scalar     = 8.0 8.0 4.0 2.0 1.0
fwhm_mov_scalar     = 8.0 8.0 4.0 2.0 1.0
lambda_scalar       = 1 1 1 1 1
estimate_bias       = 0
bias_res_init       = 32
lambda_bias_reg     = 1e9 1e9 1e9 1e9 1e9

; Parameters relating to second scalar image pair

img_ref_scalar      = ../data/vols/ref/t2/t2
img_mov_scalar      = ../data/vols/mov/t2/t2
aff_ref_scalar      = ../data/mats/ref_t2_to_t1.mat
aff_mov_scalar      = ../data/mats/mov_t2_ref_t1.mat
use_implicit_mask   = 0
use_mask_ref_scalar = 0 0 0 0 0
use_mask_mov_scalar = 0 0 0 0 0
mask_ref_scalar     = NULL
mask_mov_scalar     = NULL
fwhm_ref_scalar     = 8.0 8.0 4.0 2.0 1.0
fwhm_mov_scalar     = 8.0 8.0 4.0 2.0 1.0
lambda_scalar       = 1 1 1 1 1
estimate_bias       = 0
bias_res_init       = 32
lambda_bias_reg     = 1e9 1e9 1e9 1e9 1e9

Multimodal Scalar & Tensor Config Example

; This is a comment at the start of the config file
; Parameters defining the overall registration:

warp_res_init           = 32
warp_scaling            = 1 1 2 2 2
img_warp_space          = ../data/vols/ref/t1/t1
lambda_reg              = 4.0e5 3.7e-1 3.1e-1 2.6e-1 2.2e-1
hires                   = 6
optimiser_max_it_lowres = 5
optimiser_max_it_hires  = 5

; Parameters relating to first scalar image pair

img_ref_scalar      = ../data/vols/ref/t1/t1
img_mov_scalar      = ../data/vols/mov/t1/t1
aff_ref_scalar      = ../data/mats/identity.mat
aff_mov_scalar      = ../data/mats/mov_t1_to_ref_t1.mat
use_implicit_mask   = 0
use_mask_ref_scalar = 0 0 0 0 0
use_mask_mov_scalar = 0 0 0 0 0
mask_ref_scalar     = NULL
mask_mov_scalar     = NULL
fwhm_ref_scalar     = 8.0 8.0 4.0 2.0 1.0
fwhm_mov_scalar     = 8.0 8.0 4.0 2.0 1.0
lambda_scalar       = 1 1 1 1 1
estimate_bias       = 0
bias_res_init       = 32
lambda_bias_reg     = 1e9 1e9 1e9 1e9 1e9

; Parameters relating to first tensor image pair

img_ref_tensor      = ../data/vols/ref/dti/dti
img_mov_tensor      = ../data/vols/mov/dti/dti
aff_ref_tensor      = ../data/mats/ref_dti_to t1.mat
aff_mov_tensor      = ../data/mats/mov_dti_to_ref_t1.mat
use_mask_ref_tensor = 0 0 0 0 0
use_mask_mov_tensor = 0 0 0 0 0
mask_ref_tensor     = NULL
mask_mov_tensor     = NULL
fwhm_ref_tensor     = 8.0 8.0 4.0 2.0 1.0
fwhm_mov_tensor     = 8.0 8.0 4.0 2.0 1.0
lambda_tensor       = 1 1 1 1 1

Installing MMORF

The CUDA version of MMORF is installed as part of FSL 6.0.7 and newer. The CPU version of MMORF is installed with FSL 6.0.7.19 and newer.

You can also install MMORF independently of FSL using conda. Assuming that you have miniconda or equivalent installed, you can create a conda environment with the CUDA version of MMORF like so:

conda create -c https://fsl.fmrib.ox.ac.uk/fsldownloads/fslconda/public/ -c conda-forge -n mmorf fsl-mmorf-cuda-11.0

Or to install the CPU version:

conda create -c https://fsl.fmrib.ox.ac.uk/fsldownloads/fslconda/public/ -c conda-forge -n mmorf fsl-mmorf-cpu

Note that the CUDA MMORF conda package is currently named fsl-mmorf-cuda-11.0. It has been compiled against version 11.0 of the CUDA toolkit, but does not require the CUDA toolkit to be installed - it should work on any modern CUDA-capable GPU.

Compiling MMORF

To compile MMORF, you will need:

  • FSL 6.0.7 or newer (or a conda environment with MMORF installed, as outlined above).
  • A CUDA Toolkit installation (not necessary to compile the CPU version of MMORF).

  • Ensure that your environment is configured correctly:

  • If compiling the CUDA version of MMORF, the nvcc executable from your CUDA toolkit must be available on your $PATH variable, e.g.:

    export PATH=<cuda-toolkit>/bin/:$PATH
    

  • FSL is configured in your environment. e.g.:

    export FSLDIR=<fsl-installation>
    source $FSLDIR/etc/fslconf/fsl-devel.sh
    

  • Install make and GCC. If you are compiling the CUDA version of MMORF, note that the required GCC version differs depending on which CUDA Toolkit version you are compiling against. More information on this is available here. A convenient method of installing and maintaining separate GCC versions is to use separate conda environments, e.g.:

    $FSLDIR/bin/conda create -n gcc8 -y make "gxx_linux-64 8"
    source $FSLDIR/bin/activate gcc8
    

  • Install Git LFS. MMORF uses Git LFS to store some data files used for testing, so you need Git LFS installed if you would like to run the tests. Go to https://git-lfs.com/ and follow the instructions to install it on your system.

  • Clone the MMORF git repository.

    git clone https://git.fmrib.ox.ac.uk/fsl/MMORF
    cd MMORF
    

  • To compile the CPU version of MMORF, set the MMORF_BUILD=CPU variable:

    make MMORF_BUILD=CPU
    
    or
    export MMORF_BUILD=CPU
    make
    

  • To compile the CUDA version of MMORF, set MMORF_BUILD=GPU:

    export MMORF_BUILD=GPU
    make
    

There are a couple of variables which affect compilation of the CUDA version: - By default, the CUDA Toolkit libraries are statically linked into the MMORF executable - this means that the resulting binary can be used on a system which does not have the CUDA Toolkit installed. If you wish to dynamically link against the CUDA Toolkit libraries, you can set CUDA_DYNAMIC=1, e.g.:

export MMORF_BUILD=GPU
export CUDA_DYNAMIC=1
make
The resulting binary will only be able to be run on a a system which has a compatible version of the CUDA Toolkit installed. - By default, MMORF is compiled so as to be compatible with a wide range of GPUs ("compute capabilities"). While this is usually preferable, it requires substantially longer compilation time. When compiling MMORF to run locally, you may wish to limit the compute capabilities that MMORF is compiled for - you can do this with the GENCODEFLAGS variable, e.g.:
export MMORF_BUILD=GPU
export GENCODEFLAGS="-gencode arch=compute_72,code=sm_72"
make

  1. All build artifacts are stored in the build/ sub-directory - the CPU executable will be saved to build/CPU/mmorf_cpu, and the CUDA executable to build/GPU/mmorf_cudaX.Y (where X.Y is the CUDA Toolkit version, e.g. mmorf_cuda11.0).

Running MMORF

If installed correctly, running MMORF is as simple as:

mmorf --option_1 <argument_1> --option_2 <argument_2.1> <argument_2.2>

etc.

If you are making use of a config file called my_config.ini, then your call may look something like:

mmorf --config my_config.ini

If you have both the CPU and CUDA versions of MMORF installed, there are three commands available:

  • mmorf is a script whcih will run the CUDA version if a GPU is available, and will otherwise fall back to the CPU version.
  • mmorf_cpu is the CPU executable
  • mmorf_cuda11.0 is the GPU executable

Running tests

The test/ directory contains a handful of unit tests - to compile and run them:

export MMORF_BUILD=CPU  # or GPU
make tests
./test/run_tests_CPU    # or GPU

There is also a test/run script which will compile and run the tests for you:

MMORF_BUILD=CPU ./test/run

Code coverage reporting can be enabled by setting TEST_COVERAGE=1, e.g.:

MMORF_BUILD=GPU TEST_COVERAGE=1 ./test/run

The coverage report will be saved to coverage/index.html.

"Gotchas"

There are a few aspects of using MMORF which might be slightly unintuitive, and I will attempt to highlight them specifically here. Some of these aspects are due to moving from single to multiple modalities, and are therefore unavoidable design decisions. Others are due to MMORF still being a beta release, and are therefore idiosyncrasies which I will endeavour to remove in future versions.

Unlikely to Change

  1. An affine matrix must be supplied for every image, for both the reference and the moving subject. This is because the reference image and warp space are not necessarily the same. This is a consequence of the fact that different modality images from the same subject might not be in the same space. If the reference image is in the same space as the warp, then an identity matrix must be used as the input parameter.

Likely to Change in the Future

For a given configuration to be valid, a number of parameters must have the correct dimensionality (e.g., if you want to run 6 iterations then you need 6 smoothing values per volume). MMORF assumes that the warp_scaling parameter is always correct, and therefore if other parameters have a different length, then MMORF will not run. What this means for the user is that some parameters become compulsory even if they will have no effect on the registration. I intend to amend this behaviour in the future, but for now the following rules apply:

  1. Every lambda_bias_reg argument length must match warp_scaling argument length even if estimate_bias is set to 0.
  2. Every use_mask_ref_scalar and use_mask_mov_scalar argument length must match warp_scaling argument length even if mask_ref_scalar or mask_mov_scalar is set to NULL.
  3. Every use_mask_ref_tensor and use_mask_mov_tensor argument length must match warp_scaling argument length even if mask_ref_tensor or mask_ref_scalar is set to NULL.