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

Samuel St-Jean stjeansam at gmail.com
Mon Jul 11 02:48:31 EDT 2016


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.
So, two questions :
- Is there any adverse effect to using original headers if I know for 
certain nothing changed except dtype?
- 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.

Samuel

P.S. Quick snippet to reproduce flipping with fslorient, adapt filename 
of course but any random nifti should work, this was diffusion data. 
Check header with fslhd afterwards.

fslorient -swaporient 'out.nii.gz'

In [1]: import nibabel as nib

In [3]: vol=nib.load('out.nii.gz')

In [4]: data=vol.get_data()

In [5]: aff=vol.get_affine()

In [6]: hdr=vol.get_header()

In [7]: nib.save(nib.Nifti1Image(data,aff,hdr),'out2.nii.gz')

In [8]: nib.save(nib.Nifti1Image(data,aff),'out3.nii.gz')


2016-06-28 15:13 GMT+02:00 Matthew Brett <matthew.brett at gmail.com 
<mailto:matthew.brett at gmail.com>>:

    Hi,

    On Mon, Jun 27, 2016 at 11:50 PM, Samuel St-Jean
    <stjeansam at gmail.com <mailto:stjeansam at gmail.com>> wrote:
     > I'll need to play a bit with it today and ask some guy about the
    flipping, I
     > am reporting for someone else actually. So, it seems to be
    harmless to put
     > back the header in the case of unchanged affine (as it is in my
    situation),
     > could be that other software downstream does not really like
    something else.
     > I did not reuse it because of dtypes, voxelsize and other things
    which may
     > wildly change on processed data.
     >
     >
     > Anyway, this example is using your case number 1, no header,
    affine is
     > gotten from get_affine. Of course it was not modified during the
    processing,
     > and I end up with a different sform/qform. Accoridng to your
    first point, I
     > expected them to be identical, but I end up with a sligthly
    different (and
     > diagonal) qform. They also have different names, so ifthey were
    set to the
     > same thing I would expect them to be pristine copies.
     >
     > That's a bit troublesome now as JC pointed out, since we all use
    the same
     > set of scripts for the  heavy processing, which don't reuse
    headers as I can
     > remember. Does not seem to cause any problem for us, don't want
    to receive
     > hate mail by other people using some of our stuff and saying we
    broke their
     > data though.
     >
     > Are sform and qform set to match the affine in exactly the same
    way, or does
     > some transformation is applied and then they are identical? I
    went through
     > the info on the nibabel website about headers and affine, so
    maybe I just
     > missed it also if anyone has other info to add.

    Well - remember that the qform is stored differently from the sform.
    In particular, the qform cannot store shears, because it's the
    composition of the translations on a pure rotation on the zooms,
    whereas the sform can, because it stores a full 3 x 4 affine matrix.

    The sform and the qform will have different so-called "transform
    codes", if the sform and qform both get set (rules as described in my
    last email).  This is because the default transform codes are
    different for the sform and qform, and we use the defaults.

    The flip should always be the same though, when the sform and qform
    get set together - at least if the external software is reading the
    header correctly.

    Cheers,

    Matthew
    _______________________________________________
    Neuroimaging mailing list
    Neuroimaging at python.org <mailto: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/6b930a8a/attachment.html>


More information about the Neuroimaging mailing list