Orientation issue

Summary of what happened:

For some of my participant-sessions, BIDS-validator is telling me that my slice timing array is not appropriate because my k dimension is 80 for the corresponding nifti header?
For context, i ran dcm2niix, and then I had to do an in-house script to manually add slice timing info, as we are using a Philips scanner where DICOMs do not have this field.

Physically my images are not properly oriented, I can see it pretty easily in an MRIQC visual for example.

I compared a json file that does not have the error with one that has it, and the only thing that jumps out is the phase encoding direction that is set to “j” (no error) and “i” (error) (see relevant output log below).

Our local MR specialists mentions the original problem is because the scanner automatically changes to coronal acquisition (as opposed to axial as it was supposed to) once the angle of acquisition goes above 45 degrees?

Any idea of how I could solve this? I hear fslswapdim could help? Can I just correct this by changing the metadata field in json files? Do I need to change this at the DICOM level? What other pre-processing considerations will I have to be careful of ?

Thanks in advance!

Command used (and if a helper script was used, a link to the helper script or the command generated):

$ bids-validator /data
bids-validator@1.15.0

### helper script:

#!/bin/bash

# Define file paths
# file with no slice timing error
file1="../data/sub-RGC901/ses-1/func/sub-RGC901_ses-1_task-BDM_run-1_bold.json" 
# file with slice timing error
file2="../data/sub-RGC908/ses-x/func/sub-RGC908_ses-x_task-BDM_run-1_bold.json"

# Sort JSON files using jq and save to temporary files
jq -S . "$file1" > sorted_file1.json
jq -S . "$file2" > sorted_file2.json

# Compare the sorted JSON files
diff sorted_file1.json sorted_file2.json

# Clean up temporary files
rm sorted_file1.json sorted_file2.json

Version:

Environment (Docker, Singularity / Apptainer, custom installation):

Bids-validator is installed locally.

Data formatted according to a validatable standard? Please provide the output of the validator:

3: [WARN] The number of elements in the SliceTiming array should match the 'k' dimension of the corresponding NIfTI volume. (code: 87 - SLICETIMING_ELEMENTS)
		./sub-RGC903/ses-1/func/sub-RGC903_ses-1_task-BDM_run-1_bold.nii.gz
			Evidence: SliceTiming array is of length 45 and the value of the 'k' dimension is 80 for the corresponding nifti header.
		./sub-RGC903/ses-1/func/sub-RGC903_ses-1_task-BDM_run-2_bold.nii.gz
			Evidence: SliceTiming array is of length 45 and the value of the 'k' dimension is 80 for the corresponding nifti header.
		./sub-RGC903/ses-1/func/sub-RGC903_ses-1_task-BDM_run-3_bold.nii.gz
			Evidence: SliceTiming array is of length 45 and the value of the 'k' dimension is 80 for the corresponding nifti header.
		./sub-RGC903/ses-x/func/sub-RGC903_ses-x_task-BDM_run-1_bold.nii.gz
			Evidence: SliceTiming array is of length 45 and the value of the 'k' dimension is 80 for the corresponding nifti header.
		./sub-RGC903/ses-x/func/sub-RGC903_ses-x_task-BDM_run-2_bold.nii.gz
			Evidence: SliceTiming array is of length 45 and the value of the 'k' dimension is 80 for the corresponding nifti header.
		./sub-RGC903/ses-x/func/sub-RGC903_ses-x_task-BDM_run-3_bold.nii.gz
			Evidence: SliceTiming array is of length 45 and the value of the 'k' dimension is 80 for the corresponding nifti header.
		./sub-RGC908/ses-x/func/sub-RGC908_ses-x_task-BDM_run-1_bold.nii.gz
			Evidence: SliceTiming array is of length 45 and the value of the 'k' dimension is 80 for the corresponding nifti header.
		./sub-RGC908/ses-x/func/sub-RGC908_ses-x_task-BDM_run-2_bold.nii.gz
			Evidence: SliceTiming array is of length 45 and the value of the 'k' dimension is 80 for the corresponding nifti header.
		./sub-RGC908/ses-x/func/sub-RGC908_ses-x_task-BDM_run-3_bold.nii.gz
			Evidence: SliceTiming array is of length 45 and the value of the 'k' dimension is 80 for the corresponding nifti header.

	Please visit https://neurostars.org/search?q=SLICETIMING_ELEMENTS for existing conversations about this issue.

