[Neuroimaging] [nibabel] sform/qform flipping left - right in the affine and (possibly) fsl

Samuel St-Jean stjeansam at gmail.com
Mon Jul 11 04:59:46 EDT 2016


I guess I got my homogeneous coordinate wrong, haven't touched these in 5
years or so.
I think in the end I'll go with saving the original header, that way I
won't have to play with setting q-form name codes, even though it is still
intact but misread. Should also prevent unforeseen weird stuff with other
tools this way after all.


If only everyone would follow the same conventions, one can dream I guess.
I still wonder why fslhd changes the q-form that way, seems like they
(unintentionally) flips the data themselves in their tools, guess I'll try
asking them.
Hopefully I won't get angry emails that now I flip people data when it was
fine before though.

Thanks for the detailed explanation.

2016-07-11 10:29 GMT+02:00 Matthew Brett <matthew.brett at gmail.com>:

> Hi,
>
> On Sun, Jul 10, 2016 at 11:48 PM, Samuel St-Jean <stjeansam at gmail.com>
> wrote:
> > So, if nibabel reads and write everything correctly, must be something
> down
> > the fsl path doing non cool stuff. Question now is if I should bother
> fixing
> > it and risk breaking stuff for other people as unseen side effects.
> > At least I learned stuff, had no idea qform can not store all possible
> > transformations, so that explains why it differs slightly from the sform
> > after saving back.
> >
> > ---------
> >
> > And one week later (this above was an earlier draft) I got around to
> > actually fix it, but I cannot explain why. If I save back the header with
> > it, I do not experience the flip. If I tell nibabel to create an header
> > because I did not pass anything, it seems to put back my data in RAS
> (think
> > I saw this on the website, but I though it was only for internal
> > representation).
> >
> > So, saving back header + I change the dtype manually (since the dtype
> from
> > the header is used, which is almost always int16 to float32 for me) does
> not
> > flip anything. Although I still can't figure out why the created header
> > gives me flipped data, here is a small excerpt of said header.
> >
> > Nibabel saved back original header
> >
> > qform_name     Scanner Anat
> > qform_code     1
> > qto_xyz:1      -1.796652  0.000000  -0.000000  112.420448
> > qto_xyz:2      0.000000  1.795833  -0.054337  -90.885376
> > qto_xyz:3      0.000000  0.054236  1.799180  54.572971
> > qto_xyz:4      0.000000  0.000000  0.000000  1.000000
> > qform_xorient  Right-to-Left
> > qform_yorient  Posterior-to-Anterior
> > qform_zorient  Inferior-to-Superior
> > sform_name     Scanner Anat
> > sform_code     1
> > sto_xyz:1      -1.796652  0.000000  0.000000  112.420448
> > sto_xyz:2      0.000000  1.795833  -0.054337  -90.885376
> > sto_xyz:3      0.000000  0.054236  1.799180  54.572971
> > sto_xyz:4      0.000000  0.000000  0.000000  1.000000
> > sform_xorient  Right-to-Left
> > sform_yorient  Posterior-to-Anterior
> > sform_zorient  Inferior-to-Superior
> >
> > Nibabel recreated header
> >
> >
> > qform_name     Unknown
> > qform_code     0
> > qto_xyz:1      1.796652  0.000000  0.000000  0.000000
> > qto_xyz:2      0.000000  1.796652  0.000000  0.000000
> > qto_xyz:3      0.000000  0.000000  1.800000  0.000000
> > qto_xyz:4      0.000000  0.000000  0.000000  1.000000
> > qform_xorient  Left-to-Right
> > qform_yorient  Posterior-to-Anterior
> > qform_zorient  Inferior-to-Superior
> > sform_name     Aligned Anat
> > sform_code     2
> > sto_xyz:1      -1.796652  0.000000  0.000000  112.420448
> > sto_xyz:2      0.000000  1.795833  -0.054337  -90.885376
> > sto_xyz:3      0.000000  0.054236  1.799180  54.572971
> > sto_xyz:4      0.000000  0.000000  0.000000  1.000000
> > sform_xorient  Right-to-Left
> > sform_yorient  Posterior-to-Anterior
> > sform_zorient  Inferior-to-Superior
>
> > Notice how the qform has shearing in the first case (and original also),
> > while it has flipped orientation and no shearing in the recreated header
> > case.
>
> I don't think the qform does have shearing, it has a small rotation -
> see https://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations .
>
> Playing with nibabel and fsl, I think what is happening is the FSL is
> doing something weird to the qform, when the qform code is 0 -
> 'unknown'.  The qform is correctly stored in the header, according to
> nibabel.  This script:
>
> ```
> import numpy as np
> np.set_printoptions(suppress=True, precision=4)
>
> import nibabel as nib
>
> aff = np.array([[-1.796652, 0.000000, 0.000000,  112.420448],
>                 [ 0.000000, 1.795833, -0.054337,  -90.885376 ],
>                 [ 0.000000, 0.054236, 1.799180,  54.572971],
>                 [ 0.000000, 0.000000, 0.000000, 1.000000]])
> data = np.zeros((2, 3, 4))
>
> img = nib.Nifti1Image(data, aff)
> nib.save(img, 'out.nii')
>
> img2 = nib.load('out.nii')
> print(img2.get_qform())
> ```
>
> gives:
>
> [[  -1.7967    0.        0.0008  112.4204]
>  [  -0.        1.7958   -0.0543  -90.8854]
>  [   0.0008    0.0542    1.7992   54.573 ]
>  [   0.        0.        0.        1.    ]]
>
> whereas `fslhd` on the same file gives the same output as you got:
>
> qto_xyz:1      1.796652  0.000000  0.000000  0.000000
> qto_xyz:2      0.000000  1.796652  0.000000  0.000000
> qto_xyz:3      0.000000  0.000000  1.800000  0.000000
> qto_xyz:4      0.000000  0.000000  0.000000  1.000000
>
> > So, two questions :
> > - Is there any adverse effect to using original headers if I know for
> > certain nothing changed except dtype?
>
> Nothing that I can think of.
>
> > - Well, I still can't figure out the flipping (is it the same when
> > transformation would be applied in world space? So it still is flipped
> for
> > all software in image space in this case then)
> > or is that some nibabel RAS convention? I am still confused as to what
> > happened to the orientation for the recreated header.
>
> I think the difference must be in the interpretation that [whatever
> software shows the flip] gives to a header with a qform with code 0
> (unknown).   If you want to solve this, you might want to set the
> qform explicitly, with something like 'img.set_qform(affine,
> 'aligned')' before you save the image.
>
> Cheers,
>
> Matthew
> _______________________________________________
> Neuroimaging mailing list
> Neuroimaging at python.org
> https://mail.python.org/mailman/listinfo/neuroimaging
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/neuroimaging/attachments/20160711/e41a9a81/attachment.html>


More information about the Neuroimaging mailing list