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
586 stars 161 forks source link

Issue with Origin Values Changing After Saving and Re-Loading an Image #582

Closed tomo-akiyama closed 3 months ago

tomo-akiyama commented 3 months ago

Hello,

I'm new to using ANTsPy and have encountered an issue across multiple image processing functions, including n4_bias_field_correction, ants.resample_image, and ants.denoise_image, that I'm hoping to get some insights on. After applying these functions to MRA images and saving the processed images using ants.image_write, then reloading them with ants.image_read, I've noticed that the Origin values of the images are altered.

Here is a simplified example of my workflow:

import ants

Image processing (example with n4_bias_field_correction)

img_processed = ants.n4_bias_field_correction(img, rescale_intensities=True, shrink_factor=2)

Saving the processed image

output_path = "path/to/save/image.nii" ants.image_write(img_processed, output_path)

Reloading the processed image

img_reloaded = ants.image_read(output_path)

Observing the change

print(img_processed)

ANTsImage (RPI) Pixel Type : float (float32) Components : 1 Dimensions : (480, 480, 480) Spacing : (0.5, 0.5, 0.5) Origin : (-120.0739, 112.3748, 0.857) Direction : [ 0.9999 0. -0.0169 -0.0021 -0.9923 -0.1241 0.0168 -0.1242 0.9921]

print(img_reloaded)

ANTsImage (RPI) Pixel Type : float (float32) Components : 1 Dimensions : (480, 480, 480) Spacing : (0.5, 0.5, 0.5) Origin : (-118.7191, 122.3064, -78.5125) Direction : [ 0.9999 0. -0.0169 -0.0021 -0.9923 -0.1241 0.0168 -0.1242 0.9921]

This issue doesn't seem to be isolated to n4_bias_field_correction but also occurs with ants.resample_image and ants.denoise_image. Each time, the Origin values before saving and after reloading do not match, leading to alignment problems in subsequent processing steps.

Any advice, insights, or workarounds to maintain consistent Origin values through the image processing pipeline would be greatly appreciated. Thank you very much for your time and assistance.

Best regards,

cookpa commented 3 months ago

It is likely something particular to your data.

>>> im = ants.image_read(ants.get_data('mni'))
>>> im_n4 = ants.n4_bias_field_correction(im)
>>> ants.image_write(im_n4, 'mni_n4.nii.gz')
>>> im_n4_read = ants.image_read('mni_n4.nii.gz')
>>> im_n4_read.origin
(-90.0, 126.0, -72.0)
>>> im_n4.origin
(-90.0, 126.0, -72.0)
>>> im.origin
(-90.0, 126.0, -72.0)

I will look into it if you can share data. It's fine if you want to blank out the voxel values, an image of zeros that can reproduce the problem is OK.

Also, please let us know the details of your system and ANTsPy version

gdevenyi commented 3 months ago

Round trips through ITK with non-standards compliant Nifti files can cause issues.

In addition, make sure to test things with an itk compliant viewer such as itk snap to confirm things actually changed, rather than it being due to a viewer which uses different conventions.

tomo-akiyama commented 3 months ago

@cookpa

Thank you for your response. I'm using Python 3.10.8 with antspyx version 0.3.8, mainly for VMTK purposes. The MRI data in question comes from the freely available dataset (https://brain-development.org/ixi-dataset/). I also tested with Python 3.11.2 and antspyx version 0.4.2, but encountered the same issue with the origin shifting.

IXI002-Guys-0828-MRA.nii.gz

@gdevenyi Thank you for your suggestion. I haven't used ITK-SNAP before and encountered an error while attempting to download it, so I haven't been able to confirm the issue yet. Interestingly, when using ants.plot to overlay images, there doesn't seem to be a misalignment. However, when trying to perform calculations directly between antspy arrays, I encounter errors. ants.plot(img_processed, overlay=img_reloaded, overlay_alpha=0.5) 4c8e3781-1869-4c34-8a0c-cb9940e4d3a0 img_processed * img_reloaded

463 if isinstance(other, ANTsImage): 464 if not image_physical_space_consistency(self, other): --> 465 raise ValueError('images do not occupy same physical space') 466 other = other.numpy() 468 new_array = this_array * other

ValueError: images do not occupy same physical space

ncullen93 commented 3 months ago

The ants.plot function will resample the overlay to the main image if they are not in the same physical space. I guess it's a bit stricter for math operations.

tomo-akiyama commented 3 months ago

@ncullen93 I see, so it automatically resamples to match. Considering that math operations are not working well, it does suggest that the origin changes after reloading.

cookpa commented 3 months ago

ANTsPy 0.5.0 on Mac OS 12.7:

>>> img = ants.image_read('IXI002-Guys-0828-MRA.nii.gz')
>>> img_processed = ants.n4_bias_field_correction(img, rescale_intensities=True, shrink_factor=2)
>>> ants.image_write(img_processed, 'corrected.nii')
>>> corrected = ants.image_read('corrected.nii')
>>> corrected.origin
(-120.07392120361328, 112.37480163574219, 0.8570172786712646)
>>> img.origin
(-120.07392120361328, 112.37480163574219, 0.8570172786712646)
>>> img_processed.origin
(-120.07392120361328, 112.37480163574219, 0.8570172786712646)

0.4.2:

>>> img = ants.image_read('/Users/pcook/Downloads/IXI002-Guys-0828-MRA.nii.gz')
>>> img_processed = ants.n4_bias_field_correction(img, rescale_intensities=True, shrink_factor=2)
>>> ants.image_write(img_processed, 'corrected.nii')
>>> corrected = ants.image_read('corrected.nii')
>>> corrected.origin
(-120.07392120361328, 112.37480163574219, 0.8570172786712646)
>>> img.origin
(-120.07392120361328, 112.37480163574219, 0.8570172786712646)
>>> img_processed.origin
(-120.07392120361328, 112.37480163574219, 0.8570172786712646)

@tomo-akiyama I notice this image IXI002-Guys-0828-MRA.nii.gz has different dimensions to the one you posted earlier

ANTsImage (RPI)
Pixel Type : float (float32)
Components : 1
Dimensions : (480, 480, 480)
Spacing : (0.5, 0.5, 0.5)
Origin : (-120.0739, 112.3748, 0.857)
Direction : [ 0.9999 0. -0.0169 -0.0021 -0.9923 -0.1241 0.0168 -0.1242 0.9921]

is perhaps resampled, when I load the image, I see

>>> img
ANTsImage (RPI)
     Pixel Type : float (float32)
     Components : 1
     Dimensions : (512, 512, 100)
     Spacing    : (0.4688, 0.4688, 0.8)
     Origin     : (-120.0739, 112.3748, 0.857)
     Direction  : [ 0.9999  0.     -0.0169 -0.0021 -0.9923 -0.1241  0.0168 -0.1242  0.9921]

Can you please provide a single code snippet with all the commands you ran to produce the issue?

tomo-akiyama commented 3 months ago

@cookpa

The environment I'm using is Python 3.10.8, macOS 13.6.5, and antspyx 0.4.2.

I overlooked this point. As you pointed out, for future image processing, I had resampled my data to isotropic 0.5mm resolution and additionally added padding. It seems that after adding padding and then writing and reloading the data, the origin has shifted.

While it may seem natural for the origin to shift when adding padding, I'm curious as to why simply adding padding doesn't cause the origin to shift, but writing and reloading does. スクリーンショット 2024-03-20 12 43 11 スクリーンショット 2024-03-20 12 43 21

cookpa commented 3 months ago

Finally, a reproducible example

>>> im = ants.image_read('/Users/pcook/Downloads/IXI002-Guys-0828-MRA.nii.gz')
>>> im
ANTsImage (RPI)
     Pixel Type : float (float32)
     Components : 1
     Dimensions : (512, 512, 100)
     Spacing    : (0.4688, 0.4688, 0.8)
     Origin     : (-120.0739, 112.3748, 0.857)
     Direction  : [ 0.9999  0.     -0.0169 -0.0021 -0.9923 -0.1241  0.0168 -0.1242  0.9921]

>>> padded = ants.pad_image(im, shape=(512,512,512))
>>> padded
ANTsImage (RPI)
     Pixel Type : float (float32)
     Components : 1
     Dimensions : (512, 512, 512)
     Spacing    : (0.5, 0.5, 0.5)
     Origin     : (-120.0739, 112.3748, 0.857)
     Direction  : [ 0.9999  0.     -0.0169 -0.0021 -0.9923 -0.1241  0.0168 -0.1242  0.9921]

>>> ants.image_write(padded, '/tmp/padded.nii.gz')
>>> pad_read = ants.image_read('/tmp/padded.nii.gz')
>>> pad_read
ANTsImage (RPI)
     Pixel Type : float (float32)
     Components : 1
     Dimensions : (512, 512, 512)
     Spacing    : (0.5, 0.5, 0.5)
     Origin     : (-126.5826, 131.2544, -85.5906)
     Direction  : [ 0.9999  0.     -0.0169 -0.0021 -0.9923 -0.1241  0.0168 -0.1242  0.9921]
cookpa commented 3 months ago

Immediate workaround: write and re-read the image after padding. The updated origin will keep physical space aligned to the original.

image
tomo-akiyama commented 3 months ago

@cookpa I will try in your way. Thank you.

Thank you, everybody.