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

Accidental itk::NiftiImageIO metadata behaviour change wrt pixDim[0] #3241

Open
seanm opened this issue Mar 1, 2022 · 3 comments
Open

Accidental itk::NiftiImageIO metadata behaviour change wrt pixDim[0] #3241

seanm opened this issue Mar 1, 2022 · 3 comments
Labels
type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances

Comments

@seanm
Copy link
Contributor

seanm commented Mar 1, 2022

In f38b1dd I accidentally changed some behaviour. Specifically, the value of pixdim[0] in itk::NiftiImageIO metadata is different before and after the change.

Previous to the change, SetImageIOMetadataFromNIfTI() was populated from the nifti_1_header structure. After the change, it is populated from the nifti_image structure. The latter is created from the former with nifti_convert_nhdr2nim(). Most of the fields used in SetImageIOMetadataFromNIfTI() have 1-to-1 correspondences in these two structures. I expected all 8 elements of the pixdim array were the same in both structures, but in fact no; element 0 is special. nifti_convert_nhdr2nim() unconditionally sets pixdim[0] to 0.0 (by virtue of allocating the structure with calloc).

In my own app, I was using pixdim[0] (retrieved from itk::NiftiImageIO) as the last parameter to nifti_quatern_to_mat44(). I see now that I should be passing qfac as the last parameter. But the itk::MetaDataObject doesn't include it, so my first proposal here is to start including it.

