Skip to content

Applying transforms to point data

Philip Cook edited this page Mar 4, 2026 · 2 revisions

Transforming a point set

Point set transformations use the opposite transform ordering compared to resampling images.

Transform points from fixed to moving space:

antsApplyTransformsToPoints \
  -d 3 \
  -i landmarksInFixedSpace.csv \
  -o landmarksInMovingSpace.csv \
  -t movingToFixed_1Warp.nii.gz \
  -t movingToFixed_0GenericAffine.mat

The forward warps, which we use to deform an image from moving to fixed space, are the warps that transform points from fixed to moving space. To move points in the opposite direction:

antsApplyTransformsToPoints \
  -d 3 \
  -i landmarksInMovingSpace.csv \
  -o landmarksInFixedSpace.csv \
  -t [movingToFixed_0GenericAffine.mat, 1]
  -t movingToFixed_1InverseWarp.nii.gz \

The reason the transforms are reversed is that resampling an image from a moving space to a fixed space requires transforming points in the opposite direction. Internally, we begin with a regular voxel grid in the fixed space. For each point, we have to find where in the moving space it corresponds to, and compute some interpolated value. Conceptually, it's tempting to think of the moving image being "pushed", deformed into the fixed space. In reality, antsApplyTransforms is taking points in the fixed space and then figuring out where in the moving space to "pull" a value from.

With point set registrations, we aren't doing a "pull" operation, we are "pushing" points from one space to another and leaving them at their precise location in the other space. Thus we use the forward warps to move a point from fixed to moving space, and inverse warps to go the other way.

Point coordinate system

The input and output to antsApplyTransformsToPoints is coordinates in the LPS+ physical space used by ITK / DICOM. This differs from NIFTI coordinates (RAS+). ITK-SNAP, while based on ITK, also uses RAS+ coordinates for points.

The units of the coordinates are millimeters.

Input to antsApplyTransformsToPoints

Input to antsApplyTransformsToPoints is either a CSV file with physical coordinates, or a binary 2D MetaImage file containing the coordinates.

CSV format

The CSV format should have a header row, and at least as many columns as the there are dimensions to the transform. Usually, the transform will be 3D, and so the CSV file should look like this:

x,y,z
-10.0,20.0,30.0
-15.0,25.0,35.0

Additional numeric columns are permitted, but they will not be transformed. For example,

x,y,z,label
-10.0,20.0,30.0,1
-15.0,25.0,35.0,2

The points will be transformed and the "label" column will be copied to the output CSV file unaltered. Non-numeric data are not supported.

MetaImage format

The MetaImage format is a 2D binary image (.mha) where the first dimension corresponds to the number of points, and the second dimension corresponds to the number of dimensions in the transform. For example, for a 3D transform and 2 points, the image would have dimensions (2, 3). The pixel values in the image correspond to the coordinates of each point.

ANTsPy has a function ants.make_image that can be used to create a MetaImage from a list of points.

import ants
points = [[-10.0, 20.0, 30.0], [-15.0, 25.0, 35.0]]
points_image = ants.make_image(points)
ants.image_write(points_image, 'points.mha')

Extracting point coordinates from an image

If you need to generate coordinates from a landmark image, you can use LabelGeometryMeasures. This will report the centroid of all unique labels in the image.

If you have multiple points per label, you can use LabelClustersUniquely to assign a unique label to each cluster of connected voxels. Example:

LabelClustersUniquely \
    3 \
    points.nii.gz \
    points_unique.nii.gz \
    0 \
    0

Then LabelGeometryMeasures can be used to compute the centroid of each unique label, which will correspond to a single point in the CSV file.

LabelGeometryMeasures \
    3 \
    points_unique.nii.gz \
    na \
    points.csv

Translating points from index to physical space

ANTsPy provides a utility function to convert points from index space to physical space, and vice versa. To convert to physical space,

import ants
source_image = ants.image_read('source.nii.gz')
points_voxel = [[10, 20, 30], [15, 25, 35]]
points_physical = [ ants.transform_index_to_physical_point(source_image, x) for x in points_voxel ]

Clone this wiki locally