Skip to content

ENH: Add support for multiple metrics and regularization#1437

Open
Leirbag-gabrieL wants to merge 1 commit into
SuperElastix:mainfrom
Leirbag-gabrieL:main
Open

ENH: Add support for multiple metrics and regularization#1437
Leirbag-gabrieL wants to merge 1 commit into
SuperElastix:mainfrom
Leirbag-gabrieL:main

Conversation

@Leirbag-gabrieL
Copy link
Copy Markdown

Hi, apparently it was not possible to do a multi-image registration with a transform metric like this:

(Metric "AdvancedMattesMutualInformation" "AdvancedMattesMutualInformation" "AdvancedMattesMutualInformation" "AdvancedMattesMutualInformation" "TransformBendingEnergyPenalty")
(Metric0Weight 1.0)
(Metric1Weight 1.0)
(Metric2Weight 1.0)
(Metric3Weight 1.0)
(Metric4Weight 10.0)

(NumberOfHistogramBins0 32)
(NumberOfHistogramBins1 32)
(NumberOfHistogramBins2 32)
(NumberOfHistogramBins3 32)

Now it is possible :)

@N-Dekker
Copy link
Copy Markdown
Member

Thank you very much @Leirbag-gabrieL

Can you possibly make a minimal example of a registration that is made possible by your proposed fix? Ideally in the form of a library unit test, like this one:

GTEST_TEST(itkElastixRegistrationMethod, EuclideanDistancePointMetric)
{
static constexpr auto ImageDimension = 2U;
using PixelType = float;
using ImageType = itk::Image<PixelType, ImageDimension>;
using SizeType = itk::Size<ImageDimension>;
using OffsetType = itk::Offset<ImageDimension>;
const OffsetType translationOffset{ { 1, -2 } };
const SizeType imageSize{ { 5, 6 } };
const auto fixedImage = CreateImage<PixelType>(imageSize);
const auto movingImage = CreateImage<PixelType>(imageSize);
elx::DefaultConstruct<ElastixRegistrationMethodType<ImageType>> registration{};
using PointType = itk::Point<float, ImageDimension>;
const PointType fixedPoint{};
PointType movingPoint = fixedPoint;
for (unsigned int i{}; i < ImageDimension; ++i)
{
movingPoint[i] += translationOffset[i];
}
registration.SetFixedImage(fixedImage);
registration.SetMovingImage(movingImage);
registration.SetFixedPoints(::MakeVectorContainer(std::vector<PointType>{ fixedPoint }));
registration.SetMovingPoints(::MakeVectorContainer(std::vector<PointType>{ movingPoint }));
registration.SetParameterObject(CreateParameterObject(
ParameterMapType{ // Parameters in alphabetic order:
{ "ImageSampler", { "Full" } },
{ "MaximumNumberOfIterations", { "2" } },
{ "Metric", { "AdvancedNormalizedCorrelation", "CorrespondingPointsEuclideanDistanceMetric" } },
{ "Optimizer", { "AdaptiveStochasticGradientDescent" } },
{ "Registration", { "MultiMetricMultiResolutionRegistration" } },
{ "Transform", { "TranslationTransform" } } }));
registration.Update();
const auto transformParameters = GetTransformParametersFromFilter(registration);
EXPECT_EQ(ConvertToOffset<ImageDimension>(transformParameters), translationOffset);
}

But then, having something like { "Metric", { "AdvancedMattesMutualInformation", "TransformBendingEnergyPenalty" } } instead.

Otherwise, a complete ("dummy") test set, including elastix parameter file, would also be of help to us. Again, preferably "minimal", so that it won't take much time to process.

using MovingImageDerivativeScalesType = FixedArray<double, Self::MovingImageDimension>;

/** Typedef for transform penalty metrics. */
typedef TransformPenaltyTerm<TFixedImage> TransformMetricType;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nowadays, we are using using, instead of typedef. But a coding style issue like that is not a show-stopper. I can adjust it afterwards. Also, I think I would move the definition of TransformMetricType into the function body of CombinationImageToImageMetric::SetMetric, as that seems to be the only place where it is needed.


/** Typedef for transform penalty metrics. */
typedef TransformPenaltyTerm<TFixedImage> TransformMetricType;
typedef typename TransformMetricType::Pointer TransformMetricPointer;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TransformMetricPointer is never used, so I think I would remove it. (No show-stopper for now.)

TransformMetricType * testPtr2 = dynamic_cast<TransformMetricType *>(metric);

// Increase newly defined numberOfMetric counters
if (testPtr1)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would make it:

if( dynamic_cast<PointSetMetricType *>(metric) )

And remove the local testPtr1 variable.

Similar for testPtr2, oldTestPtr1, oldTestPtr2. No show-stopper to me, though. Just a style thing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants