register_volume3d: Native 3D volume registration ("rigid", "affine", or "SyN")

View source: R/imaging-registration-native.R

register_volume3dR Documentation

Native 3D volume registration ('rigid', 'affine', or 'SyN')

Description

Self-contained image registration for 3D volumes, implemented purely in RcppEigen (no other external registration library). It mirrors the core behavior of 'ANTs' antsRegistration: a multi-resolution, physical-shift scaled gradient-descent optimizer driving a similarity metric, working entirely in the anatomical ⁠RAS⁠ (right-anterior-superior) space. Each volume carries its own vox2ras (0-indexed voxel index to anatomical ⁠RAS⁠) 4\times 4 transform, so volumes with different sampling, orientation, or field of view are aligned correctly.

Usage

register_volume3d(
  source,
  target,
  source_vox2ras = NULL,
  target_vox2ras = NULL,
  source_mask = NULL,
  target_mask = NULL,
  source_points = NULL,
  target_points = NULL,
  points_weight = 0.5,
  weights = NULL,
  type = c("rigid", "affine", "syn", "syn_only"),
  metric = "mattes",
  shrink_factors = c(4, 2, 1),
  smoothing_sigmas = c(2, 1, 0),
  iterations = c(1000, 500, 250),
  sampling_rate = 0.2,
  interpolation = "trilinear",
  number_of_bins = 32L,
  seed = 1L,
  init_transform = NULL,
  syn_iterations = c(40, 20, 0),
  syn_sigma = 3,
  verbose = TRUE
)

Arguments

source

the moving volume to be aligned, a 3D array (for example a 'CT'); the result transform maps target into this image's space. May also be a list of already co-registered 3D arrays (multiple modalities such as 'T1', 'T2', an atlas constraint); all source channels must share the same dimensions and vox2ras

target

the fixed/reference volume to align to, a 3D array (for example a 'MRI'), or a list of co-registered arrays matching source channel-for-channel

source_vox2ras, target_vox2ras

4x4 (or 3x4) matrices mapping the 0-indexed voxel coordinate (column-row-slice, 'C'-style starting from 0, complying with 'NIfTI') to the ⁠RAS⁠ coordinate system; if NULL, the function looks for a "vox2ras" attribute on the array

source_mask, target_mask

optional 3D mask arrays restricting where the metric is evaluated; default NULL (no mask, evaluate everywhere). A target_mask (on the target/fixed grid, same dimensions as target) limits which target voxels drive the registration; a source_mask (on the source/moving grid) drops samples that map outside it. Non-zero voxels are included. Besides focusing the alignment on a region of interest, masks speed things up by skipping background: the linear stage samples only inside the mask, and the deformable stage skips warping voxels outside the (dilated) mask. One mask per grid, shared across channels

source_points, target_points

optional N x 3 matrices of corresponding landmark coordinates that add a surface/landmark term to the deformable ("syn"/"syn_only") stage; default NULL (no term). Row i of target_points (in the target/fixed ⁠RAS⁠) and row i of source_points (in the source/moving ⁠RAS⁠) must be the same anatomical location, for example corresponding cortical-surface vertices from a 'FreeSurfer' spherical registration. The term pulls the warp of each target point onto its source correspondent, recovering cortical folding (⁠gyrification⁠) that the intensity metric blurs over, while the image metric still drives deep-brain ⁠subcortical⁠ structures. Must be supplied together with equal row counts. Points must be in the same ⁠RAS⁠ frame as source_vox2ras/target_vox2ras (note 'FreeSurfer' surfaces use ⁠surface/tkr⁠ ⁠RAS⁠, which differs from scanner ⁠RAS⁠ by c_ras)

points_weight

relative weight of the landmark term against the image metric in the deformable stage; default 0.5. Larger values follow the landmarks more closely. The sparse landmark force is attenuated by syn_sigma smoothing, so this typically needs tuning for a given point count and spacing

weights

optional numeric weights, one per source/target pair, controlling each channel's contribution to the deformable cost; default is equal weighting. Weights are normalized internally to sum to 1. Only the deformable ("syn"/"syn_only") stage is multivariate; the linear stage always uses the first (primary) pair

type

type of transform to estimate; one of 'rigid' (6 degrees of freedom), 'affine' (12), "syn" (⁠affine⁠ followed by a ⁠SDR⁠ - symmetric ⁠diffeomorphic⁠ deformation), or 'syn_only' (⁠deformable⁠ stage only — no ⁠affine⁠ is estimated; init_transform is used directly as the starting ⁠affine⁠, useful when you already have a good linear alignment)

