I have a question that has already been asked at least twice (here and here) but got no explicit answer. Like their original authors, I’m struggling to understand how to compose the fixed initial, moving initial, and optimized transforms after registration to then resample the moving image. I’ll explain below everything that I understood, please tell me where I made a mistake.
From the documentation of SimpleITK, we can read that the final transform that maps points from the fixed to moving image domains is: T_{\text{opt}}\circ T_\text{m}\circ T_\text{f}^{-1}. Indeed, this is confirmed by one of SimpleITK tutorial notebooks that illustrates the different transformations using the following image:
From that, I understand that T_\text{f} goes from virtual to fixed image domains, T_\text{m} goes from virtual to moving image domains, and T_\text{opt} goes from moving to moving image domains.
Now, SimpleITK’s documentation also explains the following about CompositeTransform
:
represents multiple transformations applied one after the other T_0(T_1(T_2(... T_n(p) ...))). The semantics are stack based, that is, first in last applied:
composite_transform = CompositeTransform([T0, T1]) composite_transform.AddTransform(T2)
If I follow this correctly, after registration of two images, creating the final transform from T_\text{f}, T_\text{m}, and T_\text{opt} using a CompositeTransform
would be simply:
composite_transform = CompositeTransform(registration_method.GetInitialTransform())
composite_transform.AddTransform(registration_method.GetMovingInitialTransform())
composite_transform.AddTransform(registration_method.GetFixedInitialTransform().GetInverse())
The optimized transform was added first in the stack, so it will be applied last, as described in the documentation. We should thus obtain T_{\text{opt}}\circ T_\text{m}\circ T_\text{f}^{-1}. The following example notebook also confirms that the transforms should be composed in this order.
In theory, all is well! Problems arise when you dive a bit deeper into the documentation and try to apply all of this on actual images.
-
First, reading section 3.2 of the ITK software guide and one of ITK’s examples, we find contradicting code:
CompositeTransformType = itk.CompositeTransform[itk.D, Dimension] outputCompositeTransform = CompositeTransformType.New() outputCompositeTransform.AddTransform(movingInitialTransform) outputCompositeTransform.AddTransform(registration.GetModifiableTransform()) resampler = itk.ResampleImageFilter.New( Input=movingImage, Transform=outputCompositeTransform, UseReferenceImage=True, ReferenceImage=fixedImage, )
Here, the moving initial transform is added before the optimized transform! Could this be because ITK and SimpleITK have contradictory definitions of
CompositeTransform
? From ITK’s documentation ofCompositeTransform
, I would think that a reverse queue is actually a stack and that their definitions are the same, but ITK’s guide seems to contradict SimpleITK’s documentation, so I might be wrong somewhere…
- Using actual images, composing transforms like described in SimpleITK’s documentation does not work when using a moving initial transform that contains a flip (negative zoom) along one axis. This is fixed by composing them like described in ITK’s documentation. I’ll post a reproducible example ASAP.
Could you please help me figure out where I go wrong?