Then I got to thinking that me even needing to call nifti_quatern_to_mat44() in my app is really a layering violation (leaking ITK's use of niftilib) and that it'd be nice if qto_xyz were also part of the metadata, so I added that too.

There remains the question of whether to actually revert my accidental behaviour change. There are pros and cons.

  • it's been changed since at least ITK 5.0a1, so people may already be expecting this new behaviour as much as others are expecting the old behaviour.
  • pixdim[0] is now always 0.0 and returning something that never changes is kinda silly.
  • everything in SetImageIOMetadataFromNIfTI() now comes from nifti_image, changing one field to be from nifti_1_header would be kinda weird. Now that I review all this, there are other fields that can be subtly different between the structs, even when they have identical names. For example nifti_convert_nhdr2nim() does things like this: nim->intent_p1 = FIXED_FLOAT( nhdr.intent_p1 ) ;

Given that this change is now 4 years old, I'm inclined to keep this new/current behaviour but:

  • document the behaviour change in some release notes (where?)
  • add qfac to the metadata
  • add qto_xyz to the metadata
@seanm seanm added the type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances label Mar 1, 2022
@seanm
Copy link
Contributor Author

seanm commented Mar 1, 2022

@hjmjohnson I'd appreciate your thoughts on this one...

Here's my WIP: #3242

@dzenanz
Copy link
Member

dzenanz commented Mar 1, 2022

document the behaviour change

This could go into https://github.com/InsightSoftwareConsortium/ITK/blob/master/Documentation/ITK5MigrationGuide.md.

pixdim[0] is now always 0.0

I agree this is silly. Returning something more meaningful is better.

Finally, maybe other people were not really directly using NiftiImageIO, so no one noticed the change?

@seanm
Copy link
Contributor Author

seanm commented Mar 1, 2022

I agree this is silly. Returning something more meaningful is better.

The only thing 'more meaningful' would be to return the value from the nifti_1_header struct, but then it'd be the only field from that, with everything else from nifti_image. I actually think this would be worse at this point, since it's been like this for 4 years now.

Finally, maybe other people were not really directly using NiftiImageIO, so no one noticed the change?

It also affect only some Nifti files. I suspect not too many people are introspecting these metadata fields.

seanm added a commit to seanm/ITK that referenced this issue Mar 14, 2022
…iftiImageIO metadata

bug InsightSoftwareConsortium#3241: In f38b1dd I accidentally changed some behaviour. Specifically, the value of pixdim[0] in itk::NiftiImageIO metadata is different before and after the change.

Previous to the change, SetImageIOMetadataFromNIfTI() was populated from the nifti_1_header structure. After the change, it is populated from the nifti_image structure. The latter is created from the former with nifti_convert_nhdr2nim(). Most of the fields used in SetImageIOMetadataFromNIfTI() have 1-to-1 correspondences in these two structures. I expected all 8 elements of the pixdim array were the same in both structures, but in fact no; element 0 is special. nifti_convert_nhdr2nim() unconditionally sets pixdim[0] to 0.0 (by virtue of allocating the structure with calloc).

In my own app, I was using pixdim[0] (retrieved from itk::NiftiImageIO) as the last parameter to nifti_quatern_to_mat44(). I see now looking at niftilib itself that I should be passing qfac as the last parameter.  But the itk::MetaDataObject doesn't include it, so this commit adds qfac to the metadata dictionary.

Additionally, we now include the whole qto_xyz matrix, so that ITK clients that call nifti_quatern_to_mat44() don't even need to.  This plugs a leaky abstraction and allows my own app for example to not even know that ITK is implemented via niftilib, and never need to call down to it.

Note that the behaviour change of f38b1dd is not being reverted, because it's been four years now, no one else has noticed, and some people may be relying on this behaviour now.
seanm added a commit to seanm/ITK that referenced this issue Mar 16, 2022
…iftiImageIO metadata

bug InsightSoftwareConsortium#3241: In f38b1dd I accidentally changed some behaviour. Specifically, the value of pixdim[0] in itk::NiftiImageIO metadata is different before and after the change.

Previous to the change, SetImageIOMetadataFromNIfTI() was populated from the nifti_1_header structure. After the change, it is populated from the nifti_image structure. The latter is created from the former with nifti_convert_nhdr2nim(). Most of the fields used in SetImageIOMetadataFromNIfTI() have 1-to-1 correspondences in these two structures. I expected all 8 elements of the pixdim array were the same in both structures, but in fact no; element 0 is special. nifti_convert_nhdr2nim() unconditionally sets pixdim[0] to 0.0 (by virtue of allocating the structure with calloc).

In my own app, I was using pixdim[0] (retrieved from itk::NiftiImageIO) as the last parameter to nifti_quatern_to_mat44(). I see now looking at niftilib itself that I should be passing qfac as the last parameter.  But the itk::MetaDataObject doesn't include it, so this commit adds qfac to the metadata dictionary.

Additionally, we now include the whole qto_xyz matrix, so that ITK clients that call nifti_quatern_to_mat44() don't even need to.  This plugs a leaky abstraction and allows my own app for example to not even know that ITK is implemented via niftilib, and never need to call down to it.

Note that the behaviour change of f38b1dd is not being reverted, because it's been four years now, no one else has noticed, and some people may be relying on this behaviour now.
dzenanz pushed a commit that referenced this issue Mar 17, 2022
bug #3241: In f38b1dd I accidentally changed some behaviour. Specifically, the value of pixdim[0] in itk::NiftiImageIO metadata is different before and after the change.

Previous to the change, SetImageIOMetadataFromNIfTI() was populated from the nifti_1_header structure. After the change, it is populated from the nifti_image structure. The latter is created from the former with nifti_convert_nhdr2nim(). Most of the fields used in SetImageIOMetadataFromNIfTI() have 1-to-1 correspondences in these two structures. I expected all 8 elements of the pixdim array were the same in both structures, but in fact no; element 0 is special. nifti_convert_nhdr2nim() unconditionally sets pixdim[0] to 0.0 (by virtue of allocating the structure with calloc).

In my own app, I was using pixdim[0] (retrieved from itk::NiftiImageIO) as the last parameter to nifti_quatern_to_mat44(). I see now looking at niftilib itself that I should be passing qfac as the last parameter.  But the itk::MetaDataObject doesn't include it, so this commit adds qfac to the metadata dictionary.

Additionally, we now include the whole qto_xyz matrix, so that ITK clients that call nifti_quatern_to_mat44() don't even need to.  This plugs a leaky abstraction and allows my own app for example to not even know that ITK is implemented via niftilib, and never need to call down to it.

Note that the behaviour change of f38b1dd is not being reverted, because it's been four years now, no one else has noticed, and some people may be relying on this behaviour now.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances
Projects
None yet
Development

No branches or pull requests

2 participants