Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to convert ITK transform into Elastix transform parameters file? #246

Open
dzenanz opened this issue Sep 12, 2023 · 14 comments
Open

How to convert ITK transform into Elastix transform parameters file? #246

dzenanz opened this issue Sep 12, 2023 · 14 comments
Assignees

Comments

@dzenanz
Copy link
Member

dzenanz commented Sep 12, 2023

I have corresponding fiducial points between a pair of images. I use it to compute similarity transform. What is the best way to pass this as initial transform? See #245 for an attempt.

Setting it via SetInitialTransform seems to work (the compound registration does not work correctly in Slicer unless it is "split"), but a later call to:

parameter_object.WriteParameterFile(elastix_transform, str(output_dir / "rigid_transform.txt"))

produces:

RuntimeError: D:\a\im\_skbuild\win-amd64-3.9\cmake-build\_deps\elx-src\Core\Main\elxParameterObject.cxx:314:
ITK ERROR: ParameterObject(000002044268FF30): Error writing to disk: The number of parameter maps (2) does not match the number of provided filenames (1). Please call WriteParameterFiles instead, and provide a vector of filenames.

So I tried:

parameter_object.WriteParameterFiles(
    elastix_transform,
    ["fiducial_transform.txt", str(output_dir / "rigid_transform.txt")],
)

and it does not write the fiducial_transform.txt anywhere on the disk, with rigid_transform.txt having the following content:

(CenterOfRotationPoint -4.944945387437201 -37.24496703995354 -59.44102459574103)
(CompressResultImage "false")
(DefaultPixelValue 0)
(Direction 1 0 0 0 1 0 0 0 1)
(FinalBSplineInterpolationOrder 3)
(FixedImageDimension 3)
(FixedInternalImagePixelType "float")
(HowToCombineTransforms "Compose")
(Index 0 0 0)
(InitialTransformParameterFileName "fiducial_transform.txt")
(MovingImageDimension 3)
(MovingInternalImagePixelType "float")
(NumberOfParameters 7)
(Origin -140 -140 -383.625)
(ResampleInterpolator "FinalBSplineInterpolator")
(Resampler "DefaultResampler")
(ResultImageFormat "nii")
(ResultImagePixelType "float")
(Size 281 281 355)
(Spacing 1 1 1)
(Transform "SimilarityTransform")
(TransformParameters 0.0015680670087272384 -0.0006157409483032898 0.0031103486499469784 0.16096493087864483 -0.12426840552647811 2.9018697571742758 0.988135955256508)
(UseDirectionCosines "true")

Passing rigid_transform.txt via SetInitialTransformParameterFileName to the next (BSpline) stage of registration produces an error:

File: D:\a\im\_skbuild\win-amd64-3.9\cmake-build\_deps\elx-src\Common\ParameterFileParser\itkParameterFileParser.cxx
Line: 255
Description: ITK ERROR: ERROR: the file fiducial_transform.txt does not exist.

If I do not provide initial transform to the first registration phase, the whole pipeline works - with imperfect results.

@dzenanz
Copy link
Member Author

dzenanz commented Sep 12, 2023

Applying an old trick from #79 (comment) seems to work, at least from the no-crashing point of view. Is this still the best way to do it? Why doesn't Elastix automatically combine two similarity transforms into one?

@thewtex
Copy link
Member

thewtex commented Sep 25, 2023

@dzenanz have you tried SetExternalInitialTransform?

@dzenanz
Copy link
Member Author

dzenanz commented Sep 25, 2023

I tried it, but at the time ran into a different issue: #245.

@N-Dekker
Copy link
Collaborator

So I tried:

parameter_object.WriteParameterFiles(
    elastix_transform,
    ["fiducial_transform.txt", str(output_dir / "rigid_transform.txt")],
)

and it does not write the fiducial_transform.txt anywhere on the disk

Looks like that's the following issue:

I wasn't sure if it was worth fixing, because apparently it behaved like that for more than five years already. So that's why it's still open. ParameterObject::WriteParameterFile still works well for a single parameter map, fortunately.

@dzenanz
Copy link
Member Author

dzenanz commented Oct 9, 2023

With itk-elastix 0.19.0 and code from #245 (comment),

parameter_object.WriteParameterFiles(
    elastix_transform_parameters,
    ["fiducial_transform.txt", "rigid_transform.txt"])

produces both files, but content of fiducial_transform.txt is this:

