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

Wrong nifti header when reading a nifti file using SimpleITK #1780

Closed
NirutaDhimal opened this issue Nov 28, 2022 · 7 comments
Closed

Wrong nifti header when reading a nifti file using SimpleITK #1780

NirutaDhimal opened this issue Nov 28, 2022 · 7 comments
Labels

Comments

@NirutaDhimal
Copy link

NirutaDhimal commented Nov 28, 2022

I am using Python 3.9, windows 10 OS and SimpleITK version 2.2.0. I used SimpleITK to load nifti image and view its header information. The pixdim[0] header is changed from -1 or 1 to 0. I used ITKsnap, nifti-tool and nibabel to see the header information and they all show -1 or 1.
I understand that this issue was recently fixed in ITK and this shouldn't be a problem in the version I am using.
https://github.com/InsightSoftwareConsortium/ITK/commit/9eb4c1209d215eed244e5e0a1d8f15942a61cb76#diff-fa33a46a6dc41cde5d18ee8f63cb941794512e03c6377340b8d110fe584cfdfa

I am willing to provide you the deidentified data. Please fix this or point me to a direction where I can fix this issue for my code.

This is the code that I used.
`brain_ncct_img = sitk.ReadImage(ncct_file)
brain_ncct_data = sitk.GetArrayFromImage(brain_ncct_img)

print("header using simpleitk")
for k in brain_ncct_img.GetMetaDataKeys():
v = brain_ncct_img.GetMetaData(k)
print("({0}) = = "{1}"".format(k,v))
`

This is the result I am getting.
header using simpleitk (ITK_FileNotes) = = "TE=3e+02;Time=172913.193" (ITK_original_direction) = = "[UNKNOWN_PRINT_CHARACTERISTICS] " (ITK_original_spacing) = = "[UNKNOWN_PRINT_CHARACTERISTICS] " (aux_file) = = "" (bitpix) = = "16" (cal_max) = = "0" (cal_min) = = "0" (datatype) = = "4" (descrip) = = "TE=3e+02;Time=172913.193" (dim[0]) = = "3" (dim[1]) = = "512" (dim[2]) = = "512" (dim[3]) = = "30" (dim[4]) = = "1" (dim[5]) = = "0" (dim[6]) = = "0" (dim[7]) = = "0" (dim_info) = = "0" (intent_code) = = "0" (intent_name) = = "" (intent_p1) = = "0" (intent_p2) = = "0" (intent_p3) = = "0" (nifti_type) = = "1" (pixdim[0]) = = "0" (pixdim[1]) = = "0.443359" (pixdim[2]) = = "0.443359" (pixdim[3]) = = "4.79859" (pixdim[4]) = = "0" (pixdim[5]) = = "0" (pixdim[6]) = = "0" (pixdim[7]) = = "0" (qform_code) = = "1" (qform_code_name) = = "NIFTI_XFORM_SCANNER_ANAT" (qoffset_x) = = "81.2783" (qoffset_y) = = "99.1478" (qoffset_z) = = "-85.4728" (quatern_b) = = "0" (quatern_c) = = "0.997859" (quatern_d) = = "0.0654031" (scl_inter) = = "-1024" (scl_slope) = = "1" (sform_code) = = "1" (sform_code_name) = = "NIFTI_XFORM_SCANNER_ANAT" (slice_code) = = "0" (slice_duration) = = "0" (slice_end) = = "0" (slice_start) = = "0" (srow_x) = = "-0.443359 0 -0 81.2783" (srow_y) = = "-0 0.439566 -0.626342 99.1478" (srow_z) = = "0 0.05787 4.75754 -85.4728" (toffset) = = "0" (vox_offset) = = "352" (xyzt_units) = = "10"

@dave3d
Copy link
Member

dave3d commented Nov 28, 2022

I wrote a little test nifti generator:

import SimpleITK as sitk

img = sitk.GaussianSource(sitk.sitkUInt8, [100,100,100], [20,20,20], [50,50,50])
img.SetSpacing([-2, 3, 4])
print(img)
sitk.WriteImage(img, "test.nii.gz")

Then, when I load it and print out the metadata, I get this:

 pixdim[0] :  0
 pixdim[1] :  2
 pixdim[2] :  3
 pixdim[3] :  4

