Skip to content

Commit

Permalink
ENH: Add SetExternalTransform to TransformixFilter
Browse files Browse the repository at this point in the history
Including GoogleTest unit test.
  • Loading branch information
N-Dekker committed Aug 28, 2023
1 parent 7e883bf commit 9d9ec49
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 4 deletions.
51 changes: 51 additions & 0 deletions Core/Main/GTesting/itkTransformixFilterGTest.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <itkAffineTransform.h>
#include <itkBSplineTransform.h>
#include <itkCompositeTransform.h>
#include <itkDisplacementFieldTransform.h>
#include <itkEuler2DTransform.h>
#include <itkEuler3DTransform.h>
#include <itkFileTools.h>
Expand Down Expand Up @@ -1526,3 +1527,53 @@ GTEST_TEST(itkTransformixFilter, ExternalTransform)

EXPECT_EQ(DerefRawPointer(transformixFilter.GetOutput()), DerefRawPointer(resampleImageFilter->GetOutput()));
}

GTEST_TEST(itkTransformixFilter, SetExternalTransform)
{
enum
{
ImageDimension = 2
};
using PixelType = float;
using SizeType = itk::Size<ImageDimension>;
const SizeType imageSize{ { 5, 6 } };

using ImageType = itk::Image<PixelType, ImageDimension>;
using TransformixFilterType = itk::TransformixFilter<ImageType>;

const ImageDomain<ImageDimension> imageDomain(imageSize);
const auto movingImage = CreateImageFilledWithSequenceOfNaturalNumbers<PixelType>(imageDomain);

const auto displacementField = itk::Image<itk::Vector<double, ImageDimension>, ImageDimension>::New();

displacementField->SetRegions(imageSize);
displacementField->Allocate(true);

std::mt19937 randomNumberEngine{};

// Generate a rather arbitrary displacement field.
const itk::ImageBufferRange displacementFieldImageBufferRange{ *displacementField };
std::generate_n(
displacementFieldImageBufferRange.begin(), displacementFieldImageBufferRange.size(), [&randomNumberEngine] {
itk::Vector<double, ImageDimension> displacementVector{};

std::generate_n(displacementVector.begin(), ImageDimension, [&randomNumberEngine] {
return std::uniform_int_distribution<>{ -1, 1 }(randomNumberEngine);
});
return displacementVector;
});

elx::DefaultConstruct<itk::DisplacementFieldTransform<double, ImageDimension>> itkTransform{};
itkTransform.SetDisplacementField(displacementField);

elx::DefaultConstruct<TransformixFilterType> transformixFilter{};
transformixFilter.SetMovingImage(movingImage);
transformixFilter.SetExternalTransform(&itkTransform);
transformixFilter.SetTransformParameterObject(CreateParameterObject(
MakeMergedMap({ { "ResampleInterpolator", { "FinalLinearInterpolator" } } }, imageDomain.AsParameterMap())));
transformixFilter.Update();

const auto resampleImageFilter = CreateResampleImageFilter(*movingImage, itkTransform);

EXPECT_EQ(DerefRawPointer(transformixFilter.GetOutput()), DerefRawPointer(resampleImageFilter->GetOutput()));
}
18 changes: 14 additions & 4 deletions Core/Main/itkTransformixFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,19 @@ class ITK_TEMPLATE_EXPORT TransformixFilter : public ImageSource<TImage>
return m_OutputMesh;
}

/** 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. */
itkSetConstObjectMacro(Transform, TransformBase);
/** 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. Will override a possible previous call to
* SetExternalTransform. */
void
SetTransform(const TransformBase *);

/** Set the transformation by means of an external ITK Transform. 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. Will
* override a possible previous call to SetTransform. */
void
SetExternalTransform(TransformType *);

itkSetObjectMacro(CombinationTransform, TransformType);

Expand Down Expand Up @@ -302,6 +311,7 @@ class ITK_TEMPLATE_EXPORT TransformixFilter : public ImageSource<TImage>
SmartPointer<MeshType> m_OutputMesh{};

SmartPointer<const TransformBase> m_Transform{};
SmartPointer<TransformType> m_ExternalTransform{};

SmartPointer<TransformType> m_CombinationTransform;
};
Expand Down
56 changes: 56 additions & 0 deletions Core/Main/itkTransformixFilter.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,18 @@ TransformixFilter<TImage>::GenerateData()
}
}

if (m_ExternalTransform)
{
// External transforms should use "ResampleInterpolator" and the output image domain specification
// (Size/Spacing/Origin/Index/Direction) of the last transform parameter map.
auto transformParameterMap = std::move(transformParameterMapVector.back());
SetParameterValueAndWarnOnOverride(transformParameterMap, "Transform", "ExternalTransform");
SetParameterValueAndWarnOnOverride(
transformParameterMap, "TransformAddress", elx::Conversion::ObjectPtrToString(m_ExternalTransform));
transformParameterMapVector.clear();
transformParameterMapVector.push_back(std::move(transformParameterMap));
}

const auto movingImageDimensionString = std::to_string(MovingImageDimension);
const auto movingImagePixelTypeString = elx::PixelTypeToString<typename TImage::PixelType>();

Expand Down Expand Up @@ -605,6 +617,50 @@ TransformixFilter<TImage>::RemoveLogFileName()
}


template <typename TImage>
void
TransformixFilter<TImage>::SetTransform(const TransformBase * const transform)
{
if (transform)
{
if (m_Transform != transform)
{
m_ExternalTransform = nullptr;
m_Transform = transform;
this->Modified();
}
}
else
{
m_ExternalTransform = nullptr;
m_Transform = nullptr;
this->Modified();
}
}


template <typename TImage>
void
TransformixFilter<TImage>::SetExternalTransform(TransformType * const transform)
{
if (transform)
{
if (m_ExternalTransform != transform)
{
m_Transform = nullptr;
m_ExternalTransform = transform;
this->Modified();
}
}
else
{
m_ExternalTransform = nullptr;
m_Transform = nullptr;
this->Modified();
}
}


template <typename TImage>
auto
TransformixFilter<TImage>::GetFirstElastixTransformBase() const -> const ElastixTransformBaseType *
Expand Down

0 comments on commit 9d9ec49

Please sign in to comment.