(NumberOfParameters 0)
(Transform "ExternalTransform")
(TransformAddress "000001FF3C274330")

and subsequent run fails with:

itk::ExceptionObject (000000712F3E9F80)
Location: "ElastixTemplate - Run()" 
File: D:\a\im\_skbuild\win-amd64-3.9\cmake-build\_deps\elx-src\Components\Transforms\ExternalTransform/elxAdvancedTransformAdapter.h
Line: 146
Description: ITK ERROR: AdvancedTransformAdapter(0000021F3508AA80): Not implemented for AdvancedTransformAdapter

Error occurred during actual registration.

@N-Dekker
Copy link
Collaborator

N-Dekker commented Oct 9, 2023

With itk-elastix 0.19.0 and ... produces both files, but content of fiducial_transform.txt is this:

(NumberOfParameters 0)
(Transform "ExternalTransform")
(TransformAddress "000001FF3C274330")

If you do elastix_registration_method_object.SetOutputDirectory(an-existing-directory) and then run the registration, does it produce the transform parameter files properly? (In the specified output directory, I mean.)

@dzenanz
Copy link
Member Author

dzenanz commented Oct 9, 2023

As far as I can tell, it does: temp.zip.

@dzenanz
Copy link
Member Author

dzenanz commented Oct 9, 2023

The initial registration does that (produce those files).

@N-Dekker
Copy link
Collaborator

N-Dekker commented Oct 9, 2023

ElastixRegistrationMethod::GenerateData() has some code to specifically write an "ExternalTransform" to a separate ITK compatible transform file, either ".tfm" or ".h5". It's not yet clear to me how to make that functionality available to ParameterObject.WriteParameterFiles. Do you think it's necessary anyway? Or is it sufficient to just let elastix write the transform files to the output directory specified by SetOutputDirectory?

@dzenanz
Copy link
Member Author

dzenanz commented Oct 9, 2023

I am using ParameterObject.WriteParameterFiles to create initial transform for the next stage of registration.

ElastixRegistrationMethod::GenerateData() has some code to specifically write an "ExternalTransform" to a separate ITK compatible transform file

As I wrote earlier, I am doing this manually now. It would be good if WriteParameterFiles took care of that when appropriate.

@N-Dekker
Copy link
Collaborator

@dzenanz Sorry, can you please clarify your use case? Do I understand correctly that you have a similarity transform, created outside of elastix? Do you have it stored in an ITK ".tfm" or ".h5" file already?

Elastix supports specifying an external ".tfm" or ".h5" file as "TransformFileName" parameter, in an elastix transform parameter map or a transform parameter txt file:

https://github.com/SuperElastix/elastix/blob/57511dd16c68fbe967ff82fc734a05a9cc916bc6/Testing/Data/Translation(1%2C-2)/TransformParameters-link-to-ITK-tfm-file.txt

(This support is only implemented for ITK transform files that represent AffineTransform, BSplineTransform, Euler2D, Euler3D, Similarity2D, Similarity3D, TranslationTransform, or a Composite of them.)

Does that help you any further?

@dzenanz
Copy link
Member Author

dzenanz commented Oct 13, 2023

The use case is: use landmarks to create an initial transform, then use that to initialize first stage of registration, then result of that to initialize the next stage etc. I explained it a bit here: #245 (comment).

I already have a working code, via a workaround of providing the initial transform via TransformFileName, as described here: #79 (comment).

It would be good if this workaround was not needed. If fixing that has lousy cost/benefit ratio, than this issue can be closed.

@N-Dekker
Copy link
Collaborator

N-Dekker commented Oct 16, 2023

We need to further investigate how ParameterObject.WriteParameterFiles should deal with the "ExternalTransform" feature. You see, it's a bit tricky, because the pointer to the external transform may be dangling, once the ParameterObject is written to file. It's on our TODO list, thanks.

@idhamari
Copy link

Why there are no functions to read/write and convert between both transforms?

e.g. something like:

        elxTransform = readElastixTransform(elastixTransformFilePath)   
        itkTranform    =  convertElastix2ItkTransform(elxTranform)
        writeItkTransform(itkTransformFilePath)   

        itkTransform = readItkTransform(itkTransformFilePath)   
        elxTranform    =  convertItk2ElastixTransform(itkTransform)
        writeElastixTransform(elastixTransformFilePath)   

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants