Sct_crop_image ValueError: w2 should be positive

Hello,

I am attempting to use sct_crop_image to crop a 4D image, but whether I pass the mask or use -xmax/-xmin/etc. to manually define the bounds, I get the following error. This command works reliably on other datasets. Do you have any insight into how to fix this? Happy to provide the dataset by some secure method if necessary.

Thanks,

-Alan

Spinal Cord Toolbox (5.4)

sct_crop_image -i fmri_ss75/im.nii.gz -m fmri_ss75/im_mean_mask.nii.gz -o fmri_ss75/im_crop.nii.gz -v 2
--

Loaded fmri_ss75/im.nii.gz (path redacted) orientation RPI shape (112, 112, 24, 400)
Loaded fmri_ss75/im_mean_mask.nii.gz (path redacted) orientation RPI shape (112, 112, 24)
Bounding box: x=[28, 80], y=[48, 100], z=[0, 24]
Cropping the image...
Traceback (most recent call last):
  File "/usr/local/sct_v5.4.0/spinalcordtoolbox/scripts/sct_crop_image.py", line 180, in <module>
    main(sys.argv[1:])
  File "/usr/local/sct_v5.4.0/spinalcordtoolbox/scripts/sct_crop_image.py", line 166, in main
    img_crop = cropper.crop(background=arguments.b)
  File "/usr/local/sct_v5.4.0/spinalcordtoolbox/cropping.py", line 98, in crop
    new_origin = np.dot(img_out.hdr.get_qform(), [bbox.xmin, bbox.ymin, bbox.zmin, 1])
  File "/usr/local/sct_v5.4.0/python/envs/venv_sct/lib/python3.6/site-packages/nibabel/nifti1.py", line 915, in get_qform
    quat = self.get_qform_quaternion()
  File "/usr/local/sct_v5.4.0/python/envs/venv_sct/lib/python3.6/site-packages/nibabel/nifti1.py", line 890, in get_qform_quaternion
    return fillpositive(bcd, self.quaternion_threshold)
  File "/usr/local/sct_v5.4.0/python/envs/venv_sct/lib/python3.6/site-packages/nibabel/quaternions.py", line 99, in fillpositive
    raise ValueError(f'w2 should be positive, but is {w2:e}')
ValueError: w2 should be positive, but is -4.664622e-07

Output of sct_check_dependencies is:

Spinal Cord Toolbox (5.4)

sct_check_dependencies 
--

SCT info:
- version: 5.4
- path: /usr/local/sct_v5.4.0
OS: osx (Darwin-21.2.0-x86_64-i386-64bit)
CPU cores: Available: 16, Used by ITK functions: 16
RAM: Total: 65536MB, Used: 15915MB, Available: 49615MB
Check Python executable.............................[OK]
  Using bundled python 3.6.13 |Anaconda, Inc.| (default, Feb 23 2021, 12:58:59) 
[GCC Clang 10.0.0 ] at /usr/local/sct_v5.4.0/python/envs/venv_sct/bin/python
Check if data are installed.........................[OK]
Check if colored is installed.......................[OK] (1.4.2)
Check if dipy is installed..........................[OK] (1.4.1)
Check if futures is installed.......................[OK]
Check if h5py is installed..........................[OK] (2.10.0)
Check if Keras (2.1.5) is installed.................[OK] (2.1.5)
Check if ivadomed is installed......................[OK] (2.8.0)
Check if matplotlib is installed....................[OK] (3.3.4)
Check if nibabel is installed.......................[OK] (3.2.1)
Check if numpy is installed.........................[OK] (1.19.5)
Check if onnxruntime (1.4.0) is installed...........[OK] (1.4.0)
Check if pandas is installed........................[OK] (1.1.5)
Check if psutil is installed........................[OK] (5.8.0)
Check if pyqt5 (5.11.3) is installed................[OK] (5.11.3)
Check if pytest is installed........................[OK] (6.2.5)
Check if pytest-cov is installed....................[OK] (2.12.1)
Check if raven is installed.........................[OK]
Check if requests is installed......................[OK] (2.26.0)
Check if requirements-parser is installed...........[OK] (0.2.0)
Check if scipy is installed.........................[OK] (1.5.4)
Check if scikit-image is installed..................[OK] (0.17.2)
Check if scikit-learn is installed..................[OK] (0.24.2)
Check if tensorflow (1.5.0) is installed............[OK] (1.5.0)
Check if torch (1.5.0) is installed.................[OK] (1.5.0)
Check if torchvision (0.6.0) is installed...........[OK] (0.6.0)
Check if xlwt is installed..........................[OK] (1.3.0)
Check if tqdm is installed..........................[OK] (4.62.3)
Check if transforms3d is installed..................[OK] (0.3.1)
Check if urllib3 is installed.......................[OK] (1.26.7)
Check if pytest_console_scripts is installed........[OK]
Check if wquantiles is installed....................[OK] (0.4)
Check if spinalcordtoolbox is installed.............[OK]
Check ANTs compatibility with OS ...................[OK]
Check PropSeg compatibility with OS ................[OK]
Check if figure can be opened with matplotlib.......[OK] (Using GUI backend: 'MacOSX')
Check if figure can be opened with PyQt.............[OK]
Check FSLeyes version...............................[OK] (0.28.3+build0)

