Convert DICOM to BIDS to be fully compatible with afni_proc.py

Hello,

I wonder how to convert DICOM to BIDS to be fully compatible with afni_proc.py? By compatible I mean all information used later in afni processing is saved in the way afni prefers.

For example, I have BOLD acquired with multi-echo and multi-band. After converting DICOM to nifti using either dcm2niix_afni or dcm2niix, 3dinfo -slice_timing *.nii will print all 0 slice timing. If I use the default option for 3dTshift in afni_proc.py, it will read the slice timing from the nifti header (all 0) and the slice timing correction will not be done.

I noticed in the APMULTI_Demo1_rest, 3dinfo -slice_timing of the bold nifti indeed print the slice timing. Did you use abids_tool.py to add that information? Should I apply abids_tool.py -add_slice_times to all of my EPI files after creating the BIDS dataset from DICOM? Alternatively, I can add @filename option in the 3dTshift option but this requires a .1D file which means I need to generate each EPI based on the BIDS JSON file. Could you please recommend a better approach? Maybe there is a afni way of building BIDS dataset from DICOM?

Is slice timing the only trick when analyzing BIDS dataset using afni? In other words, what extra steps should I consider when converting DICOM to BIDS that will be used for afni analysis?

Thanks a lot.

Hi, Zhengchen-

AFNI is certainly happy to process BIDS-formatted datasets. The known structure facilitates finding the files that you want. You can also use non-BIDS-formatted datasets as inputs/starting points. You should have it be clear and organized. For example, some people have quasi-BIDS or near-BIDS file structure and naming conventions, or simply other organized data structures. Whatever the input structure you use is, be sure the data properties are correct for your acquisition/study, such as with the "Getting to Know Your Data" section of quality control considerations here.

I will let @rickr comment on using 3dTshift and putting the timing information into the header files.

--pt

1 Like

The basic problem is that NIFTI does not have codes to deal with multiband slice timing (though I had been thinking about proposing something for that). I don't actually recall whether dcm2niix ever puts timing information in the datasets, it might just go into a sidecar, rather than the dataset.

So yes, you can use abids_tool.py -add_slice_times to add the timing to the NIFTI datasets. However, only AFNI will be able to read that, since it is not a standard part of NIFTI. Also, it seems like there is a needed decode() aspect of abids_tool.py that might make this fail in python 3. We will look into this.

In that case, for now you can use the @filename method via afni_proc.py -tshift_opts_ts, passing "filename" via afni_proc.py -copy_files. It is a touch more messy, but not bad.

  • rick
1 Like

An update for abids_tool.py to better handle version checks is in, so that will work with python version >= 3.10 after building.

  • rick
1 Like

Thanks @ptaylor and @rickr. I do have the slice timing in the JSON sidecar converted from DICOM by dcm2niix. I did abids_tool.py -add_slice_times and then 3dinfo, I do see the slice timing in the history of nifti, and the printed slice timing matches with the one shown in JSON. It is not stored in the header but a history chunk, which is similar to minc recording all manipulations in history.

Since I would like to use AFNI for all processing (anat and func), I don't have to follow BIDS or use BIDS APP. However, it is convenient to store and name data following BIDS. Quasi-BIDS is a nice solution in my case then. What tool do you recommend to convert DICOM to nifti such that the nifti file itself contains all the information AFNI wants? Or probably dcm2niix + abids_tool.py is the solution?

Thanks for this great QC issue, I read it and the editorial last year, it was very helpful. This getting to know your data philosophy convinced me to switch to AFNI.

It is fine to use dcm2niix (or _afni), but then add the timing to the NIFTI files via abids_tool.py or 3drefit. Alternatively, if you have siemens mosaic images with proper slice timing in them, Dimon can be used. Whichever way you go there should be fine.

In the case of abids_tool.py, besides the history, it is probably going into the the AFNI extension of the NIFTI header, but you are seeing the 3dtrefit command that abids_tool.py called to add it in the history. Use "3dinfo -slice_timing" to display the stored timing and verify.

  • rick
