# Transformation graph for quick calculation of concatenated transforms

**URL:** <https://discourse.slicer.org/t/transformation-graph-for-quick-calculation-of-concatenated-transforms/326>\
**Category:** Development\
**Created:** [May 16, 2017, 5:23pm UTC](https://discourse.slicer.org/t/transformation-graph-for-quick-calculation-of-concatenated-transforms/326 "2017-05-16T17:23:41Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![mangotee](https://avatars.discourse-cdn.com/v4/letter/m/c57346/32.png) [@mangotee](https://discourse.slicer.org/u/mangotee)\
**Post date:** [May 16, 2017, 5:23pm UTC](https://discourse.slicer.org/t/transformation-graph-for-quick-calculation-of-concatenated-transforms/326/1 "2017-05-16T17:23:41Z")

</div>

Hi,

I am looking for a quick way to compute an in-between transformation between two volume nodes that are not directly connected in a transformation graph. Here is a figure of what I mean:

 ![](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/1X/303601b8f03cca77b1b6abbbb1d6c94e2e962c66.jpg)

Assume I have several MRI contrasts (T1/T2/T2S and HR), and I want to resample T2S in the voxel grid of a high-resolution volume “HR”. Often, I need a direct transform between the two, i.e. “T2SToHR”, e.g. for a module like “Resample Scalar/Vector/DWI Volume” to function.

In PLUS, this is neatly solved with a transformation graph, and I can simply give a string “T2SToHR” which is parsed to calculate the in-between transform via graph traversal.  
Is there a similar mechanism in Slicer?  
I want to use the “Resample Scalar/Vector/DWI Volume” module, and I want to avoid coding the transformation chain out explicitly every time I want to do such an operation.

Cheers,  
Ahmad

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [May 16, 2017, 5:31pm UTC](https://discourse.slicer.org/t/transformation-graph-for-quick-calculation-of-concatenated-transforms/326/2 "2017-05-16T17:31:50Z")

</div>

Use [slicer.vtkMRMLTransformNode.GetTransformBetweenNodes](http://apidocs.slicer.org/master/classvtkMRMLTransformNode.html#acbab274893b3d11cac30c4ee8a036f96) method for general transforms (to get a transformation chain that can include any number of non-linear transforms).

If you only have linear transforms between the nodes you can get the concatenated transforms as a single 4x4 matrix by using  
[slicer.vtkMRMLTransformNode.GetMatrixTransformBetweenNodes](http://apidocs.slicer.org/master/classvtkMRMLTransformNode.html#acbab274893b3d11cac30c4ee8a036f96).

To use this transform in a CLI module, create a new transform node and set the concatenated transform into that.

---

<div class="post-metadata">

**Author:** ![mangotee](https://avatars.discourse-cdn.com/v4/letter/m/c57346/32.png) [@mangotee](https://discourse.slicer.org/u/mangotee)\
**Post date:** [May 16, 2017, 7:43pm UTC](https://discourse.slicer.org/t/transformation-graph-for-quick-calculation-of-concatenated-transforms/326/3 "2017-05-16T19:43:11Z")

</div>

> [@lassoan](#):
>
> slicer.vtkMRMLTransformNode.GetTransformBetweenNodes

Thanks a lot! This looks exactly like what I need. However, I have problems with executing the command. Given the example above, I tried the following:  
slicer.vtkMRMLTransformNode.GetTransformBetweenNodes(nT2SToT2.SafeDownCast(vtk.vtkGeneralTransform()), nHRToT1.SafeDownCast(vtk.vtkGeneralTransform()), T.SafeDownCast(vtk.vtkGeneralTransform()))

(note: I had to SafeDownCast() - all transforms are linear - to a vtkGeneralTransform. I could have tried the matrix4x4 version you mentioned, but I wanted to try the general approach for more complex scenarios later).  
The command runs, but the result is wrong as I receive the identity matrix.  
I also received the following warning in the log:  
Generic Warning: In /Users/kitware/Dashboards/Package/Slicer-462/Libs/MRML/Core/vtkMRMLTransformNode.cxx, line 472 vtkMRMLTransformNode::GetTransformToNode failed: transformSourceToTarget is invalid

Could you give a hint regarding the syntax?  
Thanks!

---

<div class="post-metadata">

**Author:** ![mangotee](https://avatars.discourse-cdn.com/v4/letter/m/c57346/32.png) [@mangotee](https://discourse.slicer.org/u/mangotee)\
**Post date:** [May 16, 2017, 7:46pm UTC](https://discourse.slicer.org/t/transformation-graph-for-quick-calculation-of-concatenated-transforms/326/4 "2017-05-16T19:46:49Z")

</div>

P.S.: the volumes and transforms (as in the figure above) are arranged in the scene as follows:  
 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/3/b/3b8ba70bb1c84515d3e7ab449ffd4e0f807cc24c.png)

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [May 16, 2017, 7:57pm UTC](https://discourse.slicer.org/t/transformation-graph-for-quick-calculation-of-concatenated-transforms/326/5 "2017-05-16T19:57:41Z")

</div>

Example:

```
transformAToB = vtk.vtkGeneralTransform()
a = getNode('MRHead').GetParentTransformNode()
b = getNode('CTChest').GetParentTransformNode()
slicer.vtkMRMLTransformNode.GetTransformBetweenNodes(a, b, transformAToB)

```

If you want to use transformAToB in a transform node using SetTransformToParent then you have to deep-copy it using slicer.vtkMRMLTransformNode.DeepCopyTransform. If you only have linear transforms it is much simpler and faster to use GetMatrixTransformBetweenNodes and SetMatrixTransformToParent.

---

<div class="post-metadata">

**Author:** ![mangotee](https://avatars.discourse-cdn.com/v4/letter/m/c57346/32.png) [@mangotee](https://discourse.slicer.org/u/mangotee)\
**Post date:** [May 16, 2017, 8:00pm UTC](https://discourse.slicer.org/t/transformation-graph-for-quick-calculation-of-concatenated-transforms/326/6 "2017-05-16T20:00:04Z")

</div>

Excellent. I’ll try this out!  
Again thanks a lot for the quick reply!

---

<div class="post-metadata">

**Author:** ![mangotee](https://avatars.discourse-cdn.com/v4/letter/m/c57346/32.png) [@mangotee](https://discourse.slicer.org/u/mangotee)\
**Post date:** [May 17, 2017, 4:47pm UTC](https://discourse.slicer.org/t/transformation-graph-for-quick-calculation-of-concatenated-transforms/326/7 "2017-05-17T16:47:09Z")

</div>

> [@lassoan](#):
>
> slicer.vtkMRMLTransformNode.DeepCopyTransform

OK, I wrote a little utility function. In case it helps anyone, I’ll post it here. Apologies for poor naming etc… Also, indentation is wrong - if someone knows how to properly post formatted pieces of code, please let me know.

```
  def calculateInbetweenLinearTransform(self,nTrfSource,nTrfTarget,nTrfSourceToTarget):
    # Get concatenated transforms from source to target nodes in the MRML scene. Source 
    # and target nodes are allowed to be NULL, which represents the world coordinate 
    # system. Result is written into nTrfSourceToTarget (has to be a
    # vtkMRMLLinearTransformNode)
    matrixAToB = vtk.vtkMatrix4x4()
    if nTrfSource is not None:
      a = nTrfSource.GetParentTransformNode()
    else:
      a = None
    if nTrfTarget is not None:
      b = nTrfTarget.GetParentTransformNode()
    else:
      b = None
    # calculate the matrix transform
    slicer.vtkMRMLTransformNode.GetMatrixTransformBetweenNodes(a,b,matrixAToB)
    # set the matrix transform into target
    nTrfSourceToTarget.SetMatrixTransformToParent(matrixAToB)

```

---

<div class="post-metadata">

**Author:** ![Fernando](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/fernando/32/5640_2.png) [@Fernando](https://discourse.slicer.org/u/Fernando)\
**Post date:** [May 17, 2017, 5:09pm UTC](https://discourse.slicer.org/t/transformation-graph-for-quick-calculation-of-concatenated-transforms/326/8 "2017-05-17T17:09:00Z")

</div>

You just need to add 4 spaces to your code, there’s even a button for it on the Discourse GUI.

```
def calculateInbetweenLinearTransform(self, nTrfSource, nTrfTarget, nTrfSourceToTarget):
    # Get concatenated transforms from source to target nodes in the MRML scene. Source 
    # and target nodes are allowed to be NULL, which represents the world coordinate 
    # system. Result is written into nTrfSourceToTarget (has to be a
    # vtkMRMLLinearTransformNode)
    matrixAToB = vtk.vtkMatrix4x4()
    if nTrfSource is not None:
        a = nTrfSource.GetParentTransformNode()
    else:
        a = None
    if nTrfTarget is not None:
        b = nTrfTarget.GetParentTransformNode()
    else:
        b = None
    # calculate the matrix transform
    slicer.vtkMRMLTransformNode.GetMatrixTransformBetweenNodes(a,b,matrixAToB)
    # set the matrix transform into target
    nTrfSourceToTarget.SetMatrixTransformToParent(matrixAToB)
```

---

<div class="post-metadata">

**Author:** ![Fernando](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/fernando/32/5640_2.png) [@Fernando](https://discourse.slicer.org/u/Fernando)\
**Post date:** [May 21, 2017, 6:28pm UTC](https://discourse.slicer.org/t/transformation-graph-for-quick-calculation-of-concatenated-transforms/326/9 "2017-05-21T18:28:57Z")

</div>

Andras’ answer should be the solution of this topic, not mine.

---

<div class="post-metadata">

**Author:** ![xxzhou](https://avatars.discourse-cdn.com/v4/letter/x/13edae/32.png) [@xxzhou](https://discourse.slicer.org/u/xxzhou)\
**Post date:** [September 11, 2018, 3:15pm UTC](https://discourse.slicer.org/t/transformation-graph-for-quick-calculation-of-concatenated-transforms/326/10 "2018-09-11T15:15:30Z")

</div>

Hi! I am new for 3D Slicer, and when I to used your method to calculate the transform matrix, I found that  
a = getNode().GetParentTransformNode,  
a is NONE, same as b. This resulted in transformAToB equaled to Identity Matrix.

But if I directly use  
a,b = getNode(),  
transformAToB = slicer.vtkMRMLTransformNode()  
slicer.vtkMRMLTransformNode.GetTransformBetweenNodes(a, b, transformAToB)

I was told method requires a vtkMRMLTransformNode, a vtkMRMLScalarVolumeNode was provided.

I am wondering if anyone could help me find out why this problem comes out.

Appreciate your help!  
Thank you.  
Cheers,  
xxzhou

---

<div class="post-metadata">

**Author:** ![cpinter](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/cpinter/32/7995_2.png) [@cpinter](https://discourse.slicer.org/u/cpinter)\
**Post date:** [September 11, 2018, 3:54pm UTC](https://discourse.slicer.org/t/transformation-graph-for-quick-calculation-of-concatenated-transforms/326/11 "2018-09-11T15:54:53Z")

</div>

> [@xxzhou](#):
>
> a = getNode().GetParentTransformNode

Please post your actual code that doesn’t work because the one above for example is not valid, and it’s impossible like this to figure out what is going wrong.

---

<div class="post-metadata">

**Author:** ![xxzhou](https://avatars.discourse-cdn.com/v4/letter/x/13edae/32.png) [@xxzhou](https://discourse.slicer.org/u/xxzhou)\
**Post date:** [September 11, 2018, 4:22pm UTC](https://discourse.slicer.org/t/transformation-graph-for-quick-calculation-of-concatenated-transforms/326/12 "2018-09-11T16:22:13Z")

</div>

Thank you for responding! Sure.

My code is as below:

source = getNode(‘preop’).GetParentTransformNode()  
target = getNode(‘intraop’).GetParentTransformNode()  
transformNode = vtk.vtkGeneralTransform()  
slicer.vtkMRMLTransformNode.GetTransformBetweenNodes(source, target, transformNode)

Then I use:  
print(transformNode)

Output is:  
vtkGeneralTransform (0x608000134fa0)  
Debug: Off  
Modified Time: 2504871  
Reference Count: 1  
Registered Events: (none)  
Inverse: (0)  
Input: (0)  
InverseFlag: 0  
NumberOfConcatenatedTransforms: 0

There is no error, but actually source and target all equal to NONE, so it seems that transformNode = NONE

When I try to change as below:

source = getNode(‘preop’)  
target = getNode(‘intraop’)  
TransformMatrix = vtk.vtkGeneralTransform()  
slicer.vtkMRMLTransformNode.GetTransformBetweenNodes(source, target, TransformMatrix)

GetTransformBetweenNodes argument 1: method requires a vtkMRMLTransformNode, a vtkMRMLScalarVolumeNode was provided.

I think it is because source and target are both vtkMRMLScalarVolumeNode. I am wondering how to change the vtkMRMLScalarVolumeNode to vtkMRMLTransformNode, which is not NONE.

I am also wondering how to apply this transform matrix on the source image. I have tried the code above:

nTrfSourceToTarget = slicer.vtkMRMLTransformNode  
nTrfSourceToTarget.SetMatrixTransformToParent(transformNode)

but it seems do not work. The error is:  
TypeError: unbound method requires a vtkCommonCorePython.vtkMRMLTransformNode as the first argument

Too many questions…  
Thank you for your help!

---

<div class="post-metadata">

**Author:** ![cpinter](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/cpinter/32/7995_2.png) [@cpinter](https://discourse.slicer.org/u/cpinter)\
**Post date:** [September 11, 2018, 5:55pm UTC](https://discourse.slicer.org/t/transformation-graph-for-quick-calculation-of-concatenated-transforms/326/13 "2018-09-11T17:55:17Z")

</div>

How about this:

```
source = getNode(‘preop’)
target = getNode(‘intraop’)
sourceToTargetTransformNode = slicer.vtkMRMLLinearTransformNode()
slicer.mrmlScene.AddNode(sourceToTargetTransformNode)
slicer.vtkMRMLTransformNode.GetTransformBetweenNodes(source, target, sourceToTargetTransformNode)

```

You can set a parent transform with [SetAndObserveTransformNodeID](https://github.com/Slicer/Slicer/blob/master/Libs/MRML/Core/vtkMRMLTransformableNode.h#L59)

The problem in your code has been using the wrong types in each case. You need to make sure that you pass/create/get the object you need, and not some related object of a different type.
