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 minc header #712

Open
ltetrel opened this issue Jan 11, 2019 · 9 comments
Open

wrong minc header #712

ltetrel opened this issue Jan 11, 2019 · 9 comments
Labels

Comments

@ltetrel
Copy link

ltetrel commented Jan 11, 2019

Hello all,

So I am trying to convert .mnc images to .nii with nibabel, here is my code :

import nibabel as nib

(...)

# loading the original image
nibImg = nib.load(srcFilePath)
nibAffine = np.array(nibImg.affine)
nibData = np.array(nibImg.dataobj)

# inverting the data axes and rotating the affine to match nii space
nibAffine[0:3, 0:3] = nibAffine[0:3, 0:3] @ rotZ(np.pi/2) @ rotY(np.pi) @ rotX(np.pi/2)
nibData = nibData.T
nibData = np.swapaxes(nibData, 0, 1)

# creating the nifti image
niftiImg = nib.Nifti1Image(nibData, nibAffine, nibImg.header)

The problem is that the header nib.minc2.Minc2Image.header does nto have proper TR information :

In [270]: nibImg.header.get_zooms()
Out[270]: (4.000000000000021, 4.000000000000001, 4.0, 4.0)

When it should be (using mincheader):

acquisition:repetition_time = 2. ;

I started investigated and apparently it has something to do with

zooms[:n_to_set] = vox[:n_to_set]

Thank you for your work,

@effigies
Copy link
Member

Hi @ltetrel., thanks for the report. I'm not sure that's the issue, as the affine should only be setting the first three zooms. If you're familiar with the MINC formats (or at least MINC2), would you care to dig into this and submit a pull request? I suspect the issue is here:

nibabel/nibabel/minc1.py

Lines 95 to 99 in 5ab9414

def get_zooms(self):
""" Get real-world sizes of voxels """
# zooms must be positive; but steps in MINC can be negative
return tuple([abs(float(dim.step)) if hasattr(dim, 'step') else 1.0
for dim in self._dims])

It's not clear to me that we're looking for TR at all. We'll need to find how that's represented in each format, and possibly use two different approaches in MINC1 and MINC2 to return the TR.

@ltetrel
Copy link
Author

ltetrel commented Mar 1, 2019

Hi @effigies,

I am not really familiar with this format sorry.. For now it seems to work with the little trick that I posted, but maybe I am missing something..

Thx,

@effigies effigies added the bug label Jul 29, 2019
@effigies effigies mentioned this issue Jul 29, 2019
12 tasks
@effigies
Copy link
Member

Hi @ltetrel, sorry to be so slow getting back to this one. Sorry to be a bit obtuse, but I'm not sure what little trick you posted that fixed the issue for you. Could you be very explicit?

@ltetrel
Copy link
Author

ltetrel commented Jul 30, 2019

Hi @effigies,
Sorry if my issue was not clear, it has been a while so I will try to be as more clear as possible :)

Actually my main problem was that I had a wrong header information (at least affine and repetition time) when loading a .minc image into nibabel, that "break" my conversion to .nifti.

  1. The affine seemed to be rotated, so I added a little piece of code to reorientate the affine into a good landmark. For that I compared inside a viewer the original volume (.minc), with the transformed volume (.nii) to find the good transformation to realign.>
    I also had to swap some axes but not sure if this is really an issue of loading .minc or just the original format ?
def rot_x(alpha):
    return np.array([[1, 0, 0]
                     , [0, np.cos(alpha), np.sin(alpha)]
                     , [0, -np.sin(alpha), np.cos(alpha)]])

def rot_y(alpha):
    return np.array([[np.cos(alpha), 0, -np.sin(alpha)]
                     , [0, 1, 0]
                     , [np.sin(alpha), 0, np.cos(alpha)]])

def rot_z(alpha):
    return np.array([[np.cos(alpha), np.sin(alpha), 0]
                     , [-np.sin(alpha), np.cos(alpha), 0]
                     , [0, 0, 1]])

nib_affine[0:3, 0:3] = nib_affine[0:3, 0:3] @ rot_z(np.pi/2) @ rot_y(np.pi) @ rot_x(np.pi/2)
nib_data = nib_data.T
nib_data = np.swapaxes(nib_data, 0, 1)
nifti_img = nib.Nifti1Image(nib_data, nib_affine, nib_img.header)
  1. The loaded repetition time seemed to be wrong
In [270]: nibImg.header.get_zooms()
Out[270]: (4.000000000000021, 4.000000000000001, 4.0, 4.0)

When it should be (with minc toolkit):

acquisition:repetition_time = 2. ;

So in this case I had to manually overwrite the repetition time:

nifti_img.header.set_xyzt_units(xyz="mm", t="sec")
zooms = np.array(nifti_img.header.get_zooms())
zooms[3] = self._config["repetitionTimeInSec"]
nifti_img.header.set_zooms(zooms)

@effigies effigies added this to the 2.5.1 milestone Aug 2, 2019
@effigies effigies mentioned this issue Sep 15, 2019
11 tasks
@effigies
Copy link
Member

Sorry for being so slow to get back to this.

Looking at the file format reference, it indicates that acquisition:repetition_time is optional, but the time dimension variable would have a mandatory step attribute, and this is almost certainly what we would use. Is it possible that there's an issue with your MINC file?

If you're sure that it's well-formed, would you be willing to share it (with data zeroed-out and truncated to, say, 5 volumes) so we could make it part of the test suite?

@ltetrel
Copy link
Author

ltetrel commented Sep 16, 2019

Hi @effigies,

I don't know if there is an issue with this file since I am not a frequent user of minc files, I just can say that I have no artifacts when I see it, and the header seems normal.
So I will send you two sample volumes from ADNI that I zero-filled with mincmath -mult -constant 0 Resting_out.mnc. I am not sure how to get just 5 volumes from a minc files so I didn't do it.

So to test it, I think the idea would be to compare the output from mincheader with the output from nibabel after loading the file. I will mail you and cc a T1 file and a bold one,

hope this will help,

@effigies
Copy link
Member

Hi @ltetrel, you can send an attachment directly to this username @ gmail. GitHub will strip attachments.

@effigies effigies removed this from the 2.5.1 milestone Sep 19, 2019
@crocodoyle
Copy link

I'm pretty sure that there are multiple conventions used by minc files and that's the source of a lot of the confusion. I tried to dig into this a while ago, and it is possible to have a variable step size per dimension (not just time), and spatial dimensions (1mm per voxel, say), sometimes have a giant array of [1, 1, 1, 1, 1, 1, ..., 1]. Sometimes they just have a single value (1). I think many tools do not quite respect the official minc spec as well

@effigies
Copy link
Member

Does the standard specify an order of precedence? If so, we can work with that. It's not an easy thing to read through. A battery of example files and the correct interpretation of them would go a long way.

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