Example #1
0
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('-'))
Example #5
0
    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
Example #6
0
    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
Example #7
0
    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)
Example #8
0
    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
Example #9
0
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
Example #10
0
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'])
Example #11
0
    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