InsightSoftwareConsortium / SimpleITK-Notebooks

Jupyter notebooks for learning how to use SimpleITK
Apache License 2.0
838 stars 349 forks source link

Image origin changes in a saved image #433

Closed michalm248 closed 11 months ago

michalm248 commented 11 months ago

I have an image with non-integer origin values. When the image is padded by several pixels in all directions (using sitk.ConstantPad), the origin value changes accordingly. When the image is then saved and read again from the saved file, the origin values change. Example below.

What could be causing this behavior? I've tried to search and haven't found anything to clarify it. I'm using SimpleITK 2.2.1 on Windows

Thank you!

Example

---> image parameters: img.GetOrigin() (-132.4556884765625, 151.34652709960938, -74.49024963378906) img.GetSpacing() (1.0, 1.0, 1.0) np.asarray(img_pad.GetDirection()).reshape(3,3) array([[ 1.00000000e+00, -1.51727756e-06, 5.28644705e-09], [-1.51727750e-06, -9.99975721e-01, 6.96824798e-03], [ 5.28644772e-09, 6.96824798e-03, 9.99975721e-01]])

---> padding the image: img_pad=sitk.ConstantPad(img, [10,10,10], [10,10,10]) img_pad.GetOrigin() (-142.4556733566399, 161.27661700718514, -84.55968938114701) # origin is changed

---> after saving and reading from the file: img_pad_in.GetOrigin() (-142.45567321777344, 161.276611328125, -84.5596923828125)

zivy commented 11 months ago

Hello @michalm248,

Not exactly sure I understood the question, but here goes.

Padding changes the image origin, which is the expected behavior. When you write an image to disk and then read the image back, the origin does not change, which is also the expected behavior. This seems to be what is happening in your code (this is a guess as the provided snippets are not enough for reproduction).

Please see code below. If this isn't what you are doing, please modify the code to reproduce the issue:

import SimpleITK as sitk

img = sitk.Image([10,20,30], sitk.sitkUInt8)
img.SetOrigin((-132.4556884765625, 151.34652709960938, -74.49024963378906))

# pad the original image, changes the origin as seen in the printout
img_pad = sitk.ConstantPad(img,[10]*3, [10]*3)
print(f"original origin: {img.GetOrigin()}")
print(f"padded origin: {img_pad.GetOrigin()}")

# save to disk, read back and then print the origin of the padded image
file_name = "padded_image.nrrd"
sitk.WriteImage(img_pad, file_name)
img_pad_read = sitk.ReadImage(file_name)

print(f"padded origin read from disk: {img_pad_read.GetOrigin()}")
michalm248 commented 11 months ago

Hello @zivy , your code simulates well what I'm doing, except I don't create the image, it is read from a file. (I omitted lines reading from my local paths). However, in my image the direction matrix is not identity; it's given above. I add below code modified to reflect my situation. The issue I'm describing is that the output of the last line IS different after reading back from disk, as shown in my last line, from 6th digit; it comes back as (-142.45567321777344, 161.276611328125, -84.5596923828125) We are using NIFTY files; maybe there we are missing something? Thank you

In your code, I have added the direction matrix:

import SimpleITK as sitk

img = sitk.Image([10,20,30], sitk.sitkUInt8) img.SetOrigin((-132.4556884765625, 151.34652709960938, -74.49024963378906))

mat = [ 1.00000000e+00, -1.51727756e-06, 5.28644705e-09, -1.51727750e-06, -9.99975721e-01, 6.96824798e-03, 5.28644772e-09, 6.96824798e-03, 9.99975721e-01] img.SetDirection(mat)

pad the original image, changes the origin as seen in the printout

img_pad = sitk.ConstantPad(img,[10]3, [10]3) print(f"original origin: {img.GetOrigin()}") print(f"padded origin: {img_pad.GetOrigin()}")

save to disk, read back and then print the origin of the padded image - GET DIFFERENT VALUES

file_name = "padded_image.nrrd" sitk.WriteImage(img_pad, file_name) img_pad_read = sitk.ReadImage(file_name)

print(f"padded origin read from disk: {img_pad_read.GetOrigin()}")

zivy commented 11 months ago

Hello @michalm248,

The missing piece of information was the usage of the nifti file format. While nifti is widely used, I would avoid it when possible. It has some ambiguities and stores the image geometry using float and not double which can introduce inconsistencies.

If you run the following code, the origin will exhibit the discrepancy. If you change the file extension to "nrrd" you will see that there are no differences. If the nrrd file size is an issue, then set the useCompression in WriteImage to True.

import SimpleITK as sitk
import numpy as np

img = sitk.Image([10,20,30], sitk.sitkUInt8)
img.SetOrigin((-132.4556884765625, 151.34652709960938, -74.49024963378906))

mat = [ 1.00000000e+00, -1.51727756e-06, 5.28644705e-09, -1.51727750e-06, -9.99975721e-01, 6.96824798e-03, 5.28644772e-09, 6.96824798e-03, 9.99975721e-01]
img.SetDirection(mat)

# pad the original image, changes the origin as seen in the printout
img_pad = sitk.ConstantPad(img,[10]*3, [10]*3)
print(f"original origin: {img.GetOrigin()}")
print(f"padded origin: {img_pad.GetOrigin()}")

# save to disk, read back and then print the origin of the padded image
file_name = "padded_image.nii"
sitk.WriteImage(img_pad, file_name)
img_pad_read = sitk.ReadImage(file_name)

print(f"padded origin read from disk: {img_pad_read.GetOrigin()}")
michalm248 commented 11 months ago

Hello @zivy

thanks for the clarification; indeed the float format seems to be the issue. We'll have to take care of that in our code.

Many thanks!