Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added script to create jsons files for MP2RAGE #101

Merged
merged 15 commits into from
Nov 2, 2018
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions pythonCode/create_MP2RAGE_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
"""
WARNING:
This script was created in October 2018 when the specification
for MP2RAGE files was not fully finalized. Please double check with
latest version of the specs to make sure this is accurate.

This script will create the JSON files required for an MP2RAGE file.
To function it requires a BIDS data set with properly named image files:
The script will through all the folders of a BIDS data set and will create
the JSON files in every folder where it finds a file ending
with '_MPRAGE.nii.gz'.

Created by RG 2018-10-03
"""

import os
import json
from shutil import copyfile
from collections import OrderedDict

'''
DEFINE CONTENT OF JSON FILES
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these are single line comments, can you just do them in the # format rather than as block quotes ?

'''
# sub-*_inv-1_MPRAGE.json
data_inv_1 = OrderedDict()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One option that might be a little cleaner -- since you know all of the key-value pairs at the start, you could include all of them in the definition. So here, for data_inv_1, you might do:

data_inv_1 = OrderedDict([
      ('InversionTime', '900'),
      ('FlipAngle', '5')
])

Totally stylistic, but might be nice from a human readability perspective !

data_inv_1['InversionTime'] = '900' # ms
data_inv_1['FlipAngle'] = '5'

# sub-*_inv-2_MPRAGE.json
data_inv_2 = OrderedDict()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing for this dictionary !

data_inv_2['InversionTime'] = '2750' # ms
data_inv_2['FlipAngle'] = '3'

# sub-*_T1map.json
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you remove these lines since they're unused and not clarifying comments ?

# sub-*_T1w.json
data_T1 = OrderedDict()
data_T1['EstimationMethod'] = 'Marques et al., 2013'


# sub-*_MPRAGE.json
data_MP2RAGE = OrderedDict()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same stylistic comment is above -- you can really see the difference here with many fields !

data_MP2RAGE['MagneticFieldStrength'] = ''
data_MP2RAGE['ExcitationRepetitionTime'] = ''
data_MP2RAGE['InversionRepetitionTime'] = ''
data_MP2RAGE['NumberShots'] = ''
data_MP2RAGE['Manufacturer'] = ''
data_MP2RAGE['ManufacturersModelName'] = ''
data_MP2RAGE['DeviceSerialNumber'] = ''
data_MP2RAGE['SoftwareVersions'] = ''
data_MP2RAGE['StationName'] = ''
data_MP2RAGE['InstitutionName'] = ''
data_MP2RAGE['InstitutionAddress'] = ''
data_MP2RAGE['InstitutionalDepartmentName'] = ''
data_MP2RAGE['ReceiveCoilName'] = ''
data_MP2RAGE['ReceiveCoilActiveElements'] = ''
data_MP2RAGE['GradientSetType'] = ''
data_MP2RAGE['MRTransmitCoilSequence'] = ''
data_MP2RAGE['MatrixCoilMode'] = ''
data_MP2RAGE['CoilCombinationMethod'] = ''
data_MP2RAGE['NonlinearGradientCorrection'] = ''
data_MP2RAGE['WaterFatShift'] = ''
data_MP2RAGE['EchoTrainLength'] = ''
data_MP2RAGE['DwellTime'] = ''
data_MP2RAGE['MultibandAccelerationFactor'] = ''
data_MP2RAGE['AnatomicalLandmarkCoordinates'] = ''
data_MP2RAGE['MRAcquisitionType'] = ''
data_MP2RAGE['ScanningSequence'] = ''
data_MP2RAGE['SequenceVariant'] = ''
data_MP2RAGE['ScanOptions'] = ''
data_MP2RAGE['SequenceName'] = ''
data_MP2RAGE['PulseSequenceType'] = 'MP2RAGE'
data_MP2RAGE['PulseSequenceDetails'] = ''
data_MP2RAGE['ParallelReductionFactorInPlane'] = ''
data_MP2RAGE['ParallelAcquisitionTechnique'] = ''
data_MP2RAGE['PartialFourier'] = ''
data_MP2RAGE['PartialFourierDirection'] = ''
data_MP2RAGE['EffectiveEchoSpacing'] = ''
data_MP2RAGE['TotalReadoutTime'] = ''
data_MP2RAGE['PhaseEncodingDirection'] = ''
data_MP2RAGE['EchoTime1'] = '' # sec
data_MP2RAGE['EchoTime2'] = '' # sec
data_MP2RAGE['SliceThickness'] = '' # mm


'''
WRITE THEM
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here about single-line comments !

'''
start_dir = "" # insert here path to your BIDS data set

# list all subjects
subj_ls = next(os.walk(start_dir))[1]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of using os.walk, have you thought about using glob ? It's another standard import and might be a little cleaner, here !

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still gotta do that one.


for iSubj in subj_ls:
print(iSubj)
# list all subfolders and files for that subject
subj_dir = os.walk(os.path.join(start_dir, iSubj))
# go through all the files for that subject
for path, subdirs, files in subj_dir:
for name in files:
# creates the json files in the folder where the the MP2RAGE file is found
if '_MPRAGE.nii.gz' in name:
print(os.path.join(path, name))

json_folder = path

# _inv-1_MPRAGE.json
json_name = name[:12] + '_inv-1_MPRAGE.json'
# create the file
with open(os.path.join(json_folder, json_name), 'w') as ff:
json.dump(data_inv_1, ff, sort_keys=False, indent=4)

# _inv-2_MPRAGE.json
json_name = name[:12] + '_inv-2_MPRAGE.json'
with open(os.path.join(json_folder, json_name), 'w') as ff:
json.dump(data_inv_2, ff, sort_keys=False, indent=4)

# _MPRAGE.json
json_name = name[:12] + '_MPRAGE.json'
with open(os.path.join(json_folder, json_name), 'w') as ff:
json.dump(data_MP2RAGE, ff, sort_keys=False, indent=4)

# sub-01_T1map.json
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if these are clarifying or not ! Could you add more text, if they're intended to be ?

# sub-01_T1w.json
data_T1['BasedOn'] = \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a little hard to read -- could you define these strings first and then assign them as a value to this key ?

os.path.join('anat', name[:12] + '_inv1_part-mag_MPRAGE.nii.gz') + ', ' + \
os.path.join('anat', name[:12] + '_inv1_part-phase_MPRAGE.nii.gz') + ', ' + \
os.path.join('anat', name[:12] + '_inv1_part-mag_MPRAGE.nii.gz') + ', ' + \
os.path.join('anat', name[:12] + '_inv1_part-phase_MPRAGE.nii.gz') + ', '

json_name = name[:12] + '_T1map.json'
with open(os.path.join(json_folder, json_name), 'w') as ff:
json.dump(data_T1, ff, sort_keys=False, indent=4)

json_name = name[:12] + '_T1w.json'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't test this locally right now, so just to confirm -- why is it name[:12] ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is it name[:12]
Well spotted! That was one issue I had to fix. The script will now write the JSON files if it finds a _inv1_part-mag_MPRAGE.nii.gz and the name of the JSON files to create can then be based on the end index of that file without worrying how people decide to name their subjects / session...

Copy link
Collaborator

@emdupre emdupre Oct 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see now ! One other way to do this, then, that might be a little clearer -- something like:

sid = '_'.join(name.split('_')[:-3])  # excludes last three key-value pairs 

Avoiding counting characters just eases the cognitive burden when reading the code ! And writing it, too :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, then, rather calling e.g. name[:-29] multiple times, you could just assign the above string to a value and pop that in as you need it ✨

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback: helps me learn new stuff! Will implement that and directory walking today hopefully.
Also the multiple calling is definitely a bad habit from years of matlab coding: gotta lose that one fast!

with open(os.path.join(json_folder, json_name), 'w') as ff:
json.dump(data_T1, ff, sort_keys=False, indent=4)