Note on orientation: sform matrix set

Hello,

I am a new fmriprep user. I’ve seen in the html report a note that says “Note on orientation: sform matrix set.” Below, it says “The sform has been copied from qform.”

The preprocessing seems to have run correctly, but I am not sure if I should be concerned of this warning. Can someone explain under which circumstances this is generated?

I’ve looked at the sform and qform of the input data files with fslhd and they are nearly identical. I’ve posted them below in case there is something clearly off there. These nifti files were generated with dcm2niix from the raw dicoms.

qto_xyz:1 -2.991201 -0.025376 0.228191 96.067787
qto_xyz:2 0.000000 2.981620 0.331576 -61.152069
qto_xyz:3 0.229597 -0.330603 2.972875 -20.513903
qto_xyz:4 0.000000 0.000000 0.000000 1.000000

sto_xyz:1 -2.991202 -0.025376 0.228185 96.067787
sto_xyz:2 0.000000 2.981620 0.331571 -61.152069
sto_xyz:3 0.229594 -0.330603 2.972838 -20.513903
sto_xyz:4 0.000000 0.000000 0.000000 1.000000

I’d appreciate any advice!

Best,
Anthony

This is an advisory warning, and it’s intended to prompt you to look at your qform/sform matrices and make sure you understand what’s happening. Specifically, it’s advising you that the qform is being taken as canonical, which probably means that your sform code was 0, and the qform and qform code are copied to the sform.

This step is to ensure that all software treats the images as having the same orientation. We’ve found that anything short keeping of the two affines synchronized invites bugs when moving from one software suite to the next.

Thank you for the quick reply! The sform_code of the BOLD file that I input to fmriprep is 1 (as is the qform_code). Does that mean that this warning is generated from some other file that is created during the processing pipeline? Here is the complete fslhd output for the nifti file converted from dcm2niix:

sizeof_hdr 348
data_type INT16
dim0 4
dim1 64
dim2 64
dim3 36
dim4 339
dim5 1
dim6 1
dim7 1
vox_units mm
time_units s
datatype 4
nbyper 2
bitpix 16
pixdim0 -1.000000
pixdim1 3.000000
pixdim2 3.000000
pixdim3 3.000000
pixdim4 2.000000
pixdim5 0.000000
pixdim6 0.000000
pixdim7 0.000000
vox_offset 352
cal_max 0.000000
cal_min 0.000000
scl_slope 1.000000
scl_inter 0.000000
phase_dim 2
freq_dim 1
slice_dim 3
slice_name Unknown
slice_code 0
slice_start 0
slice_end 0
slice_duration 0.000000
toffset 0.000000
intent Unknown
intent_code 0
intent_name
intent_p1 0.000000
intent_p2 0.000000
intent_p3 0.000000
qform_name Scanner Anat
qform_code 1
qto_xyz:1 -2.991201 -0.025376 0.228191 96.067787
qto_xyz:2 0.000000 2.981620 0.331576 -61.152069
qto_xyz:3 0.229597 -0.330603 2.972875 -20.513903
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 -2.991202 -0.025376 0.228185 96.067787
sto_xyz:2 0.000000 2.981620 0.331571 -61.152069
sto_xyz:3 0.229594 -0.330603 2.972838 -20.513903
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
file_type NIFTI-1+
file_code 1
descrip TE=30;Time=144447.000
aux_file

Should I take this as an indication that there is something wrong with the initial qform/sform?

Thank you again!
Anthony

If you see this, it means the following:

  1. Either one of the codes was zero or the affines differ. In this case, it looks like the affines were different enough to adjust.
  2. Since you see the sform adjusted, that only happens here:

Given that your sform code is non-zero, it means that it determined your sform is invalid. That can happen if the determinant is zero (it’s not) or the scaling factor implied by your sform differs from the expected size based on the zooms (or pixdims). In this case it does:

>>> import numpy as np
>>> sform = np.array([
        [-2.991202, -0.025376, 0.228185, 96.067787],
        [0.000000, 2.981620, 0.331571, -61.152069],
        [0.229594, -0.330603, 2.972838, -20.513903],
        [0.000000, 0.000000, 0.000000, 1.000000]])
>>> RZS = sform[:3, :3]
>>> np.sqrt(np.sum(RZS * RZS, axis=0))
array([3.00000047, 3.00000002, 2.99996225])

This is based on the values that get printed by fslhd, which aren’t going to have the precision of the actual values, but it seems that it’s far enough from your (3mm)^3 voxel size to cause us to prefer the qform, which uses the pixdims as its scale factors.

This is barely in the significant digits, and is likely a situation where the precision of the NIfTI fields is slightly under that needed for exact correspondence of the qform and sform matrices. So it’s probably us being slightly overconservative, but we prefer to warn when we do update headers, rather than do it silently. I don’t see any potential for an error, though.

Thanks so much for the detailed explanation. I really appreciate it!