From 90c64c53d3ea5e3604ca46aa65d22bf311437991 Mon Sep 17 00:00:00 2001 From: Niels Dekker Date: Tue, 15 Oct 2024 16:53:12 +0200 Subject: [PATCH 1/2] ENH: Transform binary VTK input file to binary output file If the VTK input file is a binary file, the output file should also be a binary file. Otherwise, the output file is still ASCII, as before. Inspired by pull request https://github.com/SuperElastix/elastix/pull/943 "ENH: Support for binary point file (.bin) reading and writing", ChristophKirst, Aug 2, 2023. --- .../ComponentBaseClasses/elxTransformBase.hxx | 11 ++++- Testing/PythonTests/transformix_test.py | 44 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/Core/ComponentBaseClasses/elxTransformBase.hxx b/Core/ComponentBaseClasses/elxTransformBase.hxx index 1e2552095..d32aac7f6 100644 --- a/Core/ComponentBaseClasses/elxTransformBase.hxx +++ b/Core/ComponentBaseClasses/elxTransformBase.hxx @@ -967,7 +967,16 @@ TransformBase::TransformPointsSomePointsVTK(const std::string & filena try { - itk::WriteMesh(outputMesh, outputPointsFileName); + const auto writer = itk::MeshFileWriter::New(); + writer->SetInput(outputMesh); + writer->SetFileName(outputPointsFileName); + + if (itk::Deref(meshReader->GetModifiableMeshIO()).GetFileType() == itk::IOFileEnum::Binary) + { + writer->SetFileTypeAsBINARY(); + } + + writer->Update(); } catch (const itk::ExceptionObject & err) { diff --git a/Testing/PythonTests/transformix_test.py b/Testing/PythonTests/transformix_test.py index f78d9e366..ad4d069f5 100644 --- a/Testing/PythonTests/transformix_test.py +++ b/Testing/PythonTests/transformix_test.py @@ -541,6 +541,50 @@ def test_zero_translation_of_vtk_2d_points(self) -> None: output_mesh = reader.GetOutput() self.assert_equal_mesh(output_mesh, input_mesh) + def test_zero_translation_of_vtk_2d_points_binary(self) -> None: + """Tests zero-translation of VTK points in 2D""" + + source_directory_path = pathlib.Path(__file__).resolve().parent + output_directory_path = self.create_test_function_output_directory() + + parameter_directory_path = source_directory_path / "TransformParameters" + + input_mesh = itk.Mesh[itk.D, 2].New() + for i in range(4): + input_mesh.SetPoint( + i, (self.random_finite_float32(), self.random_finite_float32()) + ) + + writer = itk.MeshFileWriter[itk.Mesh[itk.D, 2]].New() + writer.SetInput(input_mesh) + writer.SetFileTypeAsBINARY() + writer.SetFileName(str(output_directory_path / "inputpoints.vtk")) + writer.Write() + + subprocess.run( + [ + str(self.transformix_exe_file_path), + "-def", + str(output_directory_path / "inputpoints.vtk"), + "-tp", + str(parameter_directory_path / "Translation(0,0).txt"), + "-out", + str(output_directory_path), + ], + capture_output=True, + check=True, + ) + + # Note that itk.meshread does not work, as the following produces a 3D mesh, instead of a 2D + # mesh. + # + # output_mesh = itk.meshread(str(output_directory_path / "outputpoints.vtk")) + reader = itk.MeshFileReader[itk.Mesh[itk.D, 2]].New() + reader.SetFileName(str(output_directory_path / "outputpoints.vtk")) + reader.Update() + output_mesh = reader.GetOutput() + self.assert_equal_mesh(output_mesh, input_mesh) + def test_translation_deformation_field(self) -> None: """Tests zero-translation of VTK points in 2D""" From 15bcdc3660d2247792f5d59f666fe80f7536e3a1 Mon Sep 17 00:00:00 2001 From: Niels Dekker Date: Wed, 16 Oct 2024 18:03:05 +0200 Subject: [PATCH 2/2] ENH: Transform float VTK input file to float output file If the VTK input file has 32-bit `float` as data type, the output file should also have 32-bit `float` data. Otherwise, the output file still has 64-bit `double` data, as before. --- .../ComponentBaseClasses/elxTransformBase.hxx | 129 +++++++++++------- 1 file changed, 76 insertions(+), 53 deletions(-) diff --git a/Core/ComponentBaseClasses/elxTransformBase.hxx b/Core/ComponentBaseClasses/elxTransformBase.hxx index d32aac7f6..57ede6e91 100644 --- a/Core/ComponentBaseClasses/elxTransformBase.hxx +++ b/Core/ComponentBaseClasses/elxTransformBase.hxx @@ -916,72 +916,95 @@ template void TransformBase::TransformPointsSomePointsVTK(const std::string & filename) const { - /** Typedef's. \todo test DummyIPPPixelType=bool. */ - using DummyIPPPixelType = float; - using MeshTraitsType = - itk::DefaultStaticMeshTraits; - using MeshType = itk::Mesh; + const itk::CommonEnums::IOComponent pointComponentType = [&filename] { + const itk::SmartPointer meshIO = + itk::MeshIOFactory::CreateMeshIO(filename.c_str(), itk::IOFileModeEnum::ReadMode); + meshIO->SetFileName(filename); + meshIO->ReadMeshInformation(); + return meshIO->GetPointComponentType(); + }(); + + // Reads a mesh from a VTK file, transforms the mesh, and writes the transformed mesh. + const auto ReadAndTransformAndWriteMesh = [&filename, this](auto coordinate) { + // The `coordinate` parameter just specifies the requested coordinate type. + (void)coordinate; + + using DummyIPPPixelType = unsigned char; + using MeshType = itk::Mesh< + DummyIPPPixelType, + FixedImageDimension, + itk::DefaultStaticMeshTraits>; + + /** Read the input points. */ + const auto meshReader = itk::MeshFileReader::New(); + meshReader->SetFileName(filename); + log::info(std::ostringstream{} << " Reading input point file: " << filename); + try + { + meshReader->Update(); + } + catch (const itk::ExceptionObject & err) + { + log::error(std::ostringstream{} << " Error while opening input point file.\n" << err); + } - /** Read the input points. */ - const auto meshReader = itk::MeshFileReader::New(); - meshReader->SetFileName(filename); - log::info(std::ostringstream{} << " Reading input point file: " << filename); - try - { - meshReader->Update(); - } - catch (const itk::ExceptionObject & err) - { - log::error(std::ostringstream{} << " Error while opening input point file.\n" << err); - } + const auto & inputMesh = *(meshReader->GetOutput()); - const auto & inputMesh = *(meshReader->GetOutput()); + /** Some user-feedback. */ + log::info(" Input points are specified in world coordinates."); + const unsigned long nrofpoints = inputMesh.GetNumberOfPoints(); + log::info(std::ostringstream{} << " Number of specified input points: " << nrofpoints); - /** Some user-feedback. */ - log::info(" Input points are specified in world coordinates."); - const unsigned long nrofpoints = inputMesh.GetNumberOfPoints(); - log::info(std::ostringstream{} << " Number of specified input points: " << nrofpoints); + /** Apply the transform. */ + log::info(" The input points are transformed."); - /** Apply the transform. */ - log::info(" The input points are transformed."); + typename MeshType::ConstPointer outputMesh; - typename MeshType::ConstPointer outputMesh; + try + { + outputMesh = Self::TransformMesh(inputMesh); + } + catch (const itk::ExceptionObject & err) + { + log::error(std::ostringstream{} << " Error while transforming points.\n" << err); + } - try - { - outputMesh = Self::TransformMesh(inputMesh); - } - catch (const itk::ExceptionObject & err) - { - log::error(std::ostringstream{} << " Error while transforming points.\n" << err); - } + const Configuration & configuration = itk::Deref(Superclass::GetConfiguration()); - const Configuration & configuration = itk::Deref(Superclass::GetConfiguration()); + if (const std::string outputDirectoryPath = configuration.GetCommandLineArgument("-out"); + !outputDirectoryPath.empty()) + { + /** Create filename and file stream. */ + const std::string outputPointsFileName = configuration.GetCommandLineArgument("-out") + "outputpoints.vtk"; + log::info(std::ostringstream{} << " The transformed points are saved in: " << outputPointsFileName); - if (const std::string outputDirectoryPath = configuration.GetCommandLineArgument("-out"); - !outputDirectoryPath.empty()) - { - /** Create filename and file stream. */ - const std::string outputPointsFileName = configuration.GetCommandLineArgument("-out") + "outputpoints.vtk"; - log::info(std::ostringstream{} << " The transformed points are saved in: " << outputPointsFileName); + try + { + const auto writer = itk::MeshFileWriter::New(); + writer->SetInput(outputMesh); + writer->SetFileName(outputPointsFileName); - try - { - const auto writer = itk::MeshFileWriter::New(); - writer->SetInput(outputMesh); - writer->SetFileName(outputPointsFileName); + if (itk::Deref(meshReader->GetModifiableMeshIO()).GetFileType() == itk::IOFileEnum::Binary) + { + writer->SetFileTypeAsBINARY(); + } - if (itk::Deref(meshReader->GetModifiableMeshIO()).GetFileType() == itk::IOFileEnum::Binary) + writer->Update(); + } + catch (const itk::ExceptionObject & err) { - writer->SetFileTypeAsBINARY(); + log::error(std::ostringstream{} << " Error while saving points.\n" << err); } - - writer->Update(); - } - catch (const itk::ExceptionObject & err) - { - log::error(std::ostringstream{} << " Error while saving points.\n" << err); } + }; + + if (pointComponentType == itk::CommonEnums::IOComponent::FLOAT) + { + ReadAndTransformAndWriteMesh(float()); + } + else + { + ReadAndTransformAndWriteMesh(double()); } } // end TransformPointsSomePointsVTK()