diff --git a/Common/GTesting/elxTransformIOGTest.cxx b/Common/GTesting/elxTransformIOGTest.cxx index f53495d44..212857808 100644 --- a/Common/GTesting/elxTransformIOGTest.cxx +++ b/Common/GTesting/elxTransformIOGTest.cxx @@ -46,6 +46,8 @@ #include "TranslationTransform/elxTranslationTransform.h" #include "WeightedCombinationTransform/elxWeightedCombinationTransform.h" +#include "itkAdvancedLimitedEuler3DTransform.h" + #include #include #include @@ -972,3 +974,39 @@ GTEST_TEST(Transform, TransformedPointSameAsITKBSpline3D) Expect_elx_TransformPoint_yields_same_point_as_ITK(itkTransform); } + + +// Tests that the limited Euler transform yields the same point-to-point transformation as the unlimited Euler +// transform, when there is no limit specified. +GTEST_TEST(Transform, TransformedPointLimitedEulerSameAsUnlimitedEuler3D) +{ + std::mt19937 randomNumberEngine; + + // Generated another pseudo-random floating point number with each call. + const auto getRandomNumber = [&randomNumberEngine] { + const std::uniform_real_distribution<> distribution{ -1.0, 1.0 }; + return distribution(randomNumberEngine); + }; + + for (const bool computeZYX : { false, true }) + { + const auto unlimitedTransform = CheckNew>(); + unlimitedTransform->SetCenter(MakePoint(getRandomNumber(), getRandomNumber(), getRandomNumber())); + unlimitedTransform->SetComputeZYX(computeZYX); + unlimitedTransform->SetTranslation(MakeVector(getRandomNumber(), getRandomNumber(), getRandomNumber())); + unlimitedTransform->SetRotation(getRandomNumber(), getRandomNumber(), getRandomNumber()); + + const auto limitedTransform = CheckNew>(); + + // Copy all the relevant properties from the "unlimited" to the "limited" Euler transform. + limitedTransform->SetCenter(unlimitedTransform->GetCenter()); + limitedTransform->SetComputeZYX(unlimitedTransform->GetComputeZYX()); + limitedTransform->SetTranslation(unlimitedTransform->GetTranslation()); + limitedTransform->SetRotation( + unlimitedTransform->GetAngleX(), unlimitedTransform->GetAngleY(), unlimitedTransform->GetAngleZ()); + + const auto inputPoint = MakePoint(getRandomNumber(), getRandomNumber(), getRandomNumber()); + + EXPECT_EQ(limitedTransform->TransformPoint(inputPoint), unlimitedTransform->TransformPoint(inputPoint)); + } +} diff --git a/Core/Main/GTesting/itkTransformixFilterGTest.cxx b/Core/Main/GTesting/itkTransformixFilterGTest.cxx index 91cb520fb..af8adbd49 100644 --- a/Core/Main/GTesting/itkTransformixFilterGTest.cxx +++ b/Core/Main/GTesting/itkTransformixFilterGTest.cxx @@ -26,6 +26,9 @@ #include "elxDefaultConstructibleSubclass.h" #include "GTesting/elxGTestUtilities.h" +#include "itkAdvancedEuler3DTransform.h" +#include "itkAdvancedLimitedEuler3DTransform.h" + // ITK header files: #include #include @@ -689,3 +692,72 @@ GTEST_TEST(itkTransformixFilter, CombineTranslationAndScale) EXPECT_EQ(DerefSmartPointer(transformixOutput), *(CreateResampleImageFilter(*inputImage, scaleAndTranslationTransform)->GetOutput())); } + + +// Tests that the limited Euler transform yields the same image-to-image transformation as the unlimited Euler +// transform, when there is no limit specified. +GTEST_TEST(itkTransformixFilter, TransformedImageLimitedEulerSameAsUnlimitedEuler3D) +{ + enum + { + ImageDimension = 3 + }; + using TPixel = float; + using ImageType = itk::Image; + + const auto image = CreateImageFilledWithSequenceOfNaturalNumbers({ 7, 8, 9 }); + + const auto transformImage = [&image](const char * const transformName, + const auto & transform) -> itk::SmartPointer { + const auto filter = CheckNew>>(); + + const auto & transformParameters = transform.GetParameters(); + + filter->SetMovingImage(image); + + filter->SetTransformParameterObject( + CreateParameterObject({ // Parameters in alphabetic order: + { "CenterOfRotationPoint", elx::Conversion::ToVectorOfStrings(transform.GetCenter()) }, + { "Direction", CreateDefaultDirectionParameterValues() }, + { "Index", ParameterValuesType(ImageDimension, "0") }, + { "NumberOfParameters", { std::to_string(transformParameters.size()) } }, + { "Origin", ParameterValuesType(ImageDimension, "0") }, + { "ResampleInterpolator", { "FinalLinearInterpolator" } }, + { "Size", ConvertToParameterValues(image->GetRequestedRegion().GetSize()) }, + { "Transform", ParameterValuesType{ transformName } }, + { "TransformParameters", elx::Conversion::ToVectorOfStrings(transformParameters) }, + { "Spacing", ParameterValuesType(ImageDimension, "1") } })); + filter->Update(); + return filter->GetOutput(); + }; + + std::mt19937 randomNumberEngine; + + // Generated another pseudo-random floating point number with each call. + const auto getRandomNumber = [&randomNumberEngine] { + const std::uniform_real_distribution<> distribution{ -1.0, 1.0 }; + return distribution(randomNumberEngine); + }; + + for (const bool computeZYX : { false, true }) + { + const auto unlimitedTransform = CheckNew>(); + unlimitedTransform->SetCenter(MakePoint(getRandomNumber(), getRandomNumber(), getRandomNumber())); + unlimitedTransform->SetComputeZYX(computeZYX); + unlimitedTransform->SetTranslation(MakeVector(getRandomNumber(), getRandomNumber(), getRandomNumber())); + unlimitedTransform->SetRotation(getRandomNumber(), getRandomNumber(), getRandomNumber()); + + const auto limitedTransform = CheckNew>(); + + // Copy all the relevant properties from the "unlimited" to the "limited" Euler transform. + limitedTransform->SetCenter(unlimitedTransform->GetCenter()); + limitedTransform->SetComputeZYX(unlimitedTransform->GetComputeZYX()); + limitedTransform->SetTranslation(unlimitedTransform->GetTranslation()); + limitedTransform->SetRotation( + unlimitedTransform->GetAngleX(), unlimitedTransform->GetAngleY(), unlimitedTransform->GetAngleZ()); + + const auto unlimitedTransformedImage = transformImage("EulerTransform", *unlimitedTransform); + const auto limitedTransformedImage = transformImage("LimitedEulerTransform", *limitedTransform); + EXPECT_EQ(DerefSmartPointer(limitedTransformedImage), DerefSmartPointer(unlimitedTransformedImage)); + } +}