-
Notifications
You must be signed in to change notification settings - Fork 4
Hdf5 Deformation fields
See here for a description of the file name conventions for "forward" and "inverse" transformations.
Some applications apply transformations (deformation fields) on a small subset of the domain over which it is defined (e.g., transforming sparse points or skeletons). In these cases, it is unnecessary and wasteful to read the whole of the deformation field from file. This format stores deformation fields as HDF5 files or N5 datasets, both of which enable blockwise, incremental loading from disk.
We observe that transforming sparse skeletons with h5 displacement fields can be over 35x faster than using nifti format displacements fields. The slowdown of the nifti is due to the overhead of loading in extraneous parts of the displacement field.
Furthermore, it enables quantization of displacement values to reduce file size. It is also possible to store multiple displacement fields at different resolutions (downsample factors).
Store an affine transform and a deformation field as dataset in the HDF5 or N5 specification.
- The forward transforms are stored in the
dfielddataset. - The inverse transform must be stored in the
invdfielddataset (optional).
-
Dimensionality
- Currently 2D and 3D deformation fields are supported.
- 2D
- Displacement fields are stored as 3D datasets, whose dimensions are ordered
[2,X,Y]where the first dimension varies fastest in memory (i.e. F-order) contains displacement vector components.
- Displacement fields are stored as 3D datasets, whose dimensions are ordered
- 3D
- Displacement fields are stored as 4D datasets, whose dimensions are ordered
[3,X,Y,Z]where the first dimension varies fastest in memory (i.e. F-order) contains displacement vector components.
- Displacement fields are stored as 4D datasets, whose dimensions are ordered
-
Block / cell size
- Blocks must be of size
3in their last dimension (i.e., block may not split across the displacement vector dimension). Otherwise, any block size is permissible.
- Blocks must be of size
-
- Data can be stored as floating point (
H5T_NATIVE_FLOAT,H5T_NATIVE_DOUBLE) types - or as signed integer types (
H5T_NATIVE_CHAR,H5T_NATIVE_SHORT,H5T_NATIVE_INT)- If the type is an integer type, the dataset must stores a
quantization_multiplierattribute (see below) and is interpreted as quantized continuous values (see below)
- If the type is an integer type, the dataset must stores a
- Data can be stored as floating point (
Downsampled versions of deformation fields may be stored in the same container as the original resolution in the same container. In this case, transformations are stored in datasets
of the form /<level>/dfield, where level indicates the amount of downsampling, and level=0 represents the full-resolution deformation field. For example an h5 file might contain
these datasets:
/0/dfield
/0/invdfield
/1/dfield
/1/invdfield
/2/dfield
/2/invdfield
where /0/dfield is the foward deformation field at full resolution, and /2/invdfield is the inverse deformation field at the lowest resolution.
- 2D
- The affine part of a transformation is stored as a double array of length 6 in the
affineattribute. - It is interpreted as a
2 x 3matrix stored in row-major order, and is the upper2 x 3submatrix of the3 x 3matrix in homogeneous coordinates
- The affine part of a transformation is stored as a double array of length 6 in the
- 3D
- The affine part of a transformation is stored as a double array of length 12 in the
affineattribute. - It is interpreted as a
3 x 4matrix stored in row-major order, and is the upper3 x 4submatrix of the4 x 4matrix in homogeneous coordinates
- The affine part of a transformation is stored as a double array of length 12 in the
-
quantization_multiplierstores quantization multiplier as adouble(see below). - 2D
-
affinestores the affine part of the transformation as adouble[6](see above). -
spacingstores pixel spacing (resolution) as adouble[2]
-
- 3D
-
affinestores the affine part of the transformation as adouble[12](see above). -
spacingstores pixel spacing (resolution) as adouble[3]
-
If the datatype is an integer type, the stored values are interpreted as quantized continuous values, where the continuous values are obtained by multiplying with the quantization_multiplier attribute, which must be present. Specifically:
vector_displacement_i = quantization_multiplier * (double) integer_value_i
Values stored at the discrete coordinate (v,i,j) in the 3D array correspond to the physical
position:
(x, y) = ( spacing[0] * i, spacing[1] * j )
Values stored at the discrete coordinate (v,i,j,k) in the 4D array correspond to the physical
position:
-
(x, y, z) = ( spacing[0] * i, spacing[1] * j, spacing[2] * k )Values stored at the discrete coordinate(v,i,j,k)in the 4D array correspond to the physical position:
This format can store both the forward and inverse transformations in a single file, and often each direction contains both an affine and deformable component (e.g., when the transform was found by ANTs - see here for ANTs conventions. The order in which these transforms are applied is different, and so great care must be taken to ensure the transform are applied correctly.
We take great care in applying the inverse transforms correctly, especially when it is the concatenation of an affine and deformable component. The safest way to proceed is to always obtain an invertible transform as follows:
n5 = new N5HDF5Reader( hdf5Path, 3, 32, 32, 32);
interp = new NLinearInterpolatorFactory<>();
transform = N5DisplacementField.openInvertible( n5, new DoubleType(), interp );
inverseTransform = transform.inverse();CMTK-compatible affine and displacement fields from H5 (Terminal)
Use the convertH5Transform script, and give it --cmtk as the third argument.
This will produce a file ending in .xform containing the affine part, a file ending in _fwd.nrrd containing the forward deformable part, and a file ending in _inv.nrrd containing the inverse deformable part (if it is present in the h5 file).
convertH5Transform transform.h5 cmtk_transform_prefix --cmtkANTS-compatible affine and displacement fields from H5 (Terminal)
Use the convertH5Transform script. It's default behavior is to produce ANTs-compatible transforms,
but you can explicitly give it the flag --ants as the third argument.
This will produce a file ending in .mat containing the affine part, a file ending in _fwd.nii containing the forward deformable part, and a file ending in _inv.nii containing the inverse deformable part (if it is present in the h5 file).
convertH5Transform transform.h5 ants_transform_prefix
# or
# convertH5Transform transform.h5 cmtk_transform_prefix --antsANTs-compatible forward and inverse affines from H5 (Java)
N5Reader n5 = new N5HDF5Reader( hdf5Path, 32, 32, 32, 3 );
AffineTransform3D forwardAffine = new AffineTransform3D();
forwardAffine.set( n5.getAttribute( "dfield", N5DisplacementField.AFFINE_ATTR, double[].class ));
AffineTransform3D reverseAffine = new AffineTransform3D();
reverseAffine.set( n5.getAttribute( "invdfield", N5DisplacementField.AFFINE_ATTR, double[].class ));ANTs-compatible forward and inverse affines from H5 (Terminal)
convertAffine transform.h5 forward_affine_file.mat
# Anything after the colon ':' tells the script which dataset to use
# 'invdfield' contains the inverrse transform
convertAffine transform.h5:invdfield inverse_affine_file.matCMTK-compatible forward and inverse affines from H5 (Terminal)
Use the convertAffine script, and give your output file name an xform extension
convertAffine transform.h5 forward_affine_file.xform
# Anything after the colon ':' tells the script which dataset to use
# 'invdfield' contains the inverrse transform
convertAffine transform.h5:invdfield inverse_affine_file.xform