metric

similarity metric: "mattes" (Mattes mutual information, the default, best for cross-modal such as 'CT'-'MRI'), "cc" (normalized cross-correlation, for same-modality), or "meansquares" (mean squared intensity difference). With multiple channels, supply either a single metric (used for every pair) or a vector with one metric per source/target pair

shrink_factors

integer down-sampling factors, one per resolution level (coarsest first); default c(4, 2, 1)

smoothing_sigmas

Gaussian smoothing applied at each level, in voxels, same length as shrink_factors; default c(2, 1, 0)

iterations

maximum optimizer iterations per level; default c(1000, 500, 250)

sampling_rate

fraction of fixed voxels sampled to evaluate the metric (speeds up large volumes); default 0.2

interpolation

output interpolation used when warping each modality onto the target grid: 'trilinear' (default), 'nearest' (keeps label or segmentation values intact), or "bspline" (cubic ⁠Catmull-Rom⁠, higher quality, slower). Like metric, supply a single value (applied to every channel) or one per source/target pair. This affects only the returned warped images, never the optimization's internal sampling (always 'trilinear', so every channel still produces smooth gradients)

number_of_bins

number of histogram bins for the "mattes" metric; default 32

seed

random seed for the voxel sampler, for reproducibility

init_transform

optional 4x4 initial ⁠RAS⁠-to-⁠RAS⁠ transform (fixed to moving) to start from

syn_iterations, syn_sigma

deformable stage controls (only used when type = "syn" or "syn_only"): per-level iteration counts and the Gaussian regularization sigma (in voxels) applied to the update field. Both are recycled to the number of levels, so syn_sigma may be a vector to vary the regularization per stage (e.g. c(3, 3, 1) to relax it at the finest level for sharper detail)

verbose

logical; if TRUE (default) print per-level and per-iteration progress to the console, including the current stage, shrink factor, smoothing sigma, cost metric, and step size (linear) or maximum displacement (deformable); useful to monitor convergence on large volumes

Value

A list with:

transform

the estimated 4x4 ⁠RAS⁠-to-⁠RAS⁠ linear transform mapping target (fixed) coordinates to source (moving) coordinates

image

the (primary) source resampled onto the target grid

images

a list with every source channel resampled onto the target grid using its own interpolation; image is the first element. For single-image input this is a length-1 list

forward_field,inverse_field

(only for "syn") the deformation fields

metric_trace

the metric value across optimizer iterations

type,metric

echoes of the inputs

See Also

apply_transform3d, resample_3d_volume

Examples



# --- synthetic same-modality example -------------------------------------
nd <- c(50, 50, 50)
vox2ras <- diag(4); vox2ras[1:3, 4] <- -25
blob <- function(cx, cy, cz, s = 6) {
  g <- expand.grid(x = 0:(nd[1]-1), y = 0:(nd[2]-1), z = 0:(nd[3]-1))
  array(exp(-((g$x-cx)^2 + (g$y-cy)^2 + (g$z-cz)^2) / (2*s^2)), nd)
}
target <- blob(25, 25, 25)
source <- blob(28, 23, 26)            # shifted by a known (3, -2, 1) mm

res <- register_volume3d(
  source, target,
  source_vox2ras = vox2ras, target_vox2ras = vox2ras,
  type = "rigid", metric = "cc"
)
res$transform[1:3, 4]                 # ~ c(3, -2, 1)


# --- multimodal registration (several co-registered channels) ------------
# Two aligned modalities (e.g. a T1 and a T2) jointly drive the deformable
# stage. Each pair may use its own metric, and `weights` set their relative
# influence (normalized internally to sum to 1). All source channels must
# share a grid; likewise all target channels.
t1_target <- blob(25, 25, 25, s = 6)
t2_target <- blob(25, 25, 25, s = 9)  # same anatomy, different contrast
t1_source <- blob(27, 24, 26, s = 6)  # both channels share the same warp
t2_source <- blob(27, 24, 26, s = 9)

res_mm <- register_volume3d(
  source = list(t1_source, t2_source),
  target = list(t1_target, t2_target),
  source_vox2ras = vox2ras, target_vox2ras = vox2ras,
  weights = c(2, 1),                  # T1 counts twice as much as T2
  metric = c("mattes", "cc"),         # one metric per channel
  type = "syn"
)
res_mm$transform[1:3, 4]



ravetools documentation built on Aug. 21, 2026, 5:06 p.m.