Heudiconv: no extraction of slice-timing data based on Philips DICOMs

Hi @mgxd,

Please find below the command I use:

docker run --rm -it -v /mnt/HDD_2To/COMAJ/BIDS/DICOM:/data:ro \
-v /mnt/HDD_2To/COMAJ/BIDS/Nifti:/output nipy/heudiconv:latest \
-d /data/{subject}/*/*.dcm -s 207187_M0_2016-10-11 \
-f /data/BIDS_heuristic_COMAJ.py -b -o /output

Below my dicominfo.tsv:

total_files_till_now example_dcm_file series_id dcm_dir_name unspecified2 unspecified3 dim1 dim2 dim3 dim4 TR TE protocol_name is_motion_corrected is_derived patient_id study_description referring_physician_name series_description sequence_name image_type accession_number patient_age patient_sex date series_uid
11 MR_1.3.46.670589.11.34037.5.0.4548.2016101113143433305.dcm 101-WIP SURVEY CLEAR 101_SURVEY - - 256 256 11 1 0.0044959002 2.303 WIP SURVEY CLEAR False False 3T11101613H15 COMAJ SURVEY Not found (‘ORIGINAL’, ‘PRIMARY’, ‘M_FFE’, ‘M’, ‘FFE’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.4548.2016101113141954303
3851 MR_1.3.46.670589.11.34037.5.0.4548.2016101113252254320.dcm 201-WIP 3DMULTIGRE SAG STRICT SENSE 201_3DMULTIGRE_SAG_STRICT - - 256 256 3840 1 0.054 30.521 WIP 3DMULTIGRE SAG STRICT SENSE False False 3T11101613H15 COMAJ 3DMULTIGRE SAG STRICT Not found (‘ORIGINAL’, ‘PRIMARY’, ‘PHASE MAP’, ‘P’, ‘FFE’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.4548.2016101113151209318
4011 MR_1.3.46.670589.11.34037.5.0.4548.2016101113370035006.dcm 301-WIP s3DT1 ISO 1mm HR SENSE 301_s3DT1_ISO_1mm_HR - - 256 256 160 1 0.0098773003 4.595 WIP s3DT1 ISO 1mm HR SENSE False False 3T11101613H15 COMAJ s3DT1 ISO 1mm HR Not found (‘ORIGINAL’, ‘PRIMARY’, ‘M_FFE’, ‘M’, ‘FFE’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.4548.2016101113252474529
4034 MR_1.3.46.670589.11.34037.5.0.5740.2016101113370804000.dcm 302-ax 302_ax - - 256 256 23 1 0.0098773003 4.595 ax False False 3T11101613H15 COMAJ ax (‘ORIGINAL’, ‘PRIMARY’, ‘PROJECTION IMAGE’, ‘M’, ‘FFE’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.2580.2016101113370746000
4057 MR_1.3.46.670589.11.34037.5.0.7788.2016101113371042000.dcm 303-coro 303_coro - - 256 256 23 1 0.0098773003 4.595 coro False False 3T11101613H15 COMAJ coro (‘ORIGINAL’, ‘PRIMARY’, ‘PROJECTION IMAGE’, ‘M’, ‘FFE’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.9836.2016101113370988000
4080 MR_1.3.46.670589.11.34037.5.0.9376.2016101113371261000.dcm 304-sag 304_sag - - 256 256 23 1 0.0098773003 4.595 sag False False 3T11101613H15 COMAJ sag (‘ORIGINAL’, ‘PRIMARY’, ‘PROJECTION IMAGE’, ‘M’, ‘FFE’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.8664.2016101113371205000
4111 MR_1.3.46.670589.11.34037.5.0.4548.2016101113402658168.dcm 401-WIP T2 * CLEAR 401_T2_ETOILE - - 256 256 31 1 0.9140009155 16.12 WIP T2 * CLEAR False False 3T11101613H15 COMAJ T2 * Not found (‘ORIGINAL’, ‘PRIMARY’, ‘M_FFE’, ‘M’, ‘FFE’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.4548.2016101113370239049
4141 MR_1.3.46.670589.11.34037.5.0.4548.2016101113412429201.dcm 501-WIP TSE T2 5mm CLEAR 501_TSE_T2_5mm - - 560 560 30 1 2.8172145996 80 WIP TSE T2 5mm CLEAR False False 3T11101613H15 COMAJ TSE T2 5mm Not found (‘ORIGINAL’, ‘PRIMARY’, ‘M_SE’, ‘M’, ‘SE’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.4548.2016101113402920199
4185 MR_1.3.46.670589.11.34037.5.0.4548.2016101113441543233.dcm 601-WIP FLAIR_3 mm SENSE 601_FLAIR_3_mm - - 560 560 44 1 11 125 WIP FLAIR_3 mm SENSE False False 3T11101613H15 COMAJ FLAIR_3 mm Not found (‘ORIGINAL’, ‘PRIMARY’, ‘M_IR’, ‘M’, ‘IR’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.4548.2016101113421765231
5145 MR_1.3.46.670589.11.34037.5.0.4548.2016101113543018279.dcm 701-WIP DTI_15dir serie 1 SENSE 701_DTI_15dir_serie_1 - - 128 128 960 1 12 56 WIP DTI_15dir serie 1 SENSE False False 3T11101613H15 COMAJ DTI_15dir serie 1 Not found (‘ORIGINAL’, ‘PRIMARY’, ‘M_SE’, ‘M’, ‘SE’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.4548.2016101113492972277
5205 MR_1.3.46.670589.11.34037.5.0.6520.2016101115153538035.dcm 702-isoTRACE 702_isoTRACE - - 128 128 60 1 12 56 isoTRACE False False 3T11101613H15 COMAJ isoTRACE (‘ORIGINAL’, ‘PRIMARY’, ‘M_SE’, ‘M’, ‘SE’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.6520.2016101115153521000
5685 MR_1.3.46.670589.11.34037.5.0.4548.2016101113570730241.dcm 801-WIP DTI CORRECTION SENSE 801_DTI_CORRECTION - - 128 128 480 1 12 56 WIP DTI CORRECTION SENSE False False 3T11101613H15 COMAJ DTI CORRECTION Not found (‘ORIGINAL’, ‘PRIMARY’, ‘M_SE’, ‘M’, ‘SE’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.4548.2016101113543280731
6645 MR_1.3.46.670589.11.34037.5.0.4548.2016101114013884723.dcm 901-WIP DTI_15dir serie 2 SENSE 901_DTI_15dir_serie_2 - - 128 128 960 1 12 56 WIP DTI_15dir serie 2 SENSE False False 3T11101613H15 COMAJ DTI_15dir serie 2 Not found (‘ORIGINAL’, ‘PRIMARY’, ‘M_SE’, ‘M’, ‘SE’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.4548.2016101113570999626
6705 MR_1.3.46.670589.11.34037.5.0.4548.2016101114070249687.dcm 1001-WIP B0 map CLEAR 1001_B0_map - - 128 128 60 1 0.5899979858 3.947 WIP B0 map CLEAR False False 3T11101613H15 COMAJ B0 map Not found (‘ORIGINAL’, ‘PRIMARY’, ‘M_FFE’, ‘M’, ‘FFE’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.4548.2016101114014381683
16705 MR_1.3.46.670589.11.34037.5.0.4548.2016101114075631749.dcm 1101-WIP FE_EPI 64x64 resting state SENSE 1101_FE_EPI_64x64_resting_state - - 64 64 10000 1 2.4 30 WIP FE_EPI 64x64 resting state SENSE False False 3T11101613H15 COMAJ FE_EPI 64x64 resting state Not found (‘ORIGINAL’, ‘PRIMARY’, ‘M_FFE’, ‘M’, ‘FFE’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.4548.2016101114071031747
16745 MR_1.3.46.670589.11.34037.5.0.4548.2016101114190599751.dcm 1201-WIP SE_FMRI_CORR_PA SENSE 1201_SE_FMRI_CORR_PA - - 64 64 40 1 7.06 58 WIP SE_FMRI_CORR_PA SENSE False False 3T11101613H15 COMAJ SE_FMRI_CORR_PA Not found (‘ORIGINAL’, ‘PRIMARY’, ‘M_SE’, ‘M’, ‘SE’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.4548.2016101114175419749
16785 MR_1.3.46.670589.11.34037.5.0.4548.2016101114201142793.dcm 1301-WIP SE_FMRI_CORR_AP SENSE 1301_SE_FMRI_CORR_AP - - 64 64 40 1 7.06 58 WIP SE_FMRI_CORR_AP SENSE False False 3T11101613H15 COMAJ SE_FMRI_CORR_AP Not found (‘ORIGINAL’, ‘PRIMARY’, ‘M_SE’, ‘M’, ‘SE’) 3T11101613H15 066Y F 20161011 1.3.46.670589.11.34037.5.0.4548.2016101114190634791

And finally the heuristic:

import os

def create_key(template, outtype=('nii.gz',), annotation_classes=None):
    if template is None or not template:
        raise ValueError('Template must be a valid format string')
    return (template, outtype, annotation_classes)


def infotodict(seqinfo):
    """Heuristic evaluator for determining which runs belong where

    allowed template fields - follow python string module:

    item: index within category
    subject: participant id
    seqitem: run number during scanning
    subindex: sub index within group
    """
    t1 = create_key('sub-{subject}/anat/sub-{subject}_T1w')
    t2 = create_key('sub-{subject}/anat/sub-{subject}_T2w')
    rest = create_key('sub-{subject}/func/sub-{subject}_task-rest_run-{item:02d}_bold')
    rest_corrpa = create_key('sub-{subject}/func/sub-{subject}_task-rest_run-{item:02d}_corrpa')
    rest_corrap = create_key('sub-{subject}/func/sub-{subject}_task-rest_run-{item:02d}_corrap')
    dwi = create_key('sub-{subject}/dwi/sub-{subject}_run-{item:02d}_dwi')
    dwi_corr = create_key('sub-{subject}/dwi/sub-{subject}_run-{item:02d}_corr')
    fmap = create_key('sub-{subject}/fmap/sub-{subject}_dir-{dir}_run-{item:02d}_epi')

    info = {t1:[], t2:[],
            rest:[], dwi:[],
            rest_corrpa:[], rest_corrap:[], dwi_corr:[],
            fmap:[]}

    for idx, s in enumerate(seqinfo):
        # T1 and T2 scans
        if (s.dim3 == 160) and (s.dim4 == 1) and ('s3DT1 ISO 1mm' in s.protocol_name):
            info[t1] = [s.series_id]
        if (s.dim3 == 44) and (s.dim4 == 1) and ('FLAIR_3 mm SENSE' in s.protocol_name):
            info[t2] = [s.series_id]
        # diffusion scans
        if ('DTI' in s.protocol_name):
            key = None
            if (s.dim3 == 960):
                key = dwi
            elif (s.dim3 == 480) and ('DTI CORRECTION' in s.series_description):
                key = dwi_corr
            if key:
                info[key].append({'item': s.series_id})
        # functional scans
        if (s.dim3 == 10000) and ('EPI 64x64 resting state' in s.protocol_name):
            info[rest].append({'item': s.series_id})
        if (s.dim3 == 40) and ('FMRI_CORR_PA' in s.protocol_name):
            info[rest_corrpa].append({'item': s.series_id})
        if (s.dim3 == 40) and ('FMRI_CORR_AP' in s.protocol_name):
            info[rest_corrap].append({'item': s.series_id})
        if (s.dim3 == 60) and ('B0 map' in s.protocol_name):
            dirtype = s.protocol_name.split('_')[-1]
            info[fmap].append({'item': s.series_id, 'dir': dirtype})

    # You can even put checks in place for your protocol
    msg = []
    if len(info[t1]) != 1: msg.append('Missing correct number of t1 runs')
    if len(info[t2]) != 1: msg.append('Missing correct number of t2 runs')
    if len(info[dwi]) != 2: msg.append('Missing correct number of dwi runs')
    if len(info[dwi_corr]) != 1: msg.append('Missing correct number of dwi_corr runs')
    if len(info[rest]) != 1: msg.append('Missing correct number of resting runs')
    if len(info[rest_corrpa]) != 1: msg.append('Missing correct number of rest_corrpa runs')
    if len(info[rest_corrap]) != 1: msg.append('Missing correct number of rest_corrap runs')
    if msg:
        raise ValueError('\n'.join(msg))
    return info

Thanks !

Ah, it looks like heudiconv is having a problem accurately extracting information from these Philips scans - there is an open issue on our github repo regarding this.

However, if dcm2niix is able to extract the slicetiming information, your heudiconv conversion should work (in theory).

rest_corrpa = create_key(‘sub-{subject}/func/sub-{subject}_task-rest_run-{item:02d}_corrpa’)
rest_corrap = create_key(‘sub-{subject}/func/sub-{subject}_task-rest_run-{item:02d}_corrap’)
dwi_corr = create_key(‘sub-{subject}/dwi/sub-{subject}_run-{item:02d}_corr’)

I don’t believe these are in valid bids form - instead, you may want to change those to:

rest = create_key('sub-{subject}/func/sub-{subject}_task-rest_run-{item:02d}_bold')
dwi = create_key('sub-{subject}/dwi/sub-{subject}_run-{item:02d}_dwi')

The problem is not with the converter but the source images. Philips DICOM images do not store slice timing information. There are no official DICOM tags to report this information - on Siemens scanners we have reverse engineered the proprietary CSA header. However, Philips scanners do not currently store this information (as confirmed recently by Matthew Clemence from Philips). I encourage users at Philips sites to lobby their research collaboration managers to include this information in future software releases.

In brief, dcm2niix and other converters are only able to convert information that is present in the source images. Until Philips provides this information, we will be unable to automatically populate these fields. Fortunately, these parameters are pretty easy to estimate and consistent across runs of the same sequence. Be aware that if you use prospective motion correction it adds a slight pause at the end of the volume, and so the actual timing may be a tiny bit different from the canonical formulas, but the difference is so small it should have no influence.

3 Likes

Ok @mgxd so heudiconv docker image should work if dcm2niix manages to extract slice-timing ? Else what should I have to do ?

Concerning the non BIDS valid names, I have other posts talking about these supplementary files used to SDC and how to manage them.

Thanks @Chris_Rorden for these details !

Could you provide me links to how fill slice-timing field by estimation of these parameters ?

I can only re-confirm that we had no slice timing information stored anywhere in DICOMs for our previous Philips (achieva?) magnet . The only thing we could deduce is the slice order (based on some of the UIDs) an “fill in” the TR accordingly to get slice timing for the believers :wink:

My web page includes some Matlab scripts to help. It also describes how you can deduce the slice order by acquiring data while moving your head in a known direction.

What you need to know:

  • TR in seconds
  • Number of slices
  • Slice order (ascending, descending, interleaved) - beware that Siemens uses different slice orders for interleaved depending on if there are an odd or even number of slices, as described on my web page.
    *Any pause between volumes (sparse imaging, prospective slice time correction).
  • Multi-band factor

Consider the case where the TR is 3 seconds, there are 36 slices, ascending slice order, continuous acquisition without a pause, no multiband. In this case the Matlab code would be

TRsec = 3
nSlices = 36
TA = TRsec/nSlices
bidsSliceTiming=[0:TA:TRsec-TA]

2 Likes

Thanks @Chris_Rorden that really helps !

I keep notes on Philips DICOM and PAR/REC conversion. These can help explain why dcm2niix is unable to populate all of the BIDS fields for these images, and provides alternative suggestions.

Thank you for the explanation.
@Chris_Rorden @yarikoptic

  1. If the Philips DICOMS fMRI data was acquired with the slice order [1:2:48, 2:2:48], and the TR=3sec, then how can I infer bidsSliceTiming in this case?
  2. Additionally, if I can successfully infer bidsSliceTiming, then how can I add that data in pre-existing .json file? I use the heudiconv and over 500 patients data, plus, I am planning to use other manufacturers such as Simens, so that it is hard to add SliceTiming data manually.

Assuming that there is no temporal gap between volumes (sparse imaging, prospective slice time correction), that this is a Philips scanner (as Siemens scanners use different interleaving depending on odd vs even number of slices), and that this is not multi-band, the Matlab script:

TRsec = 3;
nSlices = 48;
TA = TRsec/nSlices; %assumes no temporal gap between volumes
bidsSliceTiming=[0:TA:TRsec-TA]; %ascending
if false %descending
   bidsSliceTiming = flip(bidsSliceTiming);
end
if true %interleaved
    order = [1:2:nSlices 2:2:nSlices]
    bidsSliceTiming(order) = bidsSliceTiming;
end
%report results
fprintf('	"SliceTiming": [\n');
for i = 1 : nSlices
    fprintf('		%g', bidsSliceTiming(i));
    if (i < nSlices)
        fprintf(',\n');
    else
        fprintf('	],\n');
    end  
end

Generates

	"SliceTiming": [
		0,
		1.5,
		0.0625,
		1.5625,
		0.125,
		1.625,
		0.1875,
		1.6875,
		0.25,
		1.75,
		0.3125,
		1.8125,
		0.375,
		1.875,
		0.4375,
		1.9375,
		0.5,
		2,
		0.5625,
		2.0625,
		0.625,
		2.125,
		0.6875,
		2.1875,
		0.75,
		2.25,
		0.8125,
		2.3125,
		0.875,
		2.375,
		0.9375,
		2.4375,
		1,
		2.5,
		1.0625,
		2.5625,
		1.125,
		2.625,
		1.1875,
		2.6875,
		1.25,
		2.75,
		1.3125,
		2.8125,
		1.375,
		2.875,
		1.4375,
		2.9375	],
2 Likes

Thank you so much!! I really appreciated.

are you 100% sure that it was interleaved like that and not by “sqrt(nslices)” as it was in our philips?

FWIW – I found that elderly script of mine which tries to deduce the order of the slices in the philips dicom: http://www.onerussian.com/tmp/philips_order.py

fix it up for recent pydicom or use with dicom==0.9.9.post1 . For some sample recent DICOMs from Philips I got

$> python ~/trash/philips_order.py XX_0016
/home/yoh/proj/repronim/umass/dicoms/David2_En/DICOM/venvs-old-dicom/local/lib/python2.7/site-packages/dicom/__init__.py:53: UserWarning: 
This code is using an older version of pydicom, which is no longer 
maintained as of Jan 2017.  You can access the new pydicom features and API 
by installing `pydicom` from PyPI.
See 'Transitioning to pydicom 1.x' section at pydicom.readthedocs.org 
for more information.

  warnings.warn(msg)

XX_0016
 Series:    func_task-rest_run-01
 Sequence:  FEEPI
 Failed to figure out for XX_0016 (skipped): Dataset does not have attribute 'PerFrameFunctionalGroupsSequence'.
(venvs-old-dicom) 1 27272 [1].....................................:Tue 17 Sep 2019 12:41:03 PM EDT:.
(git-annex)hopa:~/proj/repronim/umass/dicoms/David2_En[master]DICOM
$> python ~/trash/philips_order.py IM_0027
/home/yoh/proj/repronim/umass/dicoms/David2_En/DICOM/venvs-old-dicom/local/lib/python2.7/site-packages/dicom/__init__.py:53: UserWarning: 
This code is using an older version of pydicom, which is no longer 
maintained as of Jan 2017.  You can access the new pydicom features and API 
by installing `pydicom` from PyPI.
See 'Transitioning to pydicom 1.x' section at pydicom.readthedocs.org 
for more information.

  warnings.warn(msg)

IM_0027
 Series:    Patient Aligned MPR AWPLAN_SMARTPLAN_TYPE_BRAIN
 Sequence:  T1TFE
 # slices:  54
 order:     [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53]
 TR (sec):  0.00
 timing???: [0.0e+00 1.0e-05 2.0e-05 3.0e-05 4.0e-05 5.0e-05 6.0e-05 7.0e-05 8.0e-05 9.0e-05 1.0e-04 1.1e-04 1.2e-04 1.3e-04 1.4e-04 1.5e-04 1.6e-04 1.7e-04 1.8e-04 1.9e-04 2.0e-04 2.1e-04 2.2e-04 2.3e-04 2.4e-04 2.5e-04 2.6e-04 2.7e-04 2.8e-04 2.9e-04 3.0e-04 3.1e-04 3.2e-04 3.3e-04 3.4e-04 3.5e-04 3.6e-04 3.7e-04 3.8e-04 3.9e-04 4.0e-04 4.1e-04 4.2e-04 4.3e-04 4.4e-04 4.5e-04 4.6e-04 4.7e-04 4.8e-04 4.9e-04 5.0e-04 5.1e-04 5.2e-04 5.3e-04]
 max(???):  0.00

so might need more fixups to work for your EPI

1 Like

Thank you so much!

I’m working on resting state fMRI (Phillips) from ADNI (http://adni.loni.usc.edu/data-samples/access-data/).
One subject has 6720 .dcm files and the dimension of one fMRI data is [64, 64, 48, 140].

I tried to use your script, however, I had some problems.
The error message said that my data has no “PerFrameFunctionalGroupsSequence”.
When I read dicom file through pydicom, the dicom file needs to be one 4D fMRI data?

PathDicom = "/home/rrt/MyProject/Dicom2/003_S_5019/2013_01_01/fMRI/"
lstFilesDCM = []
for dirName, subdirList, fileList in os.walk(PathDicom):
    for filename in fileList:
        if ".dcm" in filename.lower():  # check whether the file's DICOM
            lstFilesDCM.append(os.path.join(dirName,filename))
            
ds = pydicom.dcmread(lstFilesDCM[0])

print(ds)

One of this results shows that "(0018, 0023) MR Acquisition Type CS: ‘2D’ ".
What should I do?
Is it possible to read 6720 files at once?
Or do I have to concatenate 6720 files to one 4D .dcm file? For this case, I have no information about the order of 6720 files.

Accordingly, there are no FrameContentSequence, PlanePositionSequence…

well, in your case my wild guess would be that files are in the order of acquisition, so you could look for some position fields (ImagePositionPatient) and see if they differ.
I would have dcmdump’ed two neighboring files and checked which fields differ and how . But may be someone has a better/cooked up complete solution already? sorry - I cannot dig into it ATM myself

1 Like

I just looked at this method using Philips data from Baxter Rogers as well as ADNI 305_S_6744.

While a DateTime is included in the SOPInstanceUID for Baxter’s data, the variability suggests it is not encoding acquisition time (though neither does 0008,0033). For example, a 4 volume sequence with a TR = 1.5s (6s of acquisition) has 0008,0018 span 9.89s.

(0008,0018) UI [1.3.46.670589.11.17240.5.0.8932.2018052615291998092] #  52, 1 SOPInstanceUID
(0008,0023) DA [20180526]                               #   8, 1 ContentDate
(0008,0033) TM [152913.83]                              #  10, 1 ContentTime

The ADNI data does not embed the DateTime in the SOPInstanceUID:

(0008,0018) UI [2.16.124.113543.6006.99.8494861114665796968] #  44, 1 SOPInstanceUID
(0008,0023) DA [20190629]                               #   8, 1 ContentDate
(0008,0033) TM [094245.63]                              #  10, 1 ContentTime

For the instances I looked at, neither 0008,0018 nor 0008,0033 provide plausible slice times. Perhaps they reflect database or reconstructor times.

Perhaps this method is useful for some Philips data, but it does not generalize. I would also caution that simply subtracting times in YYYYMMDDHHmmSS format is prone to fault. For example, there are 60 seconds in a minute so the mSS will go 058,059,100,101… Similar issues for hours and running across midnight.

Also, once upon a time Philips stored data to disk in the same order they were acquired. This is no longer the case, and I speculate the order reflects when images are returned from a massively parallel pipeline. For Philips enhanced data you want to look at DimensionIndexValues to discriminate between temporal volumes (fMRI), directions (DWI), slices and echoes.

(0020,9057) UL 1                                        #   4, 1 InStackPositionNumber
(0020,9128) UL 1                                        #   4, 1 TemporalPositionIndex
(0020,9157) UL 1\1\1\1                                  #  16, 4 DimensionIndexValues

For individuals acquiring their own data, I encourage directly measuring slice time by intentionally moving your head during a volume to reveal slice order.

1 Like

Thank you so much for checking ADNI data.
It is so complicated to get slice timing from ADNI Philips data.

Do you think it is better to get slice order from the ADNI data description dictionary, and then calculate the slice timing as you previously explained?

I have not actively used Philips equipment since 2005, so my knowledge is very out of date. Current Philips users are better placed than me to answer this. I would encourage scientists with Philips systems to lobby their Research Collaboration Managers to include details like phase encoding polarity and slice timing into future DICOM images.

With that caveat, and without the physical validation data I suggested previously, your best bet is to look for the details provided for the dataset. For example, the ADNI axial fcMRI sequence suggests equidistant Temporal slice spacing, prospect. motion corr. is off (if on, a short delay occurs between volumes), TR is 3.0s, 48 slices, 1 package, and default Slice scan order. The default slice order for a single package is described here.

This might be kind of a basic question, but how would you go about calculating the slice timing for an ascending scan? I’m trying to convert to BIDS from NIfTIs acquired on a Philips (don’t have access to the DICOMS). I was under the impression that–let’s say there are 34 slices in one TR–the slice timing would just be 34 equal increments of the TR i.e. if the TR were 2, the slice timings would be [0, .058, .117 … 1.941]. But when I look at other people’s SliceTiming vectors, the numbers go up and down. I must be misunderstanding what SliceTiming is exactly…

The slice timings should be in the same order as the slices are saved to disk. So assuming an axial scan with the inferior slice saved first to disk and subsequent slices in the volume moving superior, the slice timing for ascending would be [0t, 1t, 2*t… (n-1)*t] where n is the number of slices and t is the time between slices. Assuming no temporal gap between volumes, t = TR/n where TR is the repetition time (in seconds). Note that sparse designs often insert a temporal gap after all slices. Like wise, the Philips dynamic motion correction requires a bit of time (~16ms) at the end of each scan. Historically, slices were ascending, descending, interleaved ascending and interleaved descending. However, multi-band sequence use very unusual slice acquisition orders. You can always pragmatically determine slice acquisition order by intentionally rotating your head during a volume.