Transform Issue in FastModelAlign

I am currently testing out FastModelAlign for affine transforms.
I see there are 3 transformation matrices created, I assume in this order:

  1. scaling
  2. rigid transformation
  3. affine transformation

The resulting model is the result after these 3 transformations and looks as expected.

However, if I make a copy of the original model and apply the “scaling” transform to it, it already matches the FastModelAlign output.

I.e. selecting the “scaling” transform generated by FastModelAlign to a copy of the original model seems to apply the other two transformations (rigid and affine) as well.

Similarly, if I run the registration without the affine transform, applying the “scaling” transform to the original mesh it also translates/rotates it (seems to include rigid transformation too).

I see on the github repo that there is a comment " #Put affine transform node under scaling transform node, which has been put under the rigid transform node".

Does this mean that the scaling transform also contains the information of the affine transform?
The observed behavior would suggest this, but when I look at the actual values of the scaling transform it is just a scaling transform…

I am aware of the LPS/RAS and to/from parent distinctions in the transforms and the exports work well. I am just confused by the behaviour described above within Slicer.

Thank you,
Eva

@evaherbst transforms are nested, and they are nested in the order they are created. Each one only contains the transformation associated only with that specific step. In this example I am aligning small (source) model to a large model (target). First there is a linear scaling transform, then a rigid transform and finally an affine transform. Affine transform by itself probably has very little change associated with it because previous steps already taken care of the much larger scaling and alignment transform.

If you want to have a single transform that contains everything, just right-click and harden the transforms nested in the top transform (in this case small_affine). The resultant affine transform will contain everything. The final output (in this case Model), has all these transforms applied to the small model.

image

Thank you so much Murat for the quick reply.

I had no idea about the nesting transforms, that makes so much more sense.

The only odd thing is that it seems like the transform at the bottom of my hierarchy (scaling in this case) is containing all of the transforms.

So my hierarchy looks like yours, but if I select “affine”, only affine is applied, but if I select “scaling”, all 3 are applied.

Thanks again,
Eva

1 Like

I am not sure what you mean by “select affine”, can you clarify? Selecting where?

What the transforms are doing to the model is that affine transform is transforming the rigid, which in return is transforming the scaling transform. So, if you put your original source model under the scaling transform, all other transforms will be automatically applied in the correct order and you should get the model transformed exactly in the same way as your output model.

Does this help?

1 Like

Thanks Murat! By select I meant what you did in the subject hierarchy. I should have said “applying the transform”.

What the transforms are doing to the model is that affine transform is transforming the rigid, which in return is transforming the scaling transform

Yes, this makes sense now. I was just thinking the affine transform, being the last one applied, would contain all of the ones ^before and so I should apply that in the subject hierarchy. Now it makes sense why the scaling one does all of the transformations.

I was just initially confused because I thought the scaling transform only included scaling, etc. (as it does when you just look at the numbers under “transforms”). But the nesting structure now makes sense.

Thanks for the clarification!
And thanks for developing this tool, it is proving to be very useful already!
Eva

2 Likes

Glad to hear that! @chz31

1 Like

Glad to hear that the module works! We also have some explanation of the nested transformation matrices in the FastModelAlign tutorial at “4. Investigate and Visualize the scaling and rigid transformation process”: https://github.com/SlicerMorph/Tutorials/tree/main/FastModelAlign

1 Like

Thanks for sharing the tutorial!
I didn’t see it before.
And thanks again for developing this :slight_smile:

1 Like

Apologies for one last question.

In an example I made, I am transforming a source mesh to a smaller target mesh.
The scale transform has scale factors of .59, and the source mesh is therefore shrunk to align with the target when the scale transform is applied.
All good so far.

My question is, why is the transform labeled “to parent”? I would think that the source mesh is the parent. But if that is the case, I would expect the “to parent” transform to mean “source to target”, which should be the inverse of .59, rather than .59.

Or is the parent referring to the target rather than the source?

Either way I can of course use this transform and export it (and calculate the inverse if needed). But I was just wondering about the conventions regarding what is considered the parent here.

Thanks!
Eva

I do not know the history of terminology and where term parent comes. All i that terms like source, target etc may mean different things in related different fields such computer vision and medical image analysis.

Ok, thank you for the quick reply.
I’ll just take it at face value then and not pay attention to the “to parent” labeling but just think of all the transforms as “source to target” within Slicer (with the inverse, so “target to source”, being exported from Slicer)

Best,
Eva

Also, I noticed using meshes with relatively low res (about 2k) gave inconsistencies in rigid alignment results when rerunning FastModelAlign with the same settings.
Affine alignments were almost identical.

Just a note for other users to be aware of.

Yes, as we showed empirically in the original ALPACA paper (which fastmodelalign is based on), you need 4000-6000 points for optimum registration. If your meshes are sparse in vertices and the generated point clouds are not hitting that limit, you can use the surface toolbox to increase the vertex density.

Thanks Murat! Makes sense.