Looking at the Nifti spec (https://nifti.nimh.nih.gov/pub/dist/src/niftilib/nifti1.h), the pixel spacings start at pixdim[1], which is what I see and what your output shows.

@zivy zivy added the Question label Dec 3, 2022
@NirutaDhimal
Copy link
Author

NirutaDhimal commented Dec 4, 2022

Can you try using this data and see the pixdim[0] value? The pixdim[1] value is correct and I am talking about pixdim[0]. why is it setting pixdim[0] as 0 when we clearly have its value as -1 in the nifti header? shouldn't it be -1?
https://drive.google.com/file/d/1Rmfkw7NmDh1Ljset9bp-xIFferZJcjNC/view?usp=share_link

@dave3d
Copy link
Member

dave3d commented Dec 13, 2022

I've requested access to the file.

@dave3d
Copy link
Member

dave3d commented Dec 19, 2022

So I wrote a little test program:

import nibabel as nib
import SimpleITK as sitk

# Load the initial file using NiBabel
filename = "1239-011_20220517172346_BL-NCCT.nii"
img = nib.load(filename)

# Print out pixdim
print("\nfile:", filename)
print("nibabel pixdim:", img.header['pixdim'])

# Load the file with SimpleITK and print pixdim
sitk_img = sitk.ReadImage(filename)
pixdim = []
for i in range(ord('0'), ord('8')):
    key = "pixdim[" + chr(i) + "]"
    pixdim.append( sitk_img.GetMetaData(key) )
print("\nSimpleITK pixdim:", pixdim)

# Write out a new Nifti file
sitk_filename = "1239-sitk.nii"
sitk.WriteImage(sitk_img, sitk_filename)

# Load the new file with NiBabel and print pixdim
img2 = nib.load(sitk_filename)
print("\nfile:", sitk_filename)
print("nibabel pixdim:", img2.header['pixdim'])

It does 3 things:

  1. Load the file with NiBabel and print pixdim
  2. Load the file with SimpleITK, print pixdim, write out a new Nifti file using SimpleITK
  3. Load the new Nifti file with NiBabel and print pixdim

Here are the output results:

file: 1239-011_20220517172346_BL-NCCT.nii

nibabel pixdim: [-1.          0.44335938  0.44335938  4.7985935   0.          0.
  0.          0.        ]

SimpleITK pixdim: ['0', '0.443359', '0.443359', '4.79859', '0', '0', '0', '0']

file: 1239-sitk.nii
nibabel pixdim: [-1.          0.44335938  0.44335938  4.7985935   0.          0.
  0.          0.        ]

So it seems to me that, although SimpleITK doesn't keep pixdim[0] internally properly, when it writes out the Nifti, it is being set to the proper value. It seems that pixdim[0] has to do with how the quaternion transform is handled when loading the Nifti. But once SimpleITK has loaded the image, that doesn't really matter, since SimpleITK doesn't use that quaternion. Once SimpleITK writes out a new Nifti, the pixdim[0] value does seem to have the proper value.

@NirutaDhimal
Copy link
Author

Thanks for helping me in this. I am creating a montage. I am reading the nifti file using simpleitk and using the pixel array to make the montage using matplotlib. So what's happening is if pixdim[0] is -1 the image is upside down so I will need to flip it upside down. I am currently using nibabel to get the pixdim[0] value. So my question is if the image has pixdim[0] value -1 or 1 and if simpleitk loads this image, shouldn't it have the same headers as that of the image? There may be case where we read a nifti file using simpleitk and perform some operations on the pixel data and save it in another format(not nifti). Can you fix this?

@dave3d
Copy link
Member

dave3d commented Dec 20, 2022

Can you share with me your code? And any example dataset that is not being displayed in the proper orientation?

I can try and re-writing your montage code in SimpleITK and see if that fixes it.

Also, if you want to try another file format, I'd suggest NRRD. Nifti is an old, and rather odd format. NRRD is more straightforward and consistent.

@dave3d
Copy link
Member

dave3d commented Jan 3, 2023

Another possibility is to try DICOMOrientImageFilter. Here's the documentation:

https://simpleitk.org/doxygen/latest/html/classitk_1_1simple_1_1DICOMOrientImageFilter.html

@zivy zivy closed this as completed Apr 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants