ANTsX / ANTsPy

A fast medical imaging analysis library in Python with algorithms for registration, segmentation, and more.
https://antspyx.readthedocs.io
Apache License 2.0
608 stars 161 forks source link

Cannot re-create affine transform from parameters #525

Closed jeroen-aidence closed 6 months ago

jeroen-aidence commented 6 months ago

I have computed an affine transform. I'd like to save its parameters, store them somewhere and then recreate the affine transform later. However, the transform generated by ants.new_ants_transform yields a different output compared to the original transform.

Steps to reproduce the behavior:

registration = ants.registration(
            fixed=fixed,
            moving=moving,
            type_of_transform="Affine",
        )
transform = ants.read_transform(registration['fwdtransforms'][0])
transform_new = ants.new_ants_transform(dimension=2, parameters=transform.parameters)

moved = transform.apply_to_image(moving, reference=fixed)
moved_new = transform_new.apply_to_image(moving, reference=fixed)

Expected behavior I had expected that the resulting images moved and moved_new would be identical, which is not the case

Screenshots

ntustison commented 6 months ago

https://github.com/ANTsX/ANTs/wiki/antsRegistration-reproducibility-issues

For an exactly reproducible affine transform, use type_of_transform = "antsRegistrationSyNQuickRepro[a]" or type_of_transform = "antsRegistrationSyNRepro[a]".

jeroen-aidence commented 6 months ago

Thanks for the quick reply. That does not really seem to be my issue though. I am not re-estimating the affine transform. I'm just trying to recreate it from existing parameters using ants.new_ants_transform.

Changing type_of_transform = "antsRegistrationSyNQuickRepro[a]" in ants.registration does not solve the issue.

stnava commented 6 months ago

that's user error. the right way is just to read the transform or to use it on disk. there are several ways. here is one:

import ants
fi = ants.image_read(ants.get_ants_data('r16'))
mi = ants.image_read(ants.get_ants_data('r64'))
mytx = ants.registration(fixed=fi, moving=mi, type_of_transform = 'SyN' )
tx = ants.read_transform( mytx['fwdtransforms'][1] )
warped_again = tx.apply( mi, 'image', fi )
ants.plot( warped_again, mytx['warpedmovout'] )
# read this to see the parameters you should set
help( ants.create_ants_transform )
jeroen-aidence commented 6 months ago

Alright, that is indeed a workaround and in most cases the most useful one. In my case that's a little less convenient as I was hoping to only store the transform.parameters in a pickle with a bunch of other metadata.

So you're saying that there is then no option to recreate a transform with either ants.new_ants_transform or ants.create_ants_transform. I am still a bit puzzled what the use of the parameters option then is on both functions.

stnava commented 6 months ago

read this to see the parameters you should set

help( ants.create_ants_transform )

you need more than what you have done. please read carefully.

jeroen-aidence commented 6 months ago

Thanks!

And great tool btw!

cookpa commented 6 months ago

I believe the difference arises because transform_new gets the parameters array from transform but it does not get passed transform's fixed_parameters.

While it's not an efficient way to re-use transforms, it seems wrong that there's no way to set fixed_parameters in the new_ants_transform_method. I can add fixed_parameters as an optional arg to ants.new_ants_transform with default None (implies an array of zeros), should be backwards compatible

jeroen-aidence commented 6 months ago

That explains it then. You probably would not recommend doing it this way, but additionally setting transform_new.set_fixed_parameters(transform.fixed_parameters) did the trick 👍