nipype
                                
                                 nipype copied to clipboard
                                
                                    nipype copied to clipboard
                            
                            
                            
                        Reorient node performs unwanted reslicing
Summary
Hi fellow Nipype users,
Expected behavior
I’m currently experiencing trouble in my Nipype pipeline using the Reorient node from the nipype.interfaces.image module. I want to perform LPS reorientation on an MPRAGE image with
dim_x 128 size_x 1.7500000 mm orientation_x left to right dim_y 128 size_y 1.7500000 mm orientation_y posterior to anterior dim_z 52 size_z 5.4729691 mm orientation_z inferior to superior (RAI Code: LPI)
Actual behavior
Unexpectedly the output file now has the following characteristics
dim_x 128 size_x 2.0000000 mm orientation_x right to left dim_y 128 size_y 2.0000000 mm orientation_y anterior to posterior dim_z 52 size_z 2.0000000 mm orientation_z inferior to superior (RAI Code: RAI)
Reorientation of the axis is as wanted but it appears that also reslicing to 2x2x2mm was performed leaving the image squished on the z axis.
The documentation clearly states that “No resampling is performed, and thus the output image is not de-obliqued or registered to any other image or template.” https://nipype.readthedocs.io/en/latest/api/generated/nipype.interfaces.image.html 1
Script/Workflow details
The in_file comes straight from selectfiles and out_file goes directly to datasink. So no other processing happened. Also I’ve been using similar reorientation on other images multiple times and all worked fine.
On Neurostars effigies asked me to post the header binary. For the input MPRAGE image this is:
b'\\\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00r\x00\x03\x00\x80\x00\x80\x004\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00 \x00\x00\x00\x00\x00\x80?\x00\x00\xe0?\x00\x00\xe0?\x90"\xaf@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\x7f\x00\x00\xc0\x7f\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<undefined>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00n+1\x00'
After LPS reorientation the header then is as follows:
b'\\\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00r\x00\x03\x00\x80\x00\x80\x004\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00 \x00\x00\x00\x00\x00\x80?\x00\x00\x00@\x00\x00\x00@\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\x7f\x00\x00\xc0\x7f\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<undefined>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80?\x00\x00~C\x00\x00~C\x00\x00\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00~C\x00\x00\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00~C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00n+1\x00'
I’m using Nipype version 1.5.0 with python 3.6 on Linux-centos6.
Platform details:
{'commit_hash': '%h',
 'commit_source': 'archive substitution',
 'networkx_version': '2.4',
 'nibabel_version': '3.1.0',
 'nipype_version': '1.5.0',
 'numpy_version': '1.18.5',
 'pkg_path': '/fast/users/behlandj_c/work/miniconda/envs/nipype_py3.6/lib/python3.6/site-packages/nipype',
 'scipy_version': '1.4.1',
 'sys_executable': '/fast/users/behlandj_c/work/miniconda/envs/nipype_py3.6/bin/python',
 'sys_platform': 'linux',
 'sys_version': '3.6.10 | packaged by conda-forge | (default, Apr 24 2020, '
                '16:44:11) \n'
                '[GCC 7.3.0]',
 'traits_version': '6.1.0'}
Ah, the issue here is that your original header is:
>>> print(nb.Nifti1Image.from_bytes(pre_bytes).header)
<class 'nibabel.nifti1.Nifti1Header'> object, endian='<'
sizeof_hdr      : 348
data_type       : b''
db_name         : b''
extents         : 0
session_error   : 0
regular         : b'r'
dim_info        : 0
dim             : [  3 128 128  52   0   0   0   0]
intent_p1       : 0.0
intent_p2       : 0.0
intent_p3       : 0.0
intent_code     : none
datatype        : float32
bitpix          : 32
slice_start     : 0
pixdim          : [1.       1.75     1.75     5.472969 0.       0.       0.       0.      ]
vox_offset      : 0.0
scl_slope       : nan
scl_inter       : nan
slice_end       : 0
slice_code      : unknown
xyzt_units      : 10
cal_max         : 0.0
cal_min         : 0.0
slice_duration  : 0.0
toffset         : 0.0
glmax           : 0
glmin           : 0
descrip         : b'<undefined>'
aux_file        : b''
qform_code      : unknown
sform_code      : scanner
quatern_b       : 0.0
quatern_c       : 0.0
quatern_d       : 0.0
qoffset_x       : 0.0
qoffset_y       : 0.0
qoffset_z       : 0.0
srow_x          : [2. 0. 0. 0.]
srow_y          : [0. 2. 0. 0.]
srow_z          : [0. 0. 2. 0.]
intent_name     : b''
magic           : b'n+1'
Note that your pixdims don't match your sform columns, and you don't have a qform matrix. So when you go to set the affine in the new image, all it has to go on is the sform:
>>> print(nb.Nifti1Image.from_bytes(post_bytes).header)
<class 'nibabel.nifti1.Nifti1Header'> object, endian='<'
sizeof_hdr      : 348
data_type       : b''
db_name         : b''
extents         : 0
session_error   : 0
regular         : b'r'
dim_info        : 0
dim             : [  3 128 128  52   0   0   0   0]
intent_p1       : 0.0
intent_p2       : 0.0
intent_p3       : 0.0
intent_code     : none
datatype        : float32
bitpix          : 32
slice_start     : 0
pixdim          : [1. 2. 2. 2. 0. 0. 0. 0.]
vox_offset      : 0.0
scl_slope       : nan
scl_inter       : nan
slice_end       : 0
slice_code      : unknown
xyzt_units      : 10
cal_max         : 0.0
cal_min         : 0.0
slice_duration  : 0.0
toffset         : 0.0
glmax           : 0
glmin           : 0
descrip         : b'<undefined>'
aux_file        : b''
qform_code      : unknown
sform_code      : aligned
quatern_b       : 0.0
quatern_c       : 0.0
quatern_d       : 1.0
qoffset_x       : 254.0
qoffset_y       : 254.0
qoffset_z       : 0.0
srow_x          : [ -2.   0.   0. 254.]
srow_y          : [  0.  -2.   0. 254.]
srow_z          : [0. 0. 2. 0.]
intent_name     : b''
magic           : b'n+1'
This is a pretty strange case where generic tools are not going to be able to reliably predict the intent of the user, so you're going to need to write your own interface that does the appropriate things to the pixdims, qform and sform.
Hi effigies,
Thank you so much for your help.
Could you suggest a way to go about writing such an interface? Is there documentation on this? I haven't worked with qform and sfrom before.
Best