jmtyszka / bidskit

Utility functions for working with DICOM and BIDS neuroimaging data
MIT License
61 stars 41 forks source link

RPE image IntendedFor correcting DWI: IntendedFor field does not populate #89

Closed dkp closed 2 years ago

dkp commented 2 years ago

Describe the bug It should be possible to specify that a reverse phase encode image is IntendedFor correcting the DWI.

To Reproduce Steps to reproduce the behavior: You need a dwi image in the dwi directory and an RPE B0 image in the fmap directory. The test dataset is available here: https://osf.io/yz68t/

"DTI_30_DIRs_A-P":[ "dwi", "dir-AP_dwi", "UNASSIGNED" ],

"Bzero_verify_P-A":[
    "fmap",
    "dir-PA_epi",
    ["dir-AP_dwi"]
]

Expected behavior fmap/sub-219_ses-itbs_dir-PA_run-01_epi.json should contain a populated IntendedFor field. Instead, the IntendedFor field is empty.

Additional Context qsiprep needs the RPE image to have the IntendeFor field, just like fmriprep needs the phasediff to have the IntendedFor field.

vnckppl commented 2 years ago

I was running into the same issue. I wrote this patch that you can save into a file and run from the command line.

# * Libraries
import argparse
from glob import glob
import json
from shutil import copy2

# * Import arguments
if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description='Assign field maps to dwi data'
    )
    parser.add_argument('sid', help='Subject ID.')
    parser.add_argument('ses', help='Session ID.')
    parser.add_argument('path', help='Root folder containing subject folders')
    # ** Parse arguments
    args = parser.parse_args()

# * Create class for patching
class fixjson:
    def __init__(self, sid, ses, path):

        # ** Store input arguments
        self.subid = str(sid)
        self.sesid = str(ses)
        self.base = path

        # ** Build paths
        self.idir = self.base + "/sub-" + self.subid + "/ses-" + self.sesid
        self.idir_f = self.idir + "/fmap"
        self.idir_d = self.idir + "/dwi"

        # ** Locate dwi data
        self.dwis = sorted(glob(self.idir_d + "/*dwi*nii.gz"))
        # *** Remove root + subject path from these files
        self.dwis = [
            x.replace(self.base + "/sub-" + self.subid + "/", "")
            for x in self.dwis
        ]

        # ** Locate field map json files
        self.fmaps = sorted(glob(self.idir_f + "/*dwi*json"))

        # ** Loop over json files and inject 'intended for' dwi relative path
        for my_json in self.fmaps:

            # *** Create backup
            file_bkp = my_json.replace("json", "json_bkp")
            copy2(my_json, file_bkp)

            # *** Load json file
            with open(my_json) as json_file:
                self.tmp_json = json.load(json_file)

                # **** Inject dwi path into 'intended for' field
                self.tmp_json['IntendedFor'] = self.dwis

            # *** Save the edited json over the original file
            with open(my_json, "w") as write_file:
                json.dump(self.tmp_json, write_file, indent=4)                

# * Apply patch
my_fix_object = fixjson(args.sid, args.ses, args.path)
dkp commented 2 years ago

Very thoughtful, thanks!

vnckppl commented 2 years ago

Very thoughtful, thanks!

You are welcome. Keep in mind that I wrote this for a data set that only contains one DWI series. So, if you'd have multiple DWI series with each series having their own field maps, the script will not detect that. Just FYI.