Hi @alan.seifert

There seem to be something fishy with the NIfTI header (exception raised by nibabel upon reading the image). Sending us the data would be a necessary step for us to be able to reproduce the error and find the exact cause of the issue.

Cheers

Thanks for the quick reply! Do you have a preferred e-mail address that I can send this to?

Thanks for sending the image, @alan.seifert. I can reproduce the error.

I also checked if the error is caused by the volume being 4D, but averaging it across time and inputting the 3D volume also causes the same error:

Details
julien-macbook:~/Desktop/20220223_sct $ sct_maths -i im.nii.gz -mean t -o im_mean.nii.gz

--
Spinal Cord Toolbox (git-HEAD-b918050113a12dfd7a77d808150f9e5d67fe85f8)

sct_maths -i im.nii.gz -mean t -o im_mean.nii.gz
--


Done! To view results, type:
fsleyes im_mean.nii.gz &

julien-macbook:~/Desktop/20220223_sct $ sct_crop_image -i im_mean.nii.gz -m im_mean_mask.nii.gz -o im_crop.nii.gz -v 2

--
Spinal Cord Toolbox (git-HEAD-b918050113a12dfd7a77d808150f9e5d67fe85f8)

sct_crop_image -i im_mean.nii.gz -m im_mean_mask.nii.gz -o im_crop.nii.gz -v 2
--

Loaded im_mean.nii.gz (/Users/julien/Desktop/20220223_sct/im_mean.nii.gz) orientation RPI shape (112, 112, 24)
Loaded im_mean_mask.nii.gz (/Users/julien/Desktop/20220223_sct/im_mean_mask.nii.gz) orientation RPI shape (112, 112, 24)
Bounding box: x=[28, 80], y=[48, 100], z=[0, 24]
Cropping the image...
Traceback (most recent call last):
  File "/Users/julien/code/sct/spinalcordtoolbox/scripts/sct_crop_image.py", line 180, in <module>
    main(sys.argv[1:])
  File "/Users/julien/code/sct/spinalcordtoolbox/scripts/sct_crop_image.py", line 166, in main
    img_crop = cropper.crop(background=arguments.b)
  File "/Users/julien/code/sct/spinalcordtoolbox/cropping.py", line 98, in crop
    new_origin = np.dot(img_out.hdr.get_qform(), [bbox.xmin, bbox.ymin, bbox.zmin, 1])
  File "/Users/julien/code/sct/python/envs/venv_sct/lib/python3.7/site-packages/nibabel/nifti1.py", line 915, in get_qform
    quat = self.get_qform_quaternion()
  File "/Users/julien/code/sct/python/envs/venv_sct/lib/python3.7/site-packages/nibabel/nifti1.py", line 890, in get_qform_quaternion
    return fillpositive(bcd, self.quaternion_threshold)
  File "/Users/julien/code/sct/python/envs/venv_sct/lib/python3.7/site-packages/nibabel/quaternions.py", line 99, in fillpositive
    raise ValueError(f'w2 should be positive, but is {w2:e}')
ValueError: w2 should be positive, but is -4.664622e-07

I’ll continue debugging.

Likely related error: https://github.com/nipy/nibabel/issues/626

I’m wondering if the pixdim0 being negative could be the cause of the issue:

**julien-macbook:**~/Desktop/20220223_sct $ fslhd im.nii.gz
filename im.nii.gz
size of header 348
data_type INT16
dim0 4
dim1 112
dim2 112
dim3 24
dim4 400
dim5 1
dim6 1
dim7 1
vox_units mm
time_units s
datatype 4
nbyper 2
bitpix 16
pixdim0 -1.000000   <------- HERE
pixdim1 0.750000
pixdim2 0.750000
pixdim3 3.000000
pixdim4 1.500000

