heudiconv
heudiconv copied to clipboard
heurisitics script not reading my fieldmap data - .json missing IntendedFor line
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
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.
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))
Thank you!
@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.