heudiconv icon indicating copy to clipboard operation
heudiconv copied to clipboard

heurisitics script not reading my fieldmap data - .json missing IntendedFor line

Open fareshte opened this issue 2 years ago • 3 comments

Hello! I am very new to this so please let me know if I can better clarify any part of my question. I've been using the below heudiconv script:

docker run --rm -it \
-v /Users/neurouser1/Documents/MSFatigue:/base \
nipy/heudiconv:latest \
-d /base/DICOMS/sub-{subject}/ses-{session}/scans/\*/resources/DICOM/files/\*.dcm \
-o /base/Nifti/bids_directory/ \
-f /base/heuristic_msfatigue.py \
-s pilot001 \
-ss 1 \
-c dcm2niix -b \
--overwrite

and here's my heuristics script:

#!/usr/bin/env python
import os
import re

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
# Note: All files should be in nifti format before using this script

# Struct
t1w = create_key(
    'sub-{subject}/{session}/anat/sub-{subject}_{session}_T1w')

dwi = create_key(
   'sub-{subject}/{session}/dwi/sub-{subject}_{session}_dwi')

copt2= create_key(
    'sub-{subject}/{session}/anat/sub-{subject}_{session}_T2w'
)

# flair_1
flair1= create_key(
    'sub-{subject}/{session}/anat/sub-{subject}_{session}_run-1_FLAIR')

# rest
rest1 = create_key(
    'sub-{subject}/{session}/func/sub-{subject}_{session}_task-rest_run-1_bold')

# bold1_task

task1 = create_key(
    'sub-{subject}/{session}/func/sub-{subject}_{session}_task-switching_run-1_bold')

# bold2_task
task2 = create_key(
    'sub-{subject}/{session}/func/sub-{subject}_{session}_task-switching_run-2_bold')

# field maps
fmap_SE_AP = create_key(
    'sub-{subject}/{session}/fmap/sub-{subject}_{session}_dir-AP_run-1_epi')
fmap_SE_PA = create_key(
    'sub-{subject}/{session}/fmap/sub-{subject}_{session}_dir-PA_run-1_epi')

pe_rev = create_key(
    'sub-{subject}/{session}/fmap/sub-{subject}_{session}_dir-AP_epi')

