elastix icon indicating copy to clipboard operation
elastix copied to clipboard

Euler transform with optional upper/lower limits to each parameter

Open zigaso opened this issue 4 years ago • 5 comments

Intro

This adds a new transform dubbed the AdvancedLimitedEulerTransform, which implements per axis upper and/or lower limiting. It achieves this functionality by clamping the transform gradient based on the current value of each parameter, using the Softplus function.

In the parameter file, set the transformation as:

(Transform "AdvancedLimitedEulerTransform")

Additional parameter to set are:

// SharpnessOfLimits defines the cutoff sharpness; higher values represent a sharper cutoff
(SharpnessOfLimits "200.0000000000") 
// UpperLimits and LowerLimits are defined to absolute values of each of three Euler angles and translations, ie (a, b, c, tx, ty, tz) 
(UpperLimits 0.5000000000 0.5000000000 0.5000000000 10.0000000000 10.0000000000 10.0000000000)
(LowerLimits -0.5000000000 -0.5000000000 -0.5000000000 -10.0000000000 -10.0000000000 -10.0000000000)

Note: axis limiting is implemented for 3D images and for use with gradient based optimizers! Using 2D input images will throw an error, while using another optimizer will not effectively apply the limits.

Example application

For instance if 3D CT was translated +15 mm along the X axis, and then registered onto its original version, and with the registration limited to +/- 10 mm along the X axis like this (see TransformParameters.tx15.txt):

transformix.exe -in CT_fix.nii.gz -tp TransformParameters.tx15.txt -out .
// convert result.nrrd to CT_fix_tx15.nii using your favourite converter
elastix.exe -f CT_fix.nii.gz -m CT_fix_tx15.nii -p ElastixParameters_tx15.txt -out output

The corresponding output can be found in folder output, ie. from TransformParameters.0.txt see the following line:

(TransformParameters -0.000304 -0.008137 -0.017665 -10.700775 -0.109858 0.028943)

where indeed the translation along the X axis was limited at -10 mm. The limits are also propagated into the transform parameter file: ... (SharpnessOfLimits "200.0000000000") (UpperLimits 0.5000000000 0.5000000000 0.5000000000 10.0000000000 10.0000000000 10.0000000000) (LowerLimits -0.5000000000 -0.5000000000 -0.5000000000 -10.0000000000 -10.0000000000 -10.0000000000)

Additionally, if a limit was reached, this is indicated in the transform parameters file by value close to 1 as follows:

(LowerLimitsReached 0.0000000000 0.0000000000 0.0000000000 0.9990956587 0.0000000000 0.0000000000) (UpperLimitsReached 0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.0000000000)


Specific axis could also be effectively disabled by settings the upper and lower limits to same (zero) value:

(LowerLimits 0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.0000000000) (UpperLimits 0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.0000000000)

[ElastixParameters_tx15.txt](https://github.com/SuperElastix/elastix/files/7007427/ElastixParameters_tx15.txt)
[TransformParameters.tx15.txt](https://github.com/SuperElastix/elastix/files/7007429/TransformParameters.tx15.txt)


zigaso avatar Aug 18 '21 13:08 zigaso

Hi,

it seem the Elastix framework was refactored recently and this is causing the build to fail. The problem seems to be in the waythe output transform parameter file is to be written.

We identified the following issue:

In header Core/ComponentBaseClasses/elxTransformBase.hxx

 /** Allows a derived transform class to write its data to file, by overriding this member function. */
  virtual void
  WriteDerivedTransformDataToFile(void) const
  {}

Parameters to write are passed in the implementation of function WriteToFile(xl::xoutsimple & transformationParameterInfo, const ParametersType & param) and at the end of function the following happens:

  transformationParameterInfo << Conversion::ParameterMapToString(parameterMap);

  WriteDerivedTransformDataToFile();

There is an incentive to override the WriteDerivedTransformDataToFile(), however, it does not accept any parameters, while the parameterMap seems to be written to output stream prior to calling this function.

In our current implementation we want to compute an output vector, i.e. LimitsReached, based on the current transform paramters, i.e. EulerTransformParameters and the parametersUpperLimits, LowerLimits and SharpnessOfLimits.

In previous Elastix version (4.9) there used to be a mechanism to override the WriteToFile() function and thus generate the transform parameter file, with all the parameters available in the function.

My question is how is it possible, in the refactored elastix framework, to compute output values and write them into the transform parameters file? Was this option considered? If yes, is there a use case such that to take a look?

Many thanks, Ziga

zigaso avatar Aug 24 '21 07:08 zigaso

@zigaS-fe-uni-lj Thank you for your pull request! Indeed, with a recent refactoring, the support to override elastix::TransformBase::WriteToFile was removed. We observed that in most cases, it is sufficient for elastix::TransformBase::WriteToFile to just write the content of its transform parameter map. If your (derived) transform class has some extra transform parameters to be added to the map, you may now override elastix::TransformBase::CreateDerivedTransformParametersMap(void), instead, returning a map of the extra transform parameters! I think it should be as follows:

template <class TElastix>
auto
AdvancedLimitedEulerTransformElastix<TElastix>::CreateDerivedTransformParametersMap(void) const -> ParameterMapType
{
  ParameterMapType parameterMap{
    { "CenterOfRotationPoint", Conversion::ToVectorOfStrings(m_LimitedEulerTransform->GetCenter()) },
    { "SharpnessOfLimits", { Conversion::ToString(m_LimitedEulerTransform->GetSharpnessOfLimits()) } },
    { "UpperLimits", Conversion::ToVectorOfStrings(m_LimitedEulerTransform->GetUpperLimits()) },
    { "LowerLimits", Conversion::ToVectorOfStrings(m_LimitedEulerTransform->GetLowerLimits()) }
  };

  if (SpaceDimension == 3)
  {
    parameterMap["ComputeZYX"] = { Conversion::ToString(m_LimitedEulerTransform->GetComputeZYX()) };
  }
  return parameterMap;
}

When this member function is added to AdvancedLimitedEulerTransformElastix, I think both overrides, WriteToFile and CreateTransformParametersMap, may just be removed from AdvancedLimitedEulerTransformElastix. I think, in your case, it would not be necessary to override WriteDerivedTransformDataToFile(void). However, you would need to try it out!

Maybe it's also helpful to take a look at the current revision (develop branch, git HEAD) of elxEulerTransform.h and elxEulerTransform.hxx at https://github.com/SuperElastix/elastix/tree/develop/Components/Transforms/EulerTransform

Hope this helps! Kind regards, Niels

N-Dekker avatar Aug 30 '21 14:08 N-Dekker

When a user does not specify those new parameters (SharpnessOfLimits, UpperLimits, LowerLimits) would AdvancedLimitedEulerTransform behave exactly like the existing EulerTransform? I would like it like that!

N-Dekker avatar Nov 30 '21 11:11 N-Dekker

@zigaso Just for my understanding, does your proposal only support 3D, no 2D? Because I see that you wrote "itkAdvancedLimitedEuler3DTransform.h", but no corresponding 2D transform!

Just want to be sure that that's your intention! 😃

N-Dekker avatar Dec 10 '21 15:12 N-Dekker

   你好:   邮件内容已收到,我会尽快处理,谢谢   祝好!

cosfrist avatar Dec 10 '21 15:12 cosfrist