Relevant log outputs (up to 20 lines):

[pagag24@beluga3 scripts]$ bash compare_json.sh 
5c5
<   "AcquisitionTime": "10:06:18.790000",
---
>   "AcquisitionTime": "11:48:39.760000",
13,14c13,14
<   "EstimatedEffectiveEchoSpacing": 0.000300251,
<   "EstimatedTotalReadoutTime": 0.0237198,
---
>   "EstimatedEffectiveEchoSpacing": 0.000291068,
>   "EstimatedTotalReadoutTime": 0.0229944,
18,23c18,23
<     0.999515,
<     -0.0205438,
<     0.0234043,
<     0.0301223,
<     0.828502,
<     -0.559176
---
>     0.999427,
>     -0.0293173,
>     0.0169287,
>     0.0324521,
>     0.687259,
>     -0.725688
31,32c31,32
<   "ImagingFrequency": 127.76768,
<   "InPlanePhaseEncodingDirectionDICOM": "COL",
---
>   "ImagingFrequency": 127.767885,
>   "InPlanePhaseEncodingDirectionDICOM": "ROW",
47c47
<   "PhaseEncodingAxis": "j",
---
>   "PhaseEncodingAxis": "i",
51,53c51,53
<   "PhilipsRescaleSlope": 0.806593,
<   "PhilipsScaleSlope": 0.0267301,
<   "PixelBandwidth": 2104.91,
---
>   "PhilipsRescaleSlope": 0.860317,
>   "PhilipsScaleSlope": 0.0235088,
>   "PixelBandwidth": 2190.68,
69c69
<   "WaterFatShift": 10.5683
---
>   "WaterFatShift": 10.2451

Screenshots / relevant information:

example of relevants field of a problematic json file:

{
  
  "StationName": "PHILIPS-BQ9N6TK",
  "BodyPartExamined": "BRAIN",
  "PatientPosition": "HFS",
  "SoftwareVersions": "5.3.1\\5.3.1.1",
  "MRAcquisitionType": "2D",
  "SeriesDescription": "fMRI_3mm_FOOD1",
  "ProtocolName": "WIP fMRI_3mm_FOOD1",
  "ScanningSequence": "GR",
  "SequenceVariant": "SK",
  "ScanOptions": "FS",
  "PulseSequenceName": "FEEPI",
  "ImageType": [
    "ORIGINAL",
    "PRIMARY",
    "T2",
    "NONE"
  ],
  "SeriesNumber": 601,
  "AcquisitionTime": "11:27:20.410000",
  "AcquisitionNumber": 6,
  "PhilipsRescaleSlope": 0.927228,
  "PhilipsRescaleIntercept": 0,
  "PhilipsScaleSlope": 0.018698,
  "UsePhilipsFloatNotDisplayScaling": 1,
  "SliceThickness": 3,
  "SpacingBetweenSlices": 3,
  "EchoTime": 0.03,
  "RepetitionTime": 2.75,
  "MTState": false,
  "FlipAngle": 80,
  "CoilString": "MULTI COIL",
  "PercentPhaseFOV": 100,
  "PercentSampling": 100,
  "EchoTrainLength": 39,
  "PhaseEncodingSteps": 80,
  "FrequencyEncodingSteps": 80,
  "PhaseEncodingStepsOutOfPlane": 1,
  "AcquisitionMatrixPE": 80,
  "ReconMatrixPE": 80,
  "ParallelReductionFactorInPlane": 2.1,
  "ParallelAcquisitionTechnique": "SENSE",
  "WaterFatShift": 10.5544,
  "EstimatedEffectiveEchoSpacing": 0.000299856,
  "EstimatedTotalReadoutTime": 0.0236886,
  "AcquisitionDuration": 626.962,
  "PixelBandwidth": 2108.46,
  "PhaseEncodingAxis": "j",
  "ImageOrientationPatientDICOM": [
    1,
    0,
    0,
    0,
    0.761425,
    -0.648253
  ],
  "InPlanePhaseEncodingDirectionDICOM": "COL",
  "ConversionSoftware": "dcm2niix",
  "ConversionSoftwareVersion": "v1.0.20230411",
  "TaskName": "BDM",
  "SliceTiming": [
    0.0000,
    0.0611,
    0.1222,
    0.1833,
    0.2444,
    0.3055,
    0.3666,
    0.4277,
    0.4888,
    0.5499,
    0.6110,
    0.6721,
    0.7332,
    0.7943,
    0.8554,
    0.9165,
    0.9776,
    1.0387,
    1.0998,
    1.1609,
    1.2220,
    1.2831,
    1.3442,
    1.4053,
    1.4664,
    1.5275,
    1.5886,
    1.6497,
    1.7108,
    1.7719,
    1.8330,
    1.8941,
    1.9552,
    2.0163,
    2.0774,
    2.1385,
    2.1996,
    2.2607,
    2.3218,
    2.3829,
    2.4440,
    2.5051,
    2.5662,
    2.6273,
    2.6884
  ],
  "SliceEncodingDirection": "k"
}