pe_revencoding = create_key(
    'sub-{subject}/{session}/fmap/sub-{subject}_{session}_dir-PA_epi')

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
    session: session id
    """

    last_run = len(seqinfo)

    info = {
    # baseline
    t1w: [], flair1: [], copt2: [],
    # field map
    fmap_run1_ph: [], fmap_SE_AP: [], pe_rev: [], pe_revencoding: [], fmap_SE_PA: [],
    #tms session rest scans
    rest1: [], task1: [], task2: [],
    # dwi
    dwi: [],
    }

    def get_latest_series(key, s):
        if len(info[key]) == 0:
            info[key].append(s.series_id)
        else:
            info[key] = [s.series_id]
    for s in seqinfo:
        protocol = s.protocol_name.lower()

        fileCount = s.total_files_till_now

        # Baseline Anatomicals

        if "t1w_mpr" in protocol:
            get_latest_series(t1w, s)

        # Diffusion Spin Echo EPI scan
        elif "dti_blipup" in protocol:
            get_latest_series(pe_rev, s)

        # Diffusion Spin Echo EPI scan
        elif "dti_blipdown" in protocol:
            get_latest_series(pe_revencoding, s)

        # Diffusion tensor image
        elif "dti" in protocol:
                get_latest_series(dwi, s)

        # Get flair
        elif "flair3d" in protocol:
                get_latest_series(flair1, s)

        # Get coplanar t2
        elif "coplanart2" in protocol:
                get_latest_series(copt2, s)

        #Resting task scans (Need to change according to timepoint (date))
        elif "restingbold_mb6_1200" in protocol:
            get_latest_series(rest1, s)
            #info[rest1].append(s.series_id)

        #bold_switching task scan 1
        elif "bold_switching_1" in protocol:
            get_latest_series(task1, s)

        #bold_switching task scan 2
        elif "bold_switching_2" in protocol:
            get_latest_series(task2, s)


        #B0 maps
        elif "spinechofieldmap_ap" in protocol and "M" in s.image_type:
            info[fmap_SE_AP].append(s.series_id)
        elif "spinechofieldmap_ap_rev" in protocol and "M" in s.image_type:
            info[fmap_SE_PA].append(s.series_id)

        else:
            print("Series not recognized!: ", s.protocol_name, s.dcm_dir_name)
    return info

MetadataExtras = {
    fmap_SE_AP: {
        "PhaseEncodingDirection": "j-",
        "TotalReadoutTime": 0.0513,
    },
    fmap_SE_PA: {
        "PhaseEncodingDirection": "j",
        "TotalReadoutTime": 0.0513,
    },
    pe_rev: {
        "PhaseEncodingDirection": "j-",
        "TotalReadoutTime": 0.06095
    },
    pe_revencoding: {
        "PhaseEncodingDirection" : "j",
        "TotalReadoutTime": 0.06095
    }
}

#R>>L phase encoding direction
#From scanner protocol, Echo spacing = 0.53ms, EPI factor = 116
#Total readout time = Echo spacing x 0.001 x (EPI factor-1)

IntendedFor = {
  fmap_SE_AP: [
        '{session}/func/sub-{subject}_{session}_task-switching_run-1_bold.nii.gz',
        '{session}/func/sub-{subject}_{session}_task-switching-run-2_bold.nii.gz'
    ],

    fmap_SE_PA: [
        '{session}/func/sub-{subject}_{session}_task-switching_run-1_bold.nii.gz',
        '{session}/func/sub-{subject}_{session}_task-switching-run-2_bold.nii.gz'
    ],

    pe_rev: [
        '{session}/dwi/sub-{subject}_{session}_dwi.nii.gz',
    ],

    pe_revencoding: [
        '{session}/dwi/sub-{subject}_{session}_dwi.nii.gz',
    ],
}
# Function to swap out the '_' in the subIDs as they are uploaded.
def ReplaceSubject(subj_label):
    p = re.compile('-')
    return str(p.sub("", subj_label))

but my .json file isn't reading my IntendedFor (it's missing from the .json file). Could you please advise on why this may be the case? Any insight would be appreciated.

Thank you!

Platform details:

Choose one:

  • [X] Container - docker container, nipy/heudiconv:latest
  • Heudiconv version: latest

fareshte avatar Apr 22 '22 15:04 fareshte

Hi @fareshte

The documentation on how to have the "IntendedFor" field populated is here (I'm afraid that this didn't get propagated to the documentation in readthedocs.io)

Please try that and let us know if it works.

pvelasco avatar Apr 22 '22 17:04 pvelasco

Thank you for your response (and apologies for the delayed reply)! I am still having trouble with this. The IntendedFor is still not populating. I am also having difficulty acquiring .json files for my dir_PA fieldmap. Notably, I am collecting two fieldmap scans: 1) spin echo fieldmaps and 2) B0 with magnitude and phase. Any ideas on what I'm doing wrong?

I'm pasting my heuristic script below and attaching my tsv file (in case I'm specifying the wrong echo times for the fieldmaps - the echo times are the same for the spin echo fieldmaps)

import os
import re

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

# Struct
 t1w = create_key(
     'sub-{subject}/{session}/anat/sub-{subject}_{session}_T1w')

 dwi = create_key(
     'sub-{subject}/{session}/dwi/sub-{subject}_{session}_dwi')

 copt2= create_key(
     'sub-{subject}/{session}/anat/sub-{subject}_{session}_T2w')

# flair_1
 flair1= create_key(
     'sub-{subject}/{session}/anat/sub-{subject}_{session}_run-1_FLAIR')

# rest
 rest1 = create_key(
     'sub-{subject}/{session}/func/sub-{subject}_{session}_task-rest_run-1_bold')

# bold1_task
 task1 = create_key(
     'sub-{subject}/{session}/func/sub-{subject}_{session}_task-switching_run-1_bold')

# bold2_task
 task2 = create_key(
     'sub-{subject}/{session}/func/sub-{subject}_{session}_task-switching_run-2_bold')

# field maps
 fmap_run1_ph = create_key(
     'sub-{subject}/{session}/fmap/sub-{subject}_{session}_run-1_phasediff')
 fmap_run1_mag = create_key(
     'sub-{subject}/{session}/fmap/sub-{subject}_{session}_run-1_magnitude{item}')

 fmap_SE_AP = create_key(
     'sub-{subject}/{session}/fmap/sub-{subject}_{session}_dir-AP_run-1_epi')
 fmap_SE_PA = create_key(
     'sub-{subject}/{session}/fmap/sub-{subject}_{session}_dir-PA_run-1_epi')

 pe_rev = create_key(
     'sub-{subject}/{session}/fmap/sub-{subject}_{session}_dir-AP_epi')

 pe_revencoding = create_key(
     'sub-{subject}/{session}/fmap/sub-{subject}_{session}_dir-PA_epi')

 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
     session: session id
     """

last_run = len(seqinfo)