For reference: NIfTI definition of pixdim field

I took a quick look through the other 4 datasets I currently have in process, and all 4 of those also have pixdim0 = -1 but were cropped successfully.

Indeed, manually changing qfac (associated with the pixdim[0] field) from -1 to 1 did not solve the observed error.

`qfac` definition
  1. B.: Method 2 uses a factor qfac which is either -1 or 1; qfac is
    stored in the otherwise unused pixdim[0]. If pixdim[0]=0.0 (which
    should not occur), we take qfac=1. Of course, pixdim[0] is only used
    when reading a NIFTI-1 header, not when reading an ANALYZE 7.5 header.

More likely cause is this. I’ll try modifying the threshold in nibabel to see if it solves it (as commented here), and if it does we will consider adding a flag in SCT to control this nibabel parameter.

yup! cause identified :tada:

>>> import nibabel as nib
>>> nii = nib.load("im.nii.gz")         
>>> nii.get_qform()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/julien/opt/miniconda3/lib/python3.9/site-packages/nibabel/nifti1.py", line 1831, in get_qform
    return self._header.get_qform(coded)
  File "/Users/julien/opt/miniconda3/lib/python3.9/site-packages/nibabel/nifti1.py", line 915, in get_qform
    quat = self.get_qform_quaternion()
  File "/Users/julien/opt/miniconda3/lib/python3.9/site-packages/nibabel/nifti1.py", line 890, in get_qform_quaternion
    return fillpositive(bcd, self.quaternion_threshold)
  File "/Users/julien/opt/miniconda3/lib/python3.9/site-packages/nibabel/quaternions.py", line 99, in fillpositive
    raise ValueError(f'w2 should be positive, but is {w2:e}')
ValueError: w2 should be positive, but is -4.664622e-07
>>> nib.Nifti1Header.quaternion_threshold = -1e-06
>>> nii_modifthr = nib.load("im.nii.gz")
>>> nii_modifthr.get_qform()
array([[ -0.75      ,   0.        ,   0.        ,  43.44189835],
       [  0.        ,   0.73165081,  -0.65954043, -43.35409927],
       [  0.        ,  -0.16488511,  -2.92660322, -48.88690186],
       [  0.        ,   0.        ,   0.        ,   1.        ]])

Great! In the meantime, I had already used fslroi as an interim workaround. Would we expect to see a solution built into the next release of SCT?

Yes! It is programmed for the next release. Details here:

Hi @alan.seifert,

I just wanted to let you know that SCT v5.6 has just been released, and it contains a fix for this issue:

Thank you again for your participation in the forum! Your feedback has helped improve SCT for future users. :slightly_smiling_face:

Kind regards,
Joshua

1 Like

Hi Johsua,

Thanks for addressing this! And sorry for the long delay. I no longer get the error in sct_crop_image, but now (on v5.7) I do get the error in sct_fmri_moco.

sct_fmri_moco -i fmri_3d_ms75_c4_c6/im_crop.nii.gz -ofolder fmri_3d_ms75_c4_c6 -g 1

--
Spinal Cord Toolbox (5.7)

sct_fmri_moco -i fmri_3d_ms75_c4_c6/im_crop.nii.gz -ofolder fmri_3d_ms75_c4_c6 -g 1
--


Input parameters:
  Input file ............ fmri_3d_ms75_c4_c6/im_crop.nii.gz
  Group size ............ 1
Creating temporary folder (/var/folders/9d/lt6rxcbx1dq2yxb70xf1x00c0000gn/T/sct-20221108120531.607714-moco-ahn7lc5g)

Copying input data to tmp folder and convert to nii...

Get dimensions of data...
  50 x 56 x 20

Data orientation: RPI
  Treated as axial

Set suffix of transformation file name, which depends on the orientation:
Orientation is axial, suffix is 'Warp.nii.gz'. The estimated transformation is a 3D warping field, which is composed of a stack of 2D Tx-Ty transformations

Split along T dimension...
Merge within groups: 100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 167/167 [00:02<00:00, 68.94iter/s]

Merge across groups...

-------------------------------------------------------------------------------
  Estimating motion across groups...
-------------------------------------------------------------------------------