What does fslhd report for dim3 of this image - the SliceTiming suggests 45 slices, but the error suggests 80.

$ fslhd ./nifti.nii
filename ./nifti.nii
sizeof_hdr 348
data_type INT16
dim0 4
dim1 64
dim2 64
dim3 35

Hi, thanks for your reply,

So that is part of the problem, my matrix should be 80 x 80 x 45 (slices), but because of this issue, my problematic images come out as 80 x 45 x 80.

example of problematic image

sizeof_hdr      348
data_type       FLOAT32
dim0            4
dim1            80
dim2            45
dim3            80
dim4            169
dim5            1
dim6            1
dim7            1
vox_units       mm
time_units      s
datatype        16
nbyper          4
bitpix          32
pixdim0         -1.000000
pixdim1         3.000000
pixdim2         3.000000
pixdim3         3.000000
pixdim4         2.749998
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       0
freq_dim        0
slice_dim       0
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.968790 -0.090230 -0.422071 134.121002
qto_xyz:2       -0.355679 2.173337 2.037179 -146.677338
qto_xyz:3       -0.244496 -2.066026 2.161424 -67.592514
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.968790 -0.090236 -0.422070 134.121292
sto_xyz:2       -0.355682 2.173337 2.037179 -146.677307
sto_xyz:3       -0.244491 -2.066027 2.161425 -67.592506
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         2203.11-dirty 2023-08-23T14:21:21+01:00
aux_file

example of a fine image

sizeof_hdr      348
data_type       INT16
dim0            4
dim1            80
dim2            80
dim3            45
dim4            173
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.749998
pixdim5         0.000000
pixdim6         0.000000
pixdim7         0.000000
vox_offset      352
cal_max         0.000000
cal_min         0.000000
scl_slope       37.411068
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.998544 0.090374 0.023731 120.402924
qto_xyz:2       0.061625 2.485505 -1.678828 -91.328682
qto_xyz:3       0.070236 1.677527 2.486156 -109.903519
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.998545 0.090367 0.023709 120.402924
sto_xyz:2       0.061631 2.485506 -1.678828 -91.328682
sto_xyz:3       0.070213 1.677527 2.486156 -109.903519
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=100618.790
aux_file

When I use fslswapdim, I still have the orientation issue

fslswapdim inputfile x –z y outputfile 

I end up with something like this:


sizeof_hdr      348
data_type       FLOAT32
dim0            4
dim1            80
dim2            80
dim3            45
dim4            169
dim5            1
dim6            1
dim7            1
vox_units       mm
time_units      s
datatype        16
nbyper          4
bitpix          32
pixdim0         -1.000000
pixdim1         3.000000
pixdim2         3.000000
pixdim3         3.000000
pixdim4         2.749998
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       0
freq_dim        0
slice_dim       0
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.968790 -0.422075 0.090221 130.150894
qto_xyz:2       -0.355675 2.037179 -2.173337 -51.050522
qto_xyz:3       -0.244506 2.161424 2.066026 -158.497681
qto_xyz:4       0.000000 0.000000 0.000000 1.000000
qform_xorient   Right-to-Left
qform_yorient   Inferior-to-Superior
qform_zorient   Anterior-to-Posterior
sform_name      Scanner Anat
sform_code      1
sto_xyz:1       -2.968790 -0.422070 0.090236 130.150909
sto_xyz:2       -0.355682 2.037179 -2.173337 -51.050499
sto_xyz:3       -0.244491 2.161425 2.066027 -158.497681
sto_xyz:4       0.000000 0.000000 0.000000 1.000000
sform_xorient   Right-to-Left
sform_yorient   Inferior-to-Superior
sform_zorient   Anterior-to-Posterior
file_type       NIFTI-1+
file_code       1
descrip         2203.12-dirty 2024-02-01T16:17:47+00:00
aux_file

You need to check the provenance of your images. The image that converted correctly was created by dcm2niix, which always ensures that the slice direction is preserved as the 3rd dimension on disk:

descrip         TE=30;Time=100618.790

However, your problematic image has had the order of dimensions swizzled so it no longer matches the JSON file. All slice timing tools required that the slice direction is the third dimension.

descrip         2203.12-dirty 2024-02-01T16:17:47+00:00

You need to identify what tools manipulated your images to see how it got mangled. I would convert your files with dcm2niix and use these for analyses.

Thanks a lot for your reply,

Everything was converted with dcm2niix (both proper and “dirty” images). So I think my issue is at acquisition, when the scanner switches to coronal slice acquisition. From discussion with our local Philips MR specialist this happens because we are following bariatric surgery participants, and the neck morphology sometime makes it that the position of the head is tilted forward, and the angle the technician aligns to goes past 45 degrees. So we might end up with a mismatch of what our acquisition protocol says (axial slices) v.s. what is actually being recorded (coronal slices).

Do you think that could cause this “dirty” mention in the descrip field?

And if so how do I fix this?

While both images might have been initially converted from DICOM to NIfTI using dcm2niix, the corrupted image was manipulated by an FSL tool. dcm2niix always tries to embed echo time into the header description. So you expect it to begin “TE”. However, the “dirty” was embedded by something else, I suspect an FSL tool. You can try this out yourself, here using a dcm2niix reference image as input:

$ fslhd ax_asc_35sl_6 | grep '^descrip'
descrip		TE=30;Time=134935.305;phase=1
$ fslmaths ax_asc_35sl_6 -add 0 out  
$ fslhd out | grep '^descrip'
descrip		2203.12-dirty 2024-02-01T16:17:47+00:00

As I mentioned before, you need to check the provenance of the images and see at what stage they got resliced. I think you hunch that the oblique acquisition is leading some tool to reslice the data to get it into LAS orientation, but that reslicing must be done after you slice time correct the images.

ah ok I see what you mean. I did run a fslreorient2std on most images.
Now when I re-run with dcm2niix and check with fslhd the corrupted images look fine.
Now if I understand correctly I just have to set my phaseencodingdirection properly in the json files and it should work.

Thanks a lot!

Great glad we solved your problem. Note that Philips DICOMs lack details for reproducible science including PhaseEncodingDirection and SliceTiming. You will want to make sure your in-house slice timing estimates are accurate - these can be particularly unintuitive for multi-band sequences (especially those where the number slices divided by multi-band is an even number). You can work with the Philips Clinical Scientist to determine these details.