1 Like

Thanks a lot @rickr for the confirmation.

Well, one related thing that is worth verifying when using dcm2niix is that the slice direction matches what you expect. I belive dcm2niix may default to reorienting the data to RPI, but then if the acquired slice direction is not I->S, the slice timing will not match the data. If you are acquiring axial slices from I->S, that should match. But it is worth being sure of. It is also possible to tell dcm2niix to not reorient the data.

Dimon never reorients the data.

  • rick

Thanks a lot @rick for your kindly reminding. I did the following experiments using dcm2niix with and without isFlipY, and Dimon -infile_pattern './14550005/*' -gert_create_dataset -gert_outdir ./dimon -gert_to3d_prefix dimon_2e.nii.

  1. flipY in dcm2nii and the 3dinfo info is
❯ 3dinfo flip/flip_e2.nii
++ 3dinfo: AFNI version=AFNI_24.0.10 (Mar  5 2024) [64-bit]
** AFNI converts NIFTI_datatype=512 (UINT16) in file /flip/flip_e2.nii to FLOAT32
     Warnings of this type will be muted for this session.
     Set AFNI_NIFTI_TYPE_WARN to YES to see them all, NO to see none.

Dataset File:    /flip/flip_e2.nii
Identifier Code: AFN_n2nQXo7CLyWC46w2OJasNA  Creation Date: Tue Mar 12 14:46:02 2024
Template Space:  ORIG
Dataset Type:    Echo Planar (-epan)
Byte Order:      LSB_FIRST {assumed} [this CPU native = LSB_FIRST]
Storage Mode:    NIFTI
Storage Space:   93,028,352 (93 million) bytes
Geometry String: "MATRIX(3.703033,0.025852,0.003205,-121.3513,0.025792,-3.702456,0.065417,109.1128,-0.003664,0.065448,3.69942,-33.57686):64,64,34"
Data Axes Tilt:  Oblique (1.089 deg. from plumb)
Data Axes Approximate Orientation:
  first  (x) = Right-to-Left
  second (y) = Posterior-to-Anterior
  third  (z) = Inferior-to-Superior   [-orient RPI]
R-to-L extent:  -121.351 [R] -to-   111.946 [L] -step-     3.703 mm [ 64 voxels]
A-to-P extent:  -124.184 [A] -to-   109.113 [P] -step-     3.703 mm [ 64 voxels]
I-to-S extent:   -33.577 [I] -to-    88.523 [S] -step-     3.700 mm [ 34 voxels]
Number of time steps = 167  Time step = 1.90000s  Origin = 0.00000s
  -- At sub-brick #0 '?' datum type is float
  -- At sub-brick #1 '?' datum type is float
  -- At sub-brick #2 '?' datum type is float
** For info on all 167 sub-bricks, use '3dinfo -verb' **
  1. noflipY in dcm2nii and the 3dinfo info is
❯ 3dinfo noflip/noflip_e2.nii
++ 3dinfo: AFNI version=AFNI_24.0.10 (Mar  5 2024) [64-bit]
** AFNI converts NIFTI_datatype=512 (UINT16) in file /noflip/noflip_e2.nii to FLOAT32
     Warnings of this type will be muted for this session.
     Set AFNI_NIFTI_TYPE_WARN to YES to see them all, NO to see none.

Dataset File:    /noflip/noflip_e2.nii
Identifier Code: AFN_WZb226WrvkKX3TVEUqydAA  Creation Date: Tue Mar 12 14:46:38 2024
Template Space:  ORIG
Dataset Type:    Echo Planar (-epan)
Byte Order:      LSB_FIRST {assumed} [this CPU native = LSB_FIRST]
Storage Mode:    NIFTI
Storage Space:   93,028,352 (93 million) bytes
Geometry String: "MATRIX(3.703033,-0.025852,0.003205,-119.7226,0.025792,3.702456,0.065417,-124.142,-0.003664,-0.065448,3.69942,-29.45364):64,64,34"
Data Axes Tilt:  Oblique (1.089 deg. from plumb)
Data Axes Approximate Orientation:
  first  (x) = Right-to-Left
  second (y) = Anterior-to-Posterior
  third  (z) = Inferior-to-Superior   [-orient RAI]