Input parameters:
  Input file ............ datasub-groups.nii
  Reference file ........ datasub_0_mean.nii.gz
  Polynomial degree ..... 2
  Smoothing kernel ...... 0
  Gradient step ......... 1
  Metric ................ MeanSquares
  Sampling .............. None
  Todo .................. estimate_and_apply
  Mask  ................. 
  Output mat folder ..... mat_groups

Data dimensions:
  50 x 56 x 20 x 167

Copy file_target to a temporary file...

Register. Loop across Z (note: there is only one Z if orientation is axial)
Z=0/0: 100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 167/167 [03:24<00:00,  1.22s/iter]

-------------------------------------------------------------------------------
  Apply moco
-------------------------------------------------------------------------------

Input parameters:
  Input file ............ data.nii
  Reference file ........ datasub_0_mean.nii.gz
  Polynomial degree ..... 2
  Smoothing kernel ...... 0
  Gradient step ......... 1
  Metric ................ MeanSquares
  Sampling .............. None
  Todo .................. apply
  Mask  ................. 
  Output mat folder ..... mat_final

Data dimensions:
  50 x 56 x 20 x 167

Copy file_target to a temporary file...

Register. Loop across Z (note: there is only one Z if orientation is axial)
Z=0/0:   0%|                                          | 0/167 [00:00<?, ?iter/s]/usr/local/sct_v5.7.0/bin/isct_antsApplyTransforms -d 3 -i /private/var/folders/9d/lt6rxcbx1dq2yxb70xf1x00c0000gn/T/sct-20221108120531.607714-moco-ahn7lc5g/data_T0000.nii -o /private/var/folders/9d/lt6rxcbx1dq2yxb70xf1x00c0000gn/T/sct-20221108120531.607714-moco-ahn7lc5g/data_T0000_moco.nii -t mat_final/mat.Z0000T0000Warp.nii.gz -r datasub_0_mean.nii.gz -n Linear # in /private/var/folders/9d/lt6rxcbx1dq2yxb70xf1x00c0000gn/T/sct-20221108120531.607714-moco-ahn7lc5g
Z=0/0:   0%|                                          | 0/167 [00:01<?, ?iter/s]
Traceback (most recent call last):
  File "/usr/local/sct_v5.7.0/spinalcordtoolbox/scripts/sct_fmri_moco.py", line 201, in <module>
    main(sys.argv[1:])
  File "/usr/local/sct_v5.7.0/spinalcordtoolbox/scripts/sct_fmri_moco.py", line 186, in main
    fname_output_image = moco_wrapper(param)
  File "/usr/local/sct_v5.7.0/spinalcordtoolbox/moco.py", line 373, in moco_wrapper
    file_mat_data, im_moco = moco(param_moco)
  File "/usr/local/sct_v5.7.0/spinalcordtoolbox/moco.py", line 604, in moco
    file_data_splitZ_splitT_moco[it], im_mask=input_mask)
  File "/usr/local/sct_v5.7.0/spinalcordtoolbox/moco.py", line 754, in register
    '-v', '0'])
  File "/usr/local/sct_v5.7.0/spinalcordtoolbox/scripts/sct_apply_transfo.py", line 360, in main
    transform.apply()
  File "/usr/local/sct_v5.7.0/spinalcordtoolbox/scripts/sct_apply_transfo.py", line 294, in apply
    im_src_reg.copy_qform_from_ref(Image(fname_dest))
  File "/usr/local/sct_v5.7.0/spinalcordtoolbox/image.py", line 378, in copy_qform_from_ref
    self.hdr.set_qform(im_ref.hdr.get_qform())
  File "/usr/local/sct_v5.7.0/python/envs/venv_sct/lib/python3.7/site-packages/nibabel/nifti1.py", line 917, in get_qform
    quat = self.get_qform_quaternion()
  File "/usr/local/sct_v5.7.0/python/envs/venv_sct/lib/python3.7/site-packages/nibabel/nifti1.py", line 892, in get_qform_quaternion
    return fillpositive(bcd, self.quaternion_threshold)
  File "/usr/local/sct_v5.7.0/python/envs/venv_sct/lib/python3.7/site-packages/nibabel/quaternions.py", line 99, in fillpositive
    raise ValueError(f'w2 should be positive, but is {w2:e}')
ValueError: w2 should be positive, but is -4.147325e-07

Dear @alan.seifert,

I’m sorry to hear that you’re still experiencing this issue, but thank you for sharing an update. :slightly_smiling_face:

I’ve reopened the existing issue, and will begin looking into a solution for this immediately.

Kind regards,
Joshua