All–
Say I have used elastix to register a moving image moving.nii.gz to a fixed image fixed.nii.gz, resulting in a transformation file params.txt. I would now like to apply the transformation in params.txt to a mesh input-mesh.obj describing the geometry of an object in the moving image. I have tried to adapt the example here [1], which seems to closely match my use case, but unfortunately TransformixFilter.GetOutputMesh() is returning None. I think the main difference between the example linked above and my implementation is that the example calls both SetTransformParameterObject and SetTransform, whereas mine only calls SetTransformParameterObject; however, this is intentional, and per my understanding of the documentation should work. Quoting the documentation for SetTransform() [2]:
Sets the transformation. If null, the transformation is entirely specified by the transform parameter object that is set by SetTransformParameterObject. Otherwise, the transformation is specified by this transform object, with additional information from the specified transform parameter object.
I’m attaching an example parameter file and mesh, as well as a minimal example to reproduce the issue. itk version 5.4.3. itk-elastix version 0.23.0. Would be extremely appreciative if anyone can spot my error!
Best, and thanks in advance,
–Davis
input-mesh.obj (59.6 KB)
params.txt (1.5 MB)
#!/usr/bin/env python3
import itk
import pathlib as pl
def transform_mesh_with_itk(
input_mesh_path: pl.Path,
transform_parameter_paths: list[pl.Path],
output_mesh_path: pl.Path,
) -> None:
if not input_mesh_path.exists():
raise FileNotFoundError(f"Input mesh file not found: {input_mesh_path}")
for param_file in transform_parameter_paths:
if not param_file.exists():
raise FileNotFoundError(f"Transform parameter file not found: {param_file}")
PixelType = itk.D
Dimension = 3
try:
original_mesh = itk.meshread(input_mesh_path, pixel_type=PixelType)
num_points = original_mesh.GetNumberOfPoints()
except Exception as e:
raise RuntimeError(f"Failed to load mesh file: {e}")
parameter_object = itk.ParameterObject.New()
for param_file in transform_parameter_paths:
parameter_object.AddParameterFile(str(param_file))
try:
# Create a dummy image for TransformixFilter (required by ITKElastix)
ImageType = itk.Image[PixelType, Dimension]
dummy_image = ImageType.New()
dummy_image.SetRegions([1,1,1])
dummy_image.Allocate(True)
transformix_filter = itk.TransformixFilter[ImageType].New()
transformix_filter.SetMovingImage(dummy_image)
transformix_filter.SetInputMesh(original_mesh)
transformix_filter.SetTransformParameterObject(parameter_object)
transformix_filter.Update()
transformed_mesh = transformix_filter.GetOutputMesh()
if transformed_mesh is None:
raise RuntimeError("TransformixFilter.GetOutputMesh() returned None.")
print(f"Transformed mesh with {transformed_mesh.GetNumberOfPoints()} points")
except Exception as e:
raise RuntimeError(f"An error occurred during transformation: {e}")
try:
output_mesh_path.parent.mkdir(parents=True, exist_ok=True)
itk.meshwrite(transformed_mesh, str(output_mesh_path))
except Exception as e:
raise RuntimeError(f"Failed to write output mesh file: {e}")
def main():
try:
transform_mesh_with_itk(
input_mesh_path=pl.Path("./test/input-mesh.obj"),
transform_parameter_paths=[pl.Path("./test/params.txt")],
output_mesh_path=pl.Path("./test/output-mesh.obj"),
)
except (FileNotFoundError, RuntimeError) as e:
print(f"\nERROR: {e}", file=sys.stderr)
exit(1)
if __name__ == "__main__":
import argparse
import sys
main()
[1] ITKElastix/examples/ITK_Example16_Transformix_mesh_TranslationTransform.ipynb at main · InsightSoftwareConsortium/ITKElastix · GitHub
[2] elastix: itk::TransformixFilter< TImage > Class Template Reference