elastix icon indicating copy to clipboard operation
elastix copied to clipboard

Difference between ITK and elastix BSplineTransform dealing with input points at the border

Open N-Dekker opened this issue 4 years ago • 10 comments

It appears that the elastix version of BSplineTransform excludes points at the border of the TransformDomainPhysicalDimensions, while ITK does include them, with the transformation.

For example when the OptimizerParameters are {0.75, 0.75, ... , 0.75}, and the input point is like {1.0, y} or {x, 1.0}, then elastix TransformPoint(inputPoint) just returns the input point, while ITK TransformPoint(inputPoint) yields {x + 0.75, y + 0.75}.

ITK TransformPoint(inputPoint) even transforms an input point to {x + 0.75, y + 0.75} when the point is an epsilon beyond the border.

N-Dekker avatar Aug 09 '21 08:08 N-Dekker

@mstaring @stefanklein These look like minor rounding errors to me, because the BSplineTransform implementations of ITK and elastix are slightly different. However, in order to smoothly exchange ITK Transform files with elastix, would it be worth to get elastix BSpline "in sync" with ITK?

N-Dekker avatar Aug 09 '21 08:08 N-Dekker

If the difference is only at the border, could it be due to the decision when we are at the border and when not?

This could be an region->IsInside( x ) difference for example.

Let's discuss at the sprint.

mstaring avatar Aug 09 '21 09:08 mstaring

It appears to be caused by a difference between ITK BSplineTransform::InsideValidRegion(ContinuousIndexType &index ) const and elastix AdvancedBSplineDeformableTransformBase::InsideValidRegion(const ContinuousIndexType & index) const.

Elastix: https://github.com/SuperElastix/elastix/blob/5.0.1/Common/Transforms/itkAdvancedBSplineDeformableTransformBase.hxx#L588-L605

ITK: https://github.com/InsightSoftwareConsortium/ITK/blob/v5.2.0/Modules/Core/Transform/include/itkBSplineTransform.hxx#L479-L509

Which does:

index[j] = Math::FloatAddULP(maxLimit, -6);

ITK started modifying the parameter, index, apparently, with this commit: https://github.com/InsightSoftwareConsortium/ITK/commit/054e6b70430bb478a33f31241cf186fb5dd1a93c Nick Tustison,10 June 2011, "BUG: Fixing transform domain initializer and transform domain definition."

Specifically on this line of code:

  index[j] -= 1e-6;

https://github.com/InsightSoftwareConsortium/ITK/blob/054e6b70430bb478a33f31241cf186fb5dd1a93c/Modules/Core/Transform/include/itkBSplineDeformableTransform.hxx#L685

N-Dekker avatar Sep 05 '21 22:09 N-Dekker

Then I propose to follow the ITK. Can we do it in such a way that we automatically pick up new modifications as well, e.g. by using inheritance?

mstaring avatar Sep 06 '21 07:09 mstaring

Then I propose to follow the ITK.

Honestly I don't really like the ITK approach. In my opinion, InsideValidRegion(index) should not modify the index. The adjustment of setting index[j] = Math::FloatAddULP(maxLimit, -6), as ITK now does, looks like some kind of workaround to me.

Can we do it in such a way that we automatically pick up new modifications as well, e.g. by using inheritance?

I think that would be a major redesign. But it may be worth it 🤔 having to keep our elastix transforms manually "in sync" with the corresponding ITK transforms (by copy-paste) does not seem workable in the long term.

N-Dekker avatar Sep 06 '21 08:09 N-Dekker

ok, let's discuss during our sprint. The major re-design may be put in an issue to work on later.

mstaring avatar Sep 06 '21 08:09 mstaring

I just made a few pull request to significantly improve the performance of ITK's BSpline implementation, especially https://github.com/InsightSoftwareConsortium/ITK/pull/2716 (BSplineKernelFunction::FastEvaluate(u): a faster way to evaluate BSplineKernelFunction) and https://github.com/InsightSoftwareConsortium/ITK/pull/2712 (Use FixedArray for BSplineInterpolationWeightFunction OutputType)

It's really a pity that those improvements do not get into elastix automatically.

N-Dekker avatar Sep 06 '21 09:09 N-Dekker

First attempt to implement elastix AdvancedTranslationTransform in terms of ITK TranslationTransform: https://github.com/SuperElastix/elastix/tree/AdvancedTranslationTransform-ITK-TranslationTransform Certainly a code reduction 😃

N-Dekker avatar Sep 06 '21 10:09 N-Dekker

The two InsideValidRegion(const ContinuousIndexType & index) member functions in Common/Transforms look identical, but not entirely!

https://github.com/SuperElastix/elastix/blob/5.0.1/Common/Transforms/itkAdvancedBSplineDeformableTransformBase.hxx#L589-L605

does for( unsigned int j = 0; j < SpaceDimension; j++ )

Whereas https://github.com/SuperElastix/elastix/blob/5.0.1/Common/Transforms/itkCyclicBSplineDeformableTransform.hxx#L68-L84

does for( unsigned int j = 0; j < SpaceDimension - 1; j++ )

Would that difference be intended, or is it a bug in CyclicBSplineDeformableTransform?

N-Dekker avatar Sep 16 '21 09:09 N-Dekker

that is intended due to the nature of the cyclic transform

mstaring avatar Sep 16 '21 11:09 mstaring