info = {
# baseline
t1w: [], flair1: [], copt2: [],
# field map
fmap_run1_ph: [], fmap_run1_mag: [], fmap_SE_AP: [], pe_rev: [], pe_revencoding: [], fmap_SE_PA: [],
#tms session rest scans
rest1: [], task1: [], task2: [],
# dwi
dwi: [],
 }

def get_latest_series(key, s):
    if len(info[key]) == 0:
        info[key].append(s.series_id)
    else:
        info[key] = [s.series_id]
for s in seqinfo:
    protocol = s.protocol_name.lower()

    fileCount = s.total_files_till_now

    # Baseline Anatomicals

    if "t1w_mpr" in protocol:
        get_latest_series(t1w, s)

    # Diffusion Spin Echo EPI scan
    elif "dti_blipup" in protocol:
        get_latest_series(pe_rev, s)

    # Diffusion Spin Echo EPI scan
    elif "dti_blipdown" in protocol:
        get_latest_series(pe_revencoding, s)

    # Diffusion tensor image
    elif "dti" in protocol:
            get_latest_series(dwi, s)

    # Get flair
    elif "flair3d" in protocol:
            get_latest_series(flair1, s)

    # Get coplanar t2
    elif "coplanart2" in protocol:
            get_latest_series(copt2, s)

    #Resting task scans (Need to change according to timepoint (date))
    elif "restingbold_mb6_1200" in protocol:
        get_latest_series(rest1, s)
        #info[rest1].append(s.series_id)

    #bold_switching task scan 1
    elif "bold_switching_1" in protocol:
        get_latest_series(task1, s)

    #bold_switching task scan 2
    elif "bold_switching_2" in protocol:
        get_latest_series(task2, s)


    #B0 maps
    elif "b0map" in protocol and "M" in s.image_type:
        info[fmap_run1_mag].append(s.series_id)
    elif "b0map" in protocol and "P" in s.image_type:
        info[fmap_run1_ph].append(s.series_id)
    elif "spinechofieldmap_ap" in protocol and "M" in s.image_type:
        info[fmap_SE_AP].append(s.series_id)
    elif "spinechofieldmap_ap_rev" in protocol and "M" in s.image_type:
        info[fmap_SE_PA].append(s.series_id)

    else:
        print("Series not recognized!: ", s.protocol_name, s.dcm_dir_name)
return info

 MetadataExtras = {

fmap_run1_ph: {
    "EchoTime1": 0.00492,
    "EchoTime2": 0.00738
},
fmap_SE_AP: {
    "PhaseEncodingDirection": "j-",
    "TotalReadoutTime": 0.0513,
#       "B0FieldIdentifier: "pepolar_fmap0"
},
fmap_SE_PA: {
    "PhaseEncodingDirection": "j",
    "TotalReadoutTime": 0.0513,
#         "B0FieldIdentifier: "pepolar_fmap0"
},
pe_rev: {
    "PhaseEncodingDirection": "j-",
    "TotalReadoutTime": 0.06095
},
pe_revencoding: {
    "PhaseEncodingDirection" : "j",
    "TotalReadoutTime": 0.06095
}

}

 IntendedFor = {
      fmap_run1_ph: [
          '{session}/func/sub-{subject}_{session}_task-rest-run-1_bold.nii.gz'
      ],

      fmap_run1_mag: [
          '{session}/func/sub-{subject}_{session}_task-rest-run-1_bold.nii.gz'
      ],

      fmap_SE_AP: [
          '{session}/func/sub-{subject}_{session}_task-switching_run-1_bold.nii.gz',
          '{session}/func/sub-{subject}_{session}_task-switching-run-2_bold.nii.gz'
      ],

      fmap_SE_PA: [
          '{session}/func/sub-{subject}_{session}_task-switching_run-1_bold.nii.gz',
          '{session}/func/sub-{subject}_{session}_task-switching-run-2_bold.nii.gz'
      ],

      pe_rev: [
          '{session}/dwi/sub-{subject}_{session}_dwi.nii.gz',
      ],

      pe_revencoding: [
          '{session}/dwi/sub-{subject}_{session}_dwi.nii.gz',
      ],
  }
 
 def ReplaceSubject(subj_label):
     p = re.compile('-')
     return str(p.sub("", subj_label)) 

dicominfo_ses-1.xlsx

Thank you!

fareshte avatar Jul 19 '22 17:07 fareshte

@fareshte do you still have the issue? have your read documentation @pvelasco pointed to above? what version of heudiconv:latest you actually have? in your heuristic I see you are defining some IntendedFor dictionary but that is not used by anything in heudiconv AFAIK.

yarikoptic avatar Oct 19 '22 17:10 yarikoptic