R-to-L extent:  -119.723 [R] -to-   113.574 [L] -step-     3.703 mm [ 64 voxels]
A-to-P extent:  -124.142 [A] -to-   109.155 [P] -step-     3.703 mm [ 64 voxels]
I-to-S extent:   -29.454 [I] -to-    92.646 [S] -step-     3.700 mm [ 34 voxels]
Number of time steps = 167  Time step = 1.90000s  Origin = 0.00000s
  -- At sub-brick #0 '?' datum type is float
  -- At sub-brick #1 '?' datum type is float
  -- At sub-brick #2 '?' datum type is float
** For info on all 167 sub-bricks, use '3dinfo -verb' **
  1. do AFNI Dimon the 3dinfo info is
❯ 3dinfo dimon/dimon_2e.nii
++ 3dinfo: AFNI version=AFNI_24.0.10 (Mar  5 2024) [64-bit]

Dataset File:    /dimon/dimon_2e.nii
Identifier Code: AFN__m6oBX7iRpOmouUmZJmM7A  Creation Date: Tue Mar 12 14:52:15 2024
Template Space:  ORIG
Dataset Type:    Echo Planar (-epan)
Byte Order:      LSB_FIRST {assumed} [this CPU native = LSB_FIRST]
Storage Mode:    NIFTI
Storage Space:   139,264,000 (139 million) bytes
Geometry String: "MATRIX(3.703125,-0.025852,0.003205,-119.708,0.025792,3.702456,0.065418,-124.142,-0.003664,-0.065448,3.699512,-29.45365):64,64,34"
Data Axes Tilt:  Oblique (1.089 deg. from plumb)
Data Axes Approximate Orientation:
  first  (x) = Right-to-Left
  second (y) = Anterior-to-Posterior
  third  (z) = Inferior-to-Superior   [-orient RAI]
R-to-L extent:  -119.708 [R] -to-   113.595 [L] -step-     3.703 mm [ 64 voxels]
A-to-P extent:  -124.142 [A] -to-   109.155 [P] -step-     3.703 mm [ 64 voxels]
I-to-S extent:   -29.454 [I] -to-    92.649 [S] -step-     3.700 mm [ 34 voxels]
Number of time steps = 500  Time step = 1.90000s  Origin = 0.00000s  Number time-offset slices = 34  Thickness = 3.700
  -- At sub-brick #0 '?' datum type is short:            0 to         15718
  -- At sub-brick #1 '?' datum type is short:            0 to         18442
  -- At sub-brick #2 '?' datum type is short:            0 to         17398
** For info on all 500 sub-bricks, use '3dinfo -verb' *

The only difference among these is RAI (no flipY in dcm2niix or Dimon) to RPI (flipY in dcm2niix). The slice timing of them are identical as I guess they are extracted from the DICOM CSA header. Also, the only difference between two dcm2niix conversions in the JSON file is "PhaseEncodingDirection":"j-" for flipY and "PhaseEncodingDirection":"j" for noflipY.

Now I am confused about I->S you pointed. Our acquisition was done from the top of the head to the bottom of the head (sSliceArray.ucMode is 2 for descending). Here flipY or not, would not change the order of slices from I to S, as I-to-S is the z-axis, right?
image
[image source Coordinate systems - Slicer Wiki]

When AFNI does the slice timing correction, will it assign the first slice timing shown in the 3dinfo to the slice at the top of the z-axis, etc? So even if this slice is flipped in Y direction (j in the figure) from A-to-P to P-to-A, the slice timing correction will not be affected, right? Maybe I am saying nonsense here as I am really not familiar with this business. It is so nice that you flagged this point and I would like to avoid a wrong slice timing correction. Could you please help to clarify? Thanks again.

Best,
Zhengchen