def _get_func_and_confounds(sourcedata_folder): from bids import BIDSLayout sourcedata_layout = BIDSLayout(sourcedata_folder, derivatives=True) fmriprep_layout = sourcedata_layout.derivatives['fMRIPrep'] files = fmriprep_layout.get(extensions=['.nii', 'nii.gz'], modality='func', suffix='preproc') confounds = [] metadata = [] for f in files: kwargs = {} for key in ['subject', 'run', 'task', 'session']: if hasattr(f, key): kwargs[key] = getattr(f, key) c = fmriprep_layout.get(suffix='confounds', **kwargs) c = c[0] confounds.append(c) sourcedata_file = sourcedata_layout.get(modality='func', extensions='nii.gz', **kwargs) assert (len(sourcedata_file) == 1) md = sourcedata_layout.get_metadata(sourcedata_file[0].filename) metadata.append(md) return list(zip(files, confounds, metadata))
def main(bids_dir, subject, session): print(subject, bids_dir) layout = BIDSLayout(bids_dir) bolds = layout.get(subject=subject, session=session, extensions='nii', suffix='bold') for bold in bolds: print(bold.filename) im = nb.load(bold.filename) zooms = list(im.header.get_zooms()) units = list(im.header.get_xyzt_units()) if (zooms[-1] != layout.get_metadata(bold.filename)['RepetitionTime']) or (units[1] != 's'): im.header.set_xyzt_units(t=8) # 8 = seconds zooms[-1] = layout.get_metadata(bold.filename)['RepetitionTime'] im.header.set_zooms(zooms) print(im.get_filename()) nb.save(im, im.get_filename()+'.new.nii') shutil.move(bold.filename+'.new.nii', bold.filename)
def get_scan_duration(output_dir, modality="func", task="rest"): layout = BIDSLayout(output_dir) df = layout.to_df() scans_df = df.query( "datatype==@modality & task==@task & extension=='nii.gz'") scan_durations = [] for file in scans_df.path: scan_durations.append(layout.get_metadata(file)["ScanDurationSec"]) scans_df["scan_duration"] = scan_durations scans_df.reset_index(drop=True, inplace=True) return scans_df
def main(argv=sys.argv): parser = generate_parser() args = parser.parse_args() # Set environment variables for FSL dir based on CLI os.environ['FSL_DIR'] = args.fsl_dir os.environ['FSLDIR'] = args.fsl_dir # for this script's usage of FSL_DIR... fsl_dir = args.fsl_dir + '/bin' # Load the bids layout layout = BIDSLayout(args.bids_dir) subsess = read_bids_layout(layout, subject_list=args.subject_list, collect_on_subject=args.collect) for subject, sessions in subsess: # Check if fieldmaps are concatenated if layout.get(subject=subject, session=sessions, datatype='fmap', extension='.nii.gz', acquisition='func', direction='both'): print( "Func fieldmaps are concatenated. Running seperate_concatenate_fm" ) seperate_concatenated_fm(layout, subject, sessions, fsl_dir) # recreate layout with the additional SEFMS layout = BIDSLayout(args.bids_dir) fmap = layout.get(subject=subject, session=sessions, datatype='fmap', extension='.nii.gz', acquisition='func') # Check if there are func fieldmaps and return a list of each SEFM pos/neg pair if fmap: print("Running SEFM select") base_temp_dir = fmap[0].dirname bes_pos, best_neg = sefm_select(layout, subject, sessions, base_temp_dir, fsl_dir, args.mre_dir, args.debug) for sefm in [os.path.join(x.dirname, x.filename) for x in fmap]: sefm_json = sefm.replace('.nii.gz', '.json') sefm_metadata = layout.get_metadata(sefm) if 'Philips' in sefm_metadata['Manufacturer']: insert_edit_json(sefm_json, 'EffectiveEchoSpacing', 0.00062771) if 'GE' in sefm_metadata['Manufacturer']: insert_edit_json(sefm_json, 'EffectiveEchoSpacing', 0.000536) if 'Siemens' in sefm_metadata['Manufacturer']: insert_edit_json(sefm_json, 'EffectiveEchoSpacing', 0.000510012) # Check if there are dwi fieldmaps and insert IntendedFor field accordingly if layout.get(subject=subject, session=sessions, datatype='fmap', extension='.nii.gz', acquisition='dwi'): print("Editing DWI jsons") edit_dwi_jsons(layout, subject, sessions) # Additional edits to the anat json sidecar anat = layout.get(subject=subject, session=sessions, datatype='anat', extension='.nii.gz') if anat: for TX in [os.path.join(x.dirname, x.filename) for x in anat]: TX_json = TX.replace('.nii.gz', '.json') TX_metadata = layout.get_metadata(TX) #if 'T1' in TX_metadata['SeriesDescription']: if 'Philips' in TX_metadata['Manufacturer']: insert_edit_json(TX_json, 'DwellTime', 0.00062771) if 'GE' in TX_metadata['Manufacturer']: insert_edit_json(TX_json, 'DwellTime', 0.000536) if 'Siemens' in TX_metadata['Manufacturer']: insert_edit_json(TX_json, 'DwellTime', 0.000510012) # add EffectiveEchoSpacing if it doesn't already exist # PE direction vs axis func = layout.get(subject=subject, session=sessions, datatype='func', extension='.nii.gz') if func: for task in [os.path.join(x.dirname, x.filename) for x in func]: task_json = task.replace('.nii.gz', '.json') task_metadata = layout.get_metadata(task) if 'Philips' in task_metadata['Manufacturer']: insert_edit_json(task_json, 'EffectiveEchoSpacing', 0.00062771) if 'GE' in task_metadata['Manufacturer']: if 'DV26' in task_metadata['SoftwareVersions']: insert_edit_json(task_json, 'EffectiveEchoSpacing', 0.000556) if 'Siemens' in task_metadata['Manufacturer']: insert_edit_json(task_json, 'EffectiveEchoSpacing', 0.000510012) if "PhaseEncodingAxis" in task_metadata: insert_edit_json(task_json, 'PhaseEncodingDirection', task_metadata['PhaseEncodingAxis']) elif "PhaseEncodingDirection" in task_metadata: insert_edit_json( task_json, 'PhaseEncodingAxis', task_metadata['PhaseEncodingDirection'].strip('-'))
def from_bids(cls, source_dir, subject=None, session=None, acquisition=None, run=None, inversion_efficiency=0.96): """ Creates a MEMP2RAGE-object from a properly organized BIDS-folder. The folder should be organized similar to this example: sub-01/anat/: # The first inversion time volumes * sub-01_inv-1_part-mag_MPRAGE.nii * sub-01_inv-1_part-phase_MPRAGE.nii # The four echoes of the second inversion (magnitude) * sub-01_inv-2_part-mag_echo-1_MPRAGE.nii * sub-01_inv-2_part-mag_echo-2_MPRAGE.nii * sub-01_inv-2_part-mag_echo-3_MPRAGE.nii * sub-01_inv-2_part-mag_echo-4_MPRAGE.nii # The four echoes of the second inversion (phase) * sub-01_inv-2_part-phase_echo-1_MPRAGE.nii * sub-01_inv-2_part-phase_echo-2_MPRAGE.nii * sub-01_inv-2_part-phase_echo-3_MPRAGE.nii * sub-01_inv-2_part-phase_echo-4_MPRAGE.nii # The json describing the parameters of the first inversion pulse * sub-01_inv-1_MPRAGE.json # The json describing the parameters of the second inversion pulse * sub-01_inv-2_echo-1_MPRAGE.json * sub-01_inv-2_echo-2_MPRAGE.json * sub-01_inv-2_echo-3_MPRAGE.json * sub-01_inv-2_echo-4_MPRAGE.json The JSON-files should contain all the necessary MP2RAGE sequence parameters and should look something like this: sub-01/anat/sub-01_inv-1_MPRAGE.json: { "InversionTime":0.67, "FlipAngle":7, "RepetitionTimeExcitation":0.0062, "RepetitionTimePreparation":6.723, "NumberShots":150, "FieldStrength": 7 } sub-01/anat/sub-01_inv-2_echo-1_MPRAGE.json: { "InversionTime":3.855, "FlipAngle":6, "RepetitionTimeExcitation":0.0320, "RepetitionTimePreparation":6.723, "NumberShots":150, "EchoTime": 6.0 "FieldStrength": 7 } sub-01/anat/sub-01_inv-2_echo-2_MPRAGE.json: { "InversionTime":3.855, "FlipAngle":6, "RepetitionTimeExcitation":0.0320, "RepetitionTimePreparation":6.723, "NumberShots":150, "EchoTime": 14.5 "FieldStrength": 7 } sub-01/anat/sub-01_inv-2_echo-3_MPRAGE.json: { "InversionTime":3.855, "FlipAngle":6, "RepetitionTimeExcitation":0.0320, "RepetitionTimePreparation":6.723, "NumberShots":150, "EchoTime": 23 "FieldStrength": 7 } sub-01/anat/sub-01_inv-2_echo-4_MPRAGE.json: { "InversionTime":3.855, "FlipAngle":6, "RepetitionTimeExcitation":0.0320, "RepetitionTimePreparation":6.723, "NumberShots":150, "EchoTime": 31.5 "FieldStrength": 7 } A MEMP2RAGE-object can now be created from the BIDS folder as follows: Example: >>> import pymp2rage >>> mp2rage = pymp2rage.MEMP2RAGE.from_bids('/data/sourcedata/', '01') Args: source_dir (BIDS dir): directory containing all necessary files subject (str): subject identifier **kwargs: additional keywords that are forwarded to get-function of BIDSLayout. For example `ses` could be used to select specific session. """ __dir__ = os.path.abspath(os.path.dirname(__file__)) layout = BIDSLayout(source_dir, validate=False, config=op.join(__dir__, 'bids', 'bep001.json')) df = layout.to_df() subject = str(subject) if subject is not None else subject session = str(session) if session is not None else session run = int(run) if run is not None else run for var_str, var in zip(['subject', 'session', 'run', 'acquisition'], [subject, session, run, acquisition]): if var is not None: df = df[df[var_str] == var] df = df[np.in1d(df.extension, ['nii', 'nii.gz'])] for key in ['echo', 'inv', 'fa']: if key in df.columns: df[key] = df[key].astype(float) df = df.set_index(['suffix', 'inv', 'echo', 'part']) df = df.loc[['MP2RAGE', 'TB1map']] for ix, row in df.iterrows(): for key, value in layout.get_metadata(row.path).items(): if key in [ 'EchoTime', 'InversionTime', 'RepetitionTimePreparation', 'RepetitionTimeExcitation', 'NumberShots', 'FieldStrength', 'FlipAngle' ]: df.loc[ix, key] = value if 'TB1map' in df.index: if len(df.loc['TB1map']) == 1: print('using {} as B1map'.format( str(df.loc['TB1map'].iloc[0]['path']))) b1map = df.loc['TB1map'].iloc[0]['path'] else: print('FOUND MORE THAN ONE B1-MAP! Will not use B1-correction') b1map = None else: b1map = None inv1 = df.loc[('MP2RAGE', 1, slice(None), 'mag'), 'path'].iloc[0] inv1ph = df.loc[('MP2RAGE', 1, slice(None), 'phase'), 'path'].iloc[0] inv2 = df.loc[('MP2RAGE', 2, slice(None), 'mag'), 'path'].tolist() inv2ph = df.loc[('MP2RAGE', 2, slice(None), 'phase'), 'path'].tolist() echo_times = df.loc[('MP2RAGE', 2, slice(None), 'mag'), 'EchoTime'].values MPRAGE_tr = df.loc[('MP2RAGE', 1, slice(None), 'mag'), 'RepetitionTimePreparation'].values[0] invtimesAB = df.loc[('MP2RAGE', 1, slice(None), 'mag'), 'InversionTime'].values[0], df.loc[( 'MP2RAGE', 2, slice(None), 'mag'), 'InversionTime'].values[0], nZslices = df.loc[('MP2RAGE', 1, slice(None), 'mag'), 'NumberShots'].values[0] FLASH_tr = df.loc[('MP2RAGE', 1, slice(None), 'mag'), 'RepetitionTimeExcitation'].values[0], df.loc[( 'MP2RAGE', 2, slice(None), 'mag'), 'RepetitionTimeExcitation'].values[0] B0 = df.loc[('MP2RAGE', 1, slice(None), 'mag'), 'FieldStrength'].values[0] flipangleABdegree = df.loc[('MP2RAGE', 1, slice(None), 'mag'), 'FlipAngle'].values[0], df.loc[( 'MP2RAGE', 2, slice(None), 'mag'), 'FlipAngle'].values[0] mp2rageme = cls(echo_times=echo_times, MPRAGE_tr=MPRAGE_tr, invtimesAB=invtimesAB, flipangleABdegree=flipangleABdegree, nZslices=nZslices, FLASH_tr=FLASH_tr, inversion_efficiency=inversion_efficiency, B0=B0, inv1=inv1, inv1ph=inv1ph, inv2=inv2, inv2ph=inv2ph) return mp2rageme
def _run_interface(self, runtime): import json from bids import BIDSLayout def validate_derivatives(bids_dir, derivatives): """ Validate derivatives argument provided by the user. Args: bids_dir: list Path to bids root directory. derivatives: str or list(str) Derivatives to use for denoising. Returns: derivatives_: list Validated derivatives list. scope: list Right scope keyword used in pybids query. """ if isinstance(derivatives, str): derivatives_ = [derivatives] else: derivatives_ = derivatives # Create full paths to derivatives folders derivatives_ = [ os.path.join(bids_dir, 'derivatives', d) for d in derivatives_ ] # Establish right scope keyword for arbitrary packages scope = [] for derivative_path in derivatives_: dataset_desc_path = os.path.join(derivative_path, 'dataset_description.json') try: with open(dataset_desc_path, 'r') as f: dataset_desc = json.load(f) scope.append(dataset_desc['PipelineDescription']['Name']) except FileNotFoundError as e: raise Exception(f"{derivative_path} should contain" + " dataset_description.json file") from e except KeyError as e: raise Exception( f"Key 'PipelineDescription.Name' is " + "required in {dataset_desc_path} file") from e return derivatives_, scope def validate_option(layout, option, kind='task'): """ Validate BIDS query filters provided by the user. Args: layout: bids.layout.layout.BIDSLayout Lightweight class representing BIDS project file tree. option: list Filter arguments provided by the user. kind: string Type of query. Available options are 'task', 'session' and 'subject'. Returns: option_: list Validated filter values. """ # Grab all possible filter values if kind == 'task': option_all = layout.get_tasks() elif kind == 'session': option_all = layout.get_sessions() elif kind == 'subject': option_all = layout.get_subjects() option_ = option for option_item in option_: if option_item not in option_all: raise ValueError(f'{kind} {option_item} is not found') return option_ # Validate derivatives argument derivatives, scope = validate_derivatives( bids_dir=self.inputs.bids_dir, derivatives=self.inputs.derivatives) layout = BIDSLayout(root=self.inputs.bids_dir, validate=True, derivatives=derivatives) # Validate optional arguments filter_base = {} if isdefined(self.inputs.task): task = validate_option(layout, self.inputs.task, kind='task') filter_base['task'] = task else: task = layout.get_tasks() if isdefined(self.inputs.session): session = validate_option(layout, self.inputs.session, kind='session') filter_base['session'] = session if isdefined(self.inputs.subject): subject = validate_option(layout, self.inputs.subject, kind='subject') filter_base['subject'] = subject # Define query filters keys_entities = ['task', 'session', 'subject', 'datatype'] filter_fmri = { 'extension': ['nii', 'nii.gz'], 'suffix': 'bold', 'desc': 'preproc' } filter_fmri_aroma = { 'extension': ['nii', 'nii.gz'], 'suffix': 'bold', 'desc': 'smoothAROMAnonaggr', } filter_conf = { 'extension': 'tsv', 'suffix': 'regressors', 'desc': 'confounds', } # for later filter_conf_json = { 'extension': 'json', 'suffix': 'regressors', 'desc': 'confounds', } filter_fmri.update(filter_base) ######################################################################## ### SOLUTION FOR LATER ################################################# ######################################################################## # filter_fmri_aroma.update(filter_base) # filter_conf.update(filter_base) # filter_conf_json.update(filter_base) # # Grab all requested files # fmri_prep = layout.get(scope=scope, **filter_fmri) # if self.inputs.ica_aroma: # fmri_prep_aroma = layout.get(scope=scope, **filter_fmri_aroma) # conf_raw = layout.get(scope=scope, **filter_conf) # conf_json = layout.get(scope=scope, **filter_conf_json) ######################################################################## ######################################################################## ######################################################################## fmri_prep, fmri_prep_aroma, conf_raw, conf_json, entities = ( [] for _ in range(5)) for fmri_file in layout.get(scope=scope, **filter_fmri): entity_bold = fmri_file.get_entities() # Look for corresponding confounds file filter_entities = { key: value for key, value in entity_bold.items() if key in keys_entities } # Constraining search filter_conf.update(filter_entities) filter_conf_json.update(filter_entities) conf_file = layout.get(scope=scope, **filter_conf) conf_json_file = layout.get(scope=scope, **filter_conf_json) if not conf_file: raise FileNotFoundError( f"Regressor file not found for file {fmri_file.path}") else: # Add entity only if both files are available if len(conf_file) > 1: print( f"Warning: Multiple regressors found for file {fmri_file.path}.\n" f"Selecting {conf_file[0].path}" ) # TODO: find proper warning (logging?) conf_file = conf_file[0] if not conf_json_file: raise FileNotFoundError( f"Regressor file not found for file {fmri_file.path}") else: # Add entity only if both files are available if len(conf_json_file) > 1: print( f"Warning: Multiple .json regressors found for file {fmri_file.path}.\n" f"Selecting {conf_json_file[0].path}") conf_json_file = conf_json_file[0] if self.inputs.ica_aroma: filter_fmri_aroma.update( filter_entities) # Add specific fields to constrain search fmri_aroma_file = layout.get(scope=scope, **filter_fmri_aroma) if not fmri_aroma_file: raise FileNotFoundError( f"ICA-Aroma file not found for file {fmri_file.path}") else: # Add entity only if both files are available if len(fmri_aroma_file) > 1: print( f"Warning: Multiple ICA-Aroma files found for file {fmri_file.path}.\n" f"Selecting {fmri_aroma_file[0].path}") # TODO: find proper warning (logging?) fmri_aroma_file = fmri_aroma_file[0] fmri_prep_aroma.append(fmri_aroma_file.path) fmri_prep.append(fmri_file.path) conf_raw.append(conf_file.path) conf_json.append(conf_json_file.path) entities.append(filter_entities) # Extract TRs tr_dict = {} for t in task: filter_fmri_tr = filter_fmri.copy() filter_fmri_tr['task'] = t example_file = layout.get(**filter_fmri_tr)[0] tr = layout.get_metadata(example_file.path)['RepetitionTime'] tr_dict[t] = tr self._results['fmri_prep'] = fmri_prep self._results['fmri_prep_aroma'] = fmri_prep_aroma self._results['conf_raw'] = conf_raw self._results['conf_json'] = conf_json self._results['entities'] = entities self._results['tr_dict'] = tr_dict return runtime
def from_bids(cls, source_dir, subject, use_B1map=True, **kwargs): """ Creates a MEMP2RAGE-object from a properly organized BIDS-folder. The folder should be organized similar to this example: sub-01/anat/: # The first inversion time volumes * sub-01_inv-1_part-mag_MPRAGE.nii * sub-01_inv-1_part-phase_MPRAGE.nii # The four echoes of the second inversion (magnitude) * sub-01_inv-2_part-mag_echo-1_MPRAGE.nii * sub-01_inv-2_part-mag_echo-2_MPRAGE.nii * sub-01_inv-2_part-mag_echo-3_MPRAGE.nii * sub-01_inv-2_part-mag_echo-4_MPRAGE.nii # The four echoes of the second inversion (phase) * sub-01_inv-2_part-phase_echo-1_MPRAGE.nii * sub-01_inv-2_part-phase_echo-2_MPRAGE.nii * sub-01_inv-2_part-phase_echo-3_MPRAGE.nii * sub-01_inv-2_part-phase_echo-4_MPRAGE.nii # The json describing the parameters of the first inversion pulse * sub-01_inv-1_MPRAGE.json # The json describing the parameters of the second inversion pulse * sub-01_inv-2_echo-1_MPRAGE.json * sub-01_inv-2_echo-2_MPRAGE.json * sub-01_inv-2_echo-3_MPRAGE.json * sub-01_inv-2_echo-4_MPRAGE.json The JSON-files should contain all the necessary MP2RAGE sequence parameters and should look something like this: sub-01/anat/sub-01_inv-1_MPRAGE.json: { "InversionTime":0.67, "FlipAngle":7, "ExcitationRepetitionTime":0.0062, "InversionRepetitionTime":6.723, "NumberShots":150 } sub-01/anat/sub-01_inv-2_echo-1_MPRAGE.json: { "InversionTime":3.855, "FlipAngle":6, "ExcitationRepetitionTime":0.0320, "InversionRepetitionTime":6.723, "NumberShots":150, "EchoTime": 6.0 } sub-01/anat/sub-01_inv-2_echo-2_MPRAGE.json: { "InversionTime":3.855, "FlipAngle":6, "ExcitationRepetitionTime":0.0320, "InversionRepetitionTime":6.723, "NumberShots":150, "EchoTime": 14.5 } sub-01/anat/sub-01_inv-2_echo-3_MPRAGE.json: { "InversionTime":3.855, "FlipAngle":6, "ExcitationRepetitionTime":0.0320, "InversionRepetitionTime":6.723, "NumberShots":150, "EchoTime": 23 } sub-01/anat/sub-01_inv-2_echo-4_MPRAGE.json: { "InversionTime":3.855, "FlipAngle":6, "ExcitationRepetitionTime":0.0320, "InversionRepetitionTime":6.723, "NumberShots":150, "EchoTime": 31.5 } A MP2RAGE-object can now be created from the BIDS folder as follows: Example: >>> import pymp2rage >>> mp2rage = pymp2rage.MEMP2RAGE.from_bids('/data/sourcedata/', '01') Args: source_dir (BIDS dir): directory containing all necessary files subject (str): subject identifier **kwargs: additional keywords that are forwarded to get-function of BIDSLayout. For example `ses` could be used to select specific session. """ layout = BIDSLayout(source_dir) filenames = layout.get(subject=subject, return_type='file', type='MPRAGE', extensions=['.nii', '.nii.gz'], **kwargs) part_regex = re.compile('part-(mag|phase)') inv_regex = re.compile('inv-([0-9]+)') echo_regex = re.compile('echo-([0-9]+)') parts = [ part_regex.search(fn).group(1) if part_regex.search(fn) else None for fn in filenames ] inversion_idx = [ int(inv_regex.search(fn).group(1)) if inv_regex.search(fn) else None for fn in filenames ] echo_idx = [ int(echo_regex.search(fn).group(1)) if echo_regex.search(fn) else 1 for fn in filenames ] # Check whether we have everything df = pandas.DataFrame({ 'fn': filenames, 'inv': inversion_idx, 'part': parts, 'echo': echo_idx }) tmp = df[np.in1d(df.inv, [1, 2]) & np.in1d(df.part, ['mag', 'phase']) & ((df.echo == 1))] check = (len(tmp) == 4) & (tmp.groupby(['inv', 'part']).size() == 1).all() if not check: raise ValueError('Did not find exactly one Magnitude and phase image for two' \ 'inversions. Only found: %s' % tmp.fn.tolist()) df = df.set_index(['inv', 'part', 'echo']) df.sort_index(inplace=True) inv1 = df.loc[1, 'mag', 1].fn inv1ph = df.loc[1, 'phase', 1].fn inv2 = df.loc[2, 'mag', 1].fn inv2ph = df.loc[2, 'phase', 1].fn print('Found following files for MP2RAGE:\n * inv1, magnitude: {inv1}\n * inv1, phase: {inv1ph}'\ '\n * inv2, magnitude: {inv2}\n * inv2, phase: {inv2ph}'.format(**locals())) echo_indices = df.index.get_level_values(2).unique() print('Found four echoes:') for echo in echo_indices: print(' * {}'.format(df.loc[2, 'mag', echo].fn)) meta_inv1 = layout.get_metadata(inv1) meta_inv2 = layout.get_metadata(inv2) for key in [ 'InversionRepetitionTime', 'NumberShots', 'PartialFourier' ]: if key in meta_inv1: if meta_inv1[key] != meta_inv2[key]: raise ValueError('%s of INV1 and INV2 are different!' % key) MPRAGE_tr = meta_inv1['InversionRepetitionTime'] invtimesAB = [meta_inv1['InversionTime'], meta_inv2['InversionTime']] flipangleABdegree = [meta_inv1['FlipAngle'], meta_inv2['FlipAngle']] if 'PartialFourier' in meta_inv1.keys(): nZslices = meta_inv1['NumberShots'] * np.array( [meta_inv1['PartialFourier'] - .5, 0.5]) else: nZslices = meta_inv1['NumberShots'] FLASH_tr = [ meta_inv1['ExcitationRepetitionTime'], meta_inv2['ExcitationRepetitionTime'] ] B0 = meta_inv1.pop('FieldStrength', 7) kwargs_B1 = {} if 'session' in kwargs: session = kwargs['session'] else: session = '.*' if use_B1map: B1_fieldmap = _get_B1map(layout, subject, session=session) else: B1_fieldmap = None echo_times = [] for echo in echo_indices: te = layout.get_metadata(df.loc[2, 'mag', echo].fn)['EchoTime'] echo_times.append(te) return cls(echo_times, MPRAGE_tr, invtimesAB, flipangleABdegree, nZslices, FLASH_tr, inv1=inv1, inv1ph=inv1ph, inv2=df.loc[2, 'mag'].fn.tolist(), inv2ph=df.loc[2, 'phase'].fn.tolist(), B1_fieldmap=B1_fieldmap)
def _run_interface(self, runtime): # Validate derivatives argument derivatives, scope = validate_derivatives( bids_dir=self.inputs.bids_dir, derivatives=self.inputs.derivatives) layout = BIDSLayout(root=self.inputs.bids_dir, derivatives=derivatives, validate=True, index_metadata=False) # Validate optional arguments filter_base = {} if isdefined(self.inputs.task): task = validate_option(layout, self.inputs.task, kind='task') filter_base['task'] = task else: task = layout.get_tasks() if isdefined(self.inputs.session): session = validate_option(layout, self.inputs.session, kind='session') filter_base['session'] = session if isdefined(self.inputs.subject): subject = validate_option(layout, self.inputs.subject, kind='subject') filter_base['subject'] = subject # Define query filters filter_fmri = { 'extension': ['nii', 'nii.gz'], 'suffix': 'bold', 'desc': 'preproc', 'space': 'MNI152NLin2009cAsym' } filter_fmri_aroma = { 'extension': ['nii', 'nii.gz'], 'suffix': 'bold', 'desc': 'smoothAROMAnonaggr', 'space': 'MNI152NLin2009cAsym' } filter_conf = { 'extension': 'tsv', 'suffix': 'regressors', 'desc': 'confounds', } filter_conf_json = { 'extension': 'json', 'suffix': 'regressors', 'desc': 'confounds', } filter_fmri.update(filter_base) filter_fmri_aroma.update(filter_base) filter_conf.update(filter_base) filter_conf_json.update(filter_base) # Grab all requested files fmri_prep = layout.get(scope=scope, **filter_fmri) if self.inputs.ica_aroma: fmri_prep_aroma = layout.get(scope=scope, **filter_fmri_aroma) if not fmri_prep_aroma: raise MissingFile( "ICA-AROMA files not found in BIDS directory") conf_raw = layout.get(scope=scope, **filter_conf) conf_json = layout.get(scope=scope, **filter_conf_json) # Validate correspondence between queried files entities = [] for i, fmri_file in enumerate(fmri_prep): # reference common entities for preprocessed files if self.inputs.ica_aroma and fmri_prep_aroma: compare_common_entities(fmri_file, fmri_prep_aroma[i]) compare_common_entities(fmri_file, conf_raw[i]) compare_common_entities(fmri_file, conf_json[i]) entities.append({ key: value for key, value in fmri_file.get_entities().items() if key in ['task', 'session', 'subject', 'datatype'] }) # Extract TRs tr_dict = {} #TODO: this is just a funny workaround, look for better solution later layout_for_tr = BIDSLayout(root=self.inputs.bids_dir, derivatives=derivatives, validate=True, index_metadata=True) for t in task: filter_fmri_tr = filter_fmri.copy() filter_fmri_tr['task'] = t try: example_file = layout_for_tr.get(**filter_fmri_tr)[0] except IndexError: raise MissingFile(f"no imaging file found for task {t}") tr_dict[t] = layout_for_tr.get_metadata( example_file.path)['RepetitionTime'] self._results['fmri_prep'] = [file.path for file in fmri_prep] if self.inputs.ica_aroma: self._results['fmri_prep_aroma'] = [ file.path for file in fmri_prep_aroma ] self._results['conf_raw'] = [file.path for file in conf_raw] self._results['conf_json'] = [file.path for file in conf_json] self._results['entities'] = entities self._results['tr_dict'] = tr_dict return runtime
def get_files(subject_id, session, task, raw_data_dir, preprocessed_data_dir, space=None, run=[], strict=True, **kwargs): """ Given some information, retrieve all the files and metadata from a BIDS-formatted dataset that will be passed to the analysis pipeline. """ from bids import BIDSLayout # only the raw files have the correct metadata, eg TR, and the event files are here raw_layout = BIDSLayout(raw_data_dir, validate=False, derivatives=False) preproc_layout = BIDSLayout(preprocessed_data_dir, validate=False) subjects = preproc_layout.get_subjects() assert subject_id in subjects and subject_id in raw_layout.get_subjects( ), "Subject not found!" sessions = preproc_layout.get_sessions() assert session in sessions, "Session not found!" tasks = preproc_layout.get_tasks() assert task in tasks, "Task not found!" if space == "None": space = None if space is None: print("Space is None") bolds = sorted([ f for f in preproc_layout.get(subject=subject_id, session=session, task=task, run=run, suffix='bold', extension=['nii.gz'], return_type='file') ]) else: bolds = sorted([ f for f in preproc_layout.get(subject=subject_id, session=session, task=task, run=run, suffix='bold', extension=['nii.gz'], return_type='file') if f"space-{space}" in f ]) print(f"BOLDS: {len(bolds)}\n{bolds}") if space is None: masks = sorted([ f for f in preproc_layout.get(subject=subject_id, suffix='mask', session=session, task=task, extension=['nii.gz'], return_type='file') ]) if not masks: masks = sorted([ f for f in preproc_layout.get(subject=subject_id, suffix='mask', session=session, extension=['nii.gz'], return_type='file') ]) else: masks = sorted([ f for f in preproc_layout.get(subject=subject_id, suffix='mask', session=session, task=task, extension=['nii.gz'], return_type='file') if f"space-{space}" in f ]) if not masks: masks = sorted([ f for f in preproc_layout.get(subject=subject_id, suffix='mask', session=session, extension=['nii.gz'], return_type='file') if f"space-{space}" in f ]) if len(masks ) == 1: # there is only one mask and it is to be used for all runs masks = masks * len(bolds) print(f"Masks: {len(masks)}\n{masks}") eventfiles = sorted( raw_layout.get(subject=subject_id, suffix='events', task=task, session=session, run=run, extension=['tsv'], return_type='file')) print(f"Eventfiles: {len(eventfiles)}\n{eventfiles}") raw_bolds = sorted( raw_layout.get(subject=subject_id, suffix='bold', task=task, session=session, run=run, extension=['nii.gz'], return_type='file')) TRs = [raw_layout.get_metadata(f)['RepetitionTime'] for f in raw_bolds] print(TRs, len(TRs)) confounds = sorted( preproc_layout.get(subject=subject_id, suffix="regressors", task=task, session=session, run=run, extension=['tsv'], return_type='file')) print(f"Confounds: {len(confounds)}\n{confounds}") if not confounds: confounds = [''] * len(bolds) #print(list(zip(bolds, masks, eventfiles, TRs))) # edit 11/9/18 - remove assert on event files, since some early hemifield scans don't have it # but warn! if (len(eventfiles) != len(bolds)): print("Some functional runs do not have corresponding event files!") assert TRs.count(TRs[0]) == len( TRs ), "Not all TRs are the same!" # all runs for a particular task must have same TR if strict: assert len(bolds) == len( masks ) > 0, "Input lists are not the same length!" # used to also check for ==len(confounds) TR = TRs[0] return bolds, masks, eventfiles, TR, confounds
def main(arglist): """Preprocess NYU CBI Prisma data""" # Parse arguments parser = argparse.ArgumentParser() parser.add_argument( '-subject', help= ('Freesurfer subject id. Note that we use the $SUBJECTS_DIR ' 'environmental variable to find the required data. Must be set if ' 'dir_structure==\'prisma\'. if dir_structure==\'bids\', will use ' 'the inferred BIDS subject id for the Freesurfer subject id *unless*' ' this is set, in which case we will use this instead')) parser.add_argument('-datadir', required=True, help='Raw MR data path') parser.add_argument( '-outdir', help='Output directory path. ignored if dir_structure==\'bids\'') parser.add_argument( '-epis', nargs='+', type=int, help=( 'EPI scan numbers, optional. If set (and you\'re using bids data), ' 'you must only have one task or use the bids_task input argument.' )) parser.add_argument( '-sbref', type=int, help= ('Single band reference scan number. Only required if dir_structure is' ' prisma (if bids, we determine it from filenames)')) parser.add_argument( '-distortPE', type=int, help=('Distortion scan number with same PE as epis. Only required if ' 'dir_structure is prisma (if bids, we determine it from the ' 'filenames).')) parser.add_argument( '-distortrevPE', type=int, help=( 'Distortion scan number with reverse PE as epis. Only required if ' 'dir_structure is prisma (if bids, we determine it from the ' 'filenames).')) parser.add_argument( '-PEdim', type=str, default='y', help=('PE dimension (x, y, or z). Only necessary if dir_structure is ' 'prisma (if bids, we determine it from metadata)')) parser.add_argument( "-plugin", type=str, default="MultiProc", help=( "Nipype plugin to use for running this. MultiProc (default) is " "normally fine for when running locally, though it may use up all " "your computer's resources. Linear is slower, but won't do that." "SLURM should be used on NYU HPC prince cluster.")) parser.add_argument( '-working_dir', default=None, help= ("Path to your working directory. By default, this will be within " "your output directory, but you may want to place it elsewhere. For " "example, on the HPC cluster, you may run out of space if this is in" "your /home directory, so you probably want this in /scratch")) parser.add_argument( '-dir_structure', default='prisma', help= ("{prisma, bids}. Is your data directory structured like it just came" " off the prisma scanner ('prisma') or is it BIDS structured " "('bids')? This determines how we look for the various scans. If " "your data is BIDS-structured, then datadir should point to the " "particular session you want to preprocess. Outputs will then be: " "{BIDS_dir}/derivatives/{bids_derivative_name}/{BIDS_subject_name}/" "{BIDS_session_name}/{BIDS_subject_name}_{BIDS_session_name}_" "{BIDS_task_name}_{run}_{bids_suffix}.nii.gz, where BIDS_subject_" "name, BIDS_session_name, and BIDS_task_name are all inferred from " "input data and BIDS_dir is two directories above datadir (since " "datadir corresponds to one BIDS session). We also preprocess tasks" " separately; if you have more than one task, use the -bids_task flag" " to specify which one you want to preprocess")) parser.add_argument( '-bids_derivative_name', default='preprocessed', help=("the name of the derivatives directory in which to place the " "outputs. ignored if dir_structure=='prisma'")) parser.add_argument( '-bids_suffix', default='preproc', help=( "the suffix to place at the end of the output filenames. ignored if" " dir_structure=='prisma'")) parser.add_argument( '-bids_task', default=None, help= ("Which bids task to preprocess. Only required if you have more than" " one task in your session directory (we only preprocess one task at" " a time). If you only have one task, we can grab its name from the" " filenames.")) parser.add_argument( '-plugin_args', default=None, help= ("Any additional arguments to pass to nipype's workflow.run as plugin" "_args. A single entry in the resulting dictionary should be of the" " format arg:val (e.g., n_procs:2) with multiple args separated by a" " comma with no spaces (e.g., n_procs:2,memory_gb:5). see nipype's " "plugin documentation for more details on possible values: " "http://nipype.readthedocs.io/en/latest/users/plugins.html. ")) args = vars(parser.parse_args(arglist)) try: subj_dir = os.environ['SUBJECTS_DIR'] if not os.path.isdir(subj_dir): raise Exception( "Unable to find your Freesurfer SUBJECTS_DIR! Is it on an external " "drive that you have not mounted?") except KeyError: raise Exception( "You do not have your Freesurfer SUBJECTS_DIR environmental variable set!" ) # Session paths and files session = dict() session['data'] = args['datadir'] if args['dir_structure'] == 'prisma': session['Freesurfer_subject_name'] = args['subject'] session['nii_temp'] = op.join(session['data'], '%02d+*', '*.nii') session['epis'] = [ glob(session['nii_temp'] % r)[0] for r in args['epis'] ] session['sbref'] = glob(session['nii_temp'] % args['sbref'])[0] session['distort_PE'] = glob(session['nii_temp'] % args['distortPE'])[0] session['distort_revPE'] = glob(session['nii_temp'] % args['distortrevPE'])[0] session['PE_dim'] = args['PEdim'] session['out'] = args['outdir'] session['out_names'] = [ "timeseries_corrected_run-%02d.nii.gz" % i for i in range(1, len(session['epis']) + 1) ] elif args['dir_structure'] == 'bids': layout = BIDSLayout(session['data'], validate=False) # _gets_BIDS_name returns a list, but we've already checked (for sub and ses) that it only # contains one value, so we just grab that session['BIDS_subject_name'] = _get_BIDS_name(layout, 'sub', session['data'])[0] session['BIDS_session_name'] = _get_BIDS_name(layout, 'ses', session['data'])[0] # this will be a list of task names (though in most cases, it will only contain one value) session['BIDS_task_names'] = _get_BIDS_name(layout, 'task', session['data'], args['bids_task']) if args['subject'] is None: # we do NOT want "sub-" in the Freesurfer subject name session['Freesurfer_subject_name'] = session[ 'BIDS_subject_name'].replace('sub-', '') else: session['Freesurfer_subject_name'] = args['subject'] test_files = layout.get( 'tuple', extensions=['nii', 'nii.gz'], suffix='bold', task=[i.replace('task-', '') for i in session["BIDS_task_names"]]) if args['epis'] is not None: # then we assume that args['epis'] gives us the run numbers we want run_nums = args['epis'] assert len( session['BIDS_task_names'] ) == 1, "If epis argument is set, can only analyze one task!" task_names = [ session['BIDS_task_names'][0].replace('task-', '') for i in run_nums ] else: run_nums = [i.run for i in test_files] task_names = [i.task for i in test_files] for i, n in zip(run_nums, task_names): if len( layout.get('file', extensions=['nii', 'nii.gz'], suffix='bold', run=i, task=n)) != 1: raise Exception( "Found zero or multiple bold nifti files with run %s, task %s! We " "require that there only be 1." % i, n) session['epis'] = layout.get('file', extensions=['nii', 'nii.gz'], suffix='bold', run=run_nums, task=task_names) if len(session['epis']) == 0: raise Exception( "Unable to find any epis! This probably means your pybids version isn'" "t new enough; it must be version >= 0.7!") session['sbref'] = layout.get('file', extensions=['nii', 'nii.gz'], suffix='sbref')[0] distortion_scans = layout.get('file', extensions=['nii', 'nii.gz'], suffix='epi') distortion_PEdirections = {} for scan in distortion_scans: distortion_PEdirections[layout.get_metadata(scan) ['PhaseEncodingDirection']] = scan epi_PEdirections = [] for scan in session['epis']: epi_PEdirections.append( layout.get_metadata(scan)['PhaseEncodingDirection']) if len(set(epi_PEdirections)) != 1: raise Exception( "Cannot find unique phase encoding direction for your functional data" " in data directory %s!" % session['data']) # we want PEdim to be x, y, or z, but coming from BIDS jsons it will be one of i, j, k PE_conversion_dict = { 'i': 'x', 'j': 'y', 'k': 'z', 'i-': 'x-', 'j-': 'y-', 'k-': 'z-' } session['PE_dim'] = PE_conversion_dict[epi_PEdirections[0]] session['distort_PE'] = distortion_PEdirections[epi_PEdirections[0]] if '-' not in epi_PEdirections[0]: session['distort_revPE'] = distortion_PEdirections[ "%s-" % epi_PEdirections[0]] else: session['distort_revPE'] = distortion_PEdirections[ epi_PEdirections[0].replace('-', '')] session['bids_derivative_name'] = args['bids_derivative_name'] session['bids_suffix'] = args['bids_suffix'] out_dir = op.abspath( op.join(session['data'], "../..", ("derivatives/{bids_derivative_name}/{BIDS_subject_name}/" "{BIDS_session_name}/"))) session['out'] = out_dir.format(**session) # we replace the bids suffix _bold with _preproc (or whatever we set bids_suffix to # be). this way, 'bold' can occur elsewhere in the filename without causing any issues. session['out_names'] = [ op.split(i)[1].replace('_bold', '_' + session['bids_suffix']) for i in session['epis'] ] # MAKE SURE THIS IS GZIPPED session['out_names'] = [ i + '.gz' if not i.endswith('gz') else i for i in session['out_names'] ] else: raise Exception("Don't know what to do with dir_structure %s!" % args['dir_structure']) if not os.path.isdir( os.path.join(subj_dir, session['Freesurfer_subject_name'])): raise Exception( "Unable to find Freesurfer subject dir for subject %s. Maybe you should " "use the -subject flag to specify an alternate name?" % (session['Freesurfer_subject_name'])) if not op.exists(session['out']): os.makedirs(session['out']) if args['working_dir'] is not None: session['working_dir'] = args['working_dir'] else: session['working_dir'] = session['out'] if not op.exists(session["working_dir"]): os.makedirs(session['working_dir']) session['plugin_args'] = {} if args['plugin_args'] is not None: for arg in args['plugin_args'].split(','): if len(arg.split(':')) != 2: raise Exception( "Your plugin_args is incorrectly formatted, each should contain one colon!" ) k, v = arg.split(':') try: session['plugin_args'][k] = int(v) except ValueError: try: session['plugin_args'][k] = float(v) except ValueError: session['plugin_args'][k] = v session['plugin'] = args['plugin'] # Dump session info to json with open(op.join(session['out'], 'session.json'), 'w') as sess_file: json.dump(session, sess_file, sort_keys=True, indent=4) # Define preprocessing worklow preproc_wf = create_preproc_workflow(session) # Execute workflow in parallel preproc_wf.run(session["plugin"], plugin_args=session['plugin_args'])
def _run_interface(self, runtime): # Validate derivatives argument derivatives, _ = BIDSValidate.validate_derivatives( bids_dir=self.inputs.bids_dir, derivatives=self.inputs.derivatives ) # Load layout layout = BIDSLayout( root=self.inputs.bids_dir, derivatives=derivatives, validate=False ) # Load pipelines pipelines_dicts = [] for pipeline in self.inputs.pipelines: pipelines_dicts.append(load_pipeline_from_json(pipeline)) # Check if there is at least one pipeline requiring aroma include_aroma = any(map(is_IcaAROMA, pipelines_dicts)) # Check if there is at least one pipeline requiring no armoa files include_no_aroma = not all(map(is_IcaAROMA, pipelines_dicts)) # Check missing files and act accordingly entities_files, (tasks, sessions, subjects, runs) = BIDSValidate.validate_files( layout=layout, tasks=self.inputs.tasks, sessions=self.inputs.sessions, subjects=self.inputs.subjects, runs=self.inputs.runs, include_aroma=include_aroma, include_no_aroma=include_no_aroma ) # Convert entities_files into separate lists of BIDSImageFile Objects def filter_entity(entity_files: t.List[t.Dict[str, t.Any]], key: str) -> t.List[str]: return list(entity[key].path for entity in entity_files if entity.get(key) is not None) conf_raw = filter_entity(entities_files, 'conf_raw') conf_json = filter_entity(entities_files, 'conf_json') if include_no_aroma: fmri_prep = filter_entity(entities_files, 'fmri_prep') else: fmri_prep = [] if include_aroma: fmri_prep_aroma = filter_entity(entities_files, 'fmri_prep_aroma') else: fmri_prep_aroma = [] # Extract TR for specific tasks tr_dict = {} # TODO: this is just a funny workaround, look for better solution later layout_for_tr = BIDSLayout( root=self.inputs.bids_dir, derivatives=derivatives, validate=True, index_metadata=True ) for task in tasks: filter_fmri_tr = { 'extension': ['nii', 'nii.gz'], 'suffix': 'bold', 'desc': 'preproc', 'space': 'MNI152NLin2009cAsym', 'task': task } try: example_file = layout_for_tr.get(**filter_fmri_tr)[0] except IndexError: raise MissingFile(f'no imaging file found for task {task}') tr_dict[task] = layout_for_tr.get_metadata(example_file.path)[ 'RepetitionTime'] # check space # TODO: # spaces = layout.get_entities()['space'].unique() # for space in spaces: # get_parcellation_file_path(space) # Prepare outputs self._results['fmri_prep'] = fmri_prep self._results['fmri_prep_aroma'] = fmri_prep_aroma self._results['conf_raw'] = conf_raw self._results['conf_json'] = conf_json self._results['tasks'] = tasks self._results['sessions'] = sessions self._results['subjects'] = subjects self._results['runs'] = runs self._results['pipelines'] = pipelines_dicts self._results['tr_dict'] = tr_dict return runtime