示例#1
0
def test_get_entities_from_fname_errors(fname):
    """Test parsing entities from bids filename.

    Extends utility for not supported BIDS entities, such
    as 'description'.
    """
    if 'desc' in fname:
        with pytest.raises(KeyError, match='Unexpected entity'):
            params = get_entities_from_fname(fname, on_error='raise')
        with pytest.warns(RuntimeWarning, match='Unexpected entity'):
            params = get_entities_from_fname(fname, on_error='warn')
        params = get_entities_from_fname(fname, on_error='ignore')
    else:
        params = get_entities_from_fname(fname, on_error='raise')

    expected_keys = [
        'subject', 'session', 'task', 'acquisition', 'run', 'processing',
        'space', 'recording', 'split', 'suffix'
    ]

    assert params['subject'] == '01'
    assert params['session'] == '02'
    assert params['run'] == '3'
    assert params['task'] == 'test'
    assert params['split'] == '01'
    if 'meg' in fname:
        assert params['suffix'] == 'meg'
    if 'desc' in fname:
        assert params['desc'] == 'tfr'
        expected_keys.append('desc')
    assert list(params.keys()) == expected_keys
示例#2
0
    def find_result_path(self):
        """
        Identifies the directory path pointing to where results stored

        Following BIDS requirements, we only have either the subject folder or both subject and session.

        Parameters
        ----------
        none

        Returns
        -------
        result_path: str
            location of results files within BIDS folder

        """
        params = get_entities_from_fname(self.unique_name)
        if params["ses"] is None:
            result_path = os.path.join(self.root_path, "derivatives",
                                       "automagic", f"sub-{params['sub']}")
        else:
            result_path = os.path.join(
                self.root_path,
                "derivatives",
                "automagic",
                f"sub-{params['sub']}",
                f"ses-{params['ses']}",
            )
        return result_path
示例#3
0
def test_get_entities_from_fname(fname):
    """Test parsing entities from a bids filename."""
    params = get_entities_from_fname(fname)
    assert params['subject'] == '01'
    assert params['session'] == '02'
    assert params['run'] == '3'
    assert params['task'] == 'test'
    assert params['split'] == '01'
    assert list(params.keys()) == ['subject', 'session', 'task',
                                   'acquisition', 'run', 'processing',
                                   'space', 'recording', 'split']
示例#4
0
def load_data(bids_basename):
    # read_raw_bids automatically
    # - populates bad channels using the BIDS channels.tsv
    # - sets channels types according to BIDS channels.tsv `type` column
    # - sets raw.annotations using the BIDS events.tsv

    params = get_entities_from_fname(bids_basename)
    subject = params['subject']
    session = params['session']

    extra_params = dict()
    if config.allow_maxshield:
        extra_params['allow_maxshield'] = config.allow_maxshield

    raw = read_raw_bids(bids_basename=bids_basename,
                        bids_root=config.bids_root,
                        extra_params=extra_params,
                        kind=config.get_kind())

    if config.daysback is not None:
        raw.anonymize(daysback=config.daysback)

    if subject != 'emptyroom':
        # Crop the data.
        if config.crop is not None:
            raw.crop(*config.crop)

        # Rename events.
        if config.rename_events:
            rename_events(raw=raw, subject=subject, session=session)

    raw.load_data()
    if hasattr(raw, 'fix_mag_coil_types'):
        raw.fix_mag_coil_types()

    montage_name = config.eeg_template_montage
    if config.get_kind() == 'eeg' and montage_name:
        msg = (f'Setting EEG channel locatiions to template montage: '
               f'{montage_name}.')
        logger.info(gen_log_message(message=msg, step=1, subject=subject,
                                    session=session))
        montage = mne.channels.make_standard_montage(montage_name)
        raw.set_montage(montage, on_missing='warn')

    return raw
示例#5
0
def convert_img_to_bids(image_input, bids_root, bids_fname, verbose=True):
    """Run Bids Conversion script to be updated.

    Performs BIDS conversion for Ct/T1/DTI/fMRI data.

    TODO: demo for DTI/FMRI
    """
    if verbose:
        print(f"bids_root is {bids_root}")
        print(f"Reading in image files from: {image_input}")

    # create temporary filepath to store the nifti file
    with tempfile.TemporaryDirectory() as tmpdir:
        output_fpath = Path(tmpdir, "tmp.nii").as_posix()

        if len([x for x in Path(image_input).glob("*.dcm")]) > 0:
            print("Converting dicom -> Nifti...")
            # try to run mrconvert and reorient to `LAS` direction
            # try:
            image_input = _convert_dicom_to_nifti(image_input, output_fpath)
            # except Exception as e:
            #     "mrconvert {params.CT_FOLDER} {output.CT_bids_fname};"
        else:
            print("Passed NIFTI image, so skipping mrconvert from dicom -> nifti...")
            image_input = str(image_input)

        print(image_input)
        # determine the BIDS identifiers
        params = get_entities_from_fname(bids_fname)
        subject = params["sub"]
        session = params["ses"]

        print("\n\nWriting now to BIDS...")
        # write to BIDS
        anat_dir = write_anat(
            bids_root,
            subject,
            t1w=image_input,
            session=session,
            overwrite=True,
            verbose=True,
        )
示例#6
0
def _read_dig_bids(electrodes_fpath, coordsystem_fpath,
                   raw, datatype, verbose):
    """Read MNE-Python formatted DigMontage from BIDS files.

    Handles coordinatesystem.json and electrodes.tsv reading
    to DigMontage.

    Parameters
    ----------
    electrodes_fpath : str
        Filepath of the electrodes.tsv to read.
    coordsystem_fpath : str
        Filepath of the coordsystem.json to read.
    raw : instance of Raw
        The data as MNE-Python Raw object.
    datatype : str
        Type of the data recording. Can be ``meg``, ``eeg``,
        or ``ieeg``.
    verbose : bool
        Set verbose output to true or false.

    Returns
    -------
    raw : instance of Raw
        The data as MNE-Python Raw object.
    """
    # get the space entity
    params = get_entities_from_fname(electrodes_fpath)
    space = params['space']
    if space is None:
        space = ''
    space = space.lower()

    # read in coordinate information
    coord_frame, coord_unit = _handle_coordsystem_reading(coordsystem_fpath,
                                                          datatype, verbose)

    if datatype == 'meg':
        if coord_frame not in BIDS_MEG_COORDINATE_FRAMES:
            warn("MEG Coordinate frame is not accepted "
                 "BIDS keyword. The allowed keywords are: "
                 "{}".format(BIDS_MEG_COORDINATE_FRAMES))
            coord_frame = None
        elif coord_frame == 'other':
            warn("Coordinate frame of MEG data can't be determined "
                 "when 'other'. The currently accepted keywords are: "
                 "{}".format(BIDS_MEG_COORDINATE_FRAMES))
            coord_frame = None
        else:
            coord_frame = BIDS_TO_MNE_FRAMES.get(coord_frame, None)
    elif datatype == 'ieeg':
        if coord_frame not in BIDS_IEEG_COORDINATE_FRAMES:
            warn("iEEG Coordinate frame is not accepted "
                 "BIDS keyword. The allowed keywords are: "
                 "{}".format(BIDS_IEEG_COORDINATE_FRAMES))
            coord_frame = None
        elif coord_frame == 'pixels':
            warn("Coordinate frame of iEEG data in pixels does not "
                 "get read in by mne-python. Skipping reading of "
                 "electrodes.tsv ...")
            coord_frame = None
        elif coord_frame == 'acpc':
            coord_frame = BIDS_TO_MNE_FRAMES.get(coord_frame, None)
        elif coord_frame == 'other':
            # XXX: We allow 'other' coordinate frames, but must be mne-python
            if space not in BIDS_TO_MNE_FRAMES:
                # default coordinate frames to available ones in mne-python
                # noqa: see https://bids-specification.readthedocs.io/en/stable/99-appendices/08-coordinate-systems.html
                warn("Defaulting coordinate frame to unknown "
                     "from coordinate system input {}".format(coord_frame))
            coord_frame = BIDS_TO_MNE_FRAMES.get(space, None)
    elif datatype == 'eeg':
        # only accept captrak
        if coord_frame not in BIDS_EEG_COORDINATE_FRAMES:
            warn("EEG Coordinate frame is not accepted "
                 "BIDS keyword. The allowed keywords are: "
                 "{}".format(BIDS_IEEG_COORDINATE_FRAMES))
            coord_frame = None
        else:
            coord_frame = BIDS_TO_MNE_FRAMES.get(coord_frame, None)

    # check coordinate units
    if coord_unit not in BIDS_COORDINATE_UNITS:
        warn("Coordinate unit is not an accepted BIDS unit for {}. "
             "Please specify to be one of {}. Skipping electrodes.tsv "
             "reading..."
             .format(electrodes_fpath, BIDS_COORDINATE_UNITS))
        coord_frame = None

    # only set montage if coordinate frame was properly parsed
    if coord_frame is not None:
        # read in electrode coordinates and attach to raw
        raw = _handle_electrodes_reading(electrodes_fpath, coord_frame,
                                         coord_unit, raw, verbose)

    return raw
示例#7
0
def _summarize_channels_tsv(root, scans_fpaths, verbose=True):
    """Summarize channels.tsv data in BIDS root directory.

    Currently, summarizes all REQUIRED components of channels
    data, and some RECOMMENDED and OPTIONAL components.

    Parameters
    ----------
    root : str | pathlib.Path
        The path of the root of the BIDS compatible folder.
    scans_fpaths : list
        A list of all *_scans.tsv files in ``root``. The summary
        will occur for all scans listed in the *_scans.tsv files.
    verbose : bool

    Returns
    -------
    template_dict : dict
        A dictionary of values for various template strings.
    """
    root = Path(root)

    # keep track of channel type, status
    ch_status_count = {'bad': [], 'good': []}
    ch_count = []

    # loop through each scan
    for scan_fpath in scans_fpaths:
        # load in the scans.tsv file
        # and read metadata for each scan
        scans_tsv = _from_tsv(scan_fpath)
        scans = scans_tsv['filename']
        for scan in scans:
            # summarize metadata of recordings
            bids_path, _ = _parse_ext(scan)
            datatype = op.dirname(scan)
            if datatype not in ['meg', 'eeg', 'ieeg']:
                continue

            # convert to BIDS Path
            params = get_entities_from_fname(bids_path)
            bids_path = BIDSPath(root=root, **params)

            # XXX: improve to allow emptyroom
            if bids_path.subject == 'emptyroom':
                continue

            channels_fname = _find_matching_sidecar(bids_path=bids_path,
                                                    suffix='channels',
                                                    extension='.tsv')

            # summarize channels.tsv
            channels_tsv = _from_tsv(channels_fname)
            for status in ch_status_count.keys():
                ch_status = [
                    ch for ch in channels_tsv['status'] if ch == status
                ]
                ch_status_count[status].append(len(ch_status))
            ch_count.append(len(channels_tsv['name']))

    # create summary template strings for status
    template_dict = {
        'mean_chs': np.mean(ch_count),
        'std_chs': np.std(ch_count),
        'mean_good_chs': np.mean(ch_status_count['good']),
        'std_good_chs': np.std(ch_status_count['good']),
        'mean_bad_chs': np.mean(ch_status_count['bad']),
        'std_bad_chs': np.std(ch_status_count['bad']),
    }
    for key, val in template_dict.items():
        template_dict[key] = round(val, 2)
    return template_dict
示例#8
0
def _summarize_sidecar_json(root, scans_fpaths, verbose=True):
    """Summarize scans in BIDS root directory.

    Parameters
    ----------
    root : str | pathlib.Path
        The path of the root of the BIDS compatible folder.
    scans_fpaths : list
        A list of all *_scans.tsv files in ``root``. The summary
        will occur for all scans listed in the *_scans.tsv files.
    verbose : bool
        Set verbose output to true or false.

    Returns
    -------
    template_dict : dict
        A dictionary of values for various template strings.
    """
    n_scans = 0
    powerlinefreqs, sfreqs = set(), set()
    manufacturers = set()
    length_recordings = []

    # loop through each scan
    for scan_fpath in scans_fpaths:
        # load in the scans.tsv file
        # and read metadata for each scan
        scans_tsv = _from_tsv(scan_fpath)
        scans = scans_tsv['filename']
        for scan in scans:
            # summarize metadata of recordings
            bids_path, ext = _parse_ext(scan)
            datatype = op.dirname(scan)
            if datatype not in ALLOWED_DATATYPES:
                continue

            n_scans += 1

            # convert to BIDS Path
            params = get_entities_from_fname(bids_path)
            bids_path = BIDSPath(root=root, **params)

            # XXX: improve to allow emptyroom
            if bids_path.subject == 'emptyroom':
                continue

            sidecar_fname = _find_matching_sidecar(bids_path=bids_path,
                                                   suffix=datatype,
                                                   extension='.json')
            with open(sidecar_fname, 'r', encoding='utf-8-sig') as fin:
                sidecar_json = json.load(fin)

            # aggregate metadata from each scan
            # REQUIRED kwargs
            sfreq = sidecar_json['SamplingFrequency']
            powerlinefreq = str(sidecar_json['PowerLineFrequency'])
            software_filters = sidecar_json.get('SoftwareFilters')
            if not software_filters:
                software_filters = 'n/a'

            # RECOMMENDED kwargs
            manufacturer = sidecar_json.get('Manufacturer', 'n/a')
            record_duration = sidecar_json.get('RecordingDuration', 'n/a')

            sfreqs.add(str(np.round(sfreq, 2)))
            powerlinefreqs.add(str(powerlinefreq))
            if manufacturer != 'n/a':
                manufacturers.add(manufacturer)
            length_recordings.append(record_duration)

    # XXX: length summary is only allowed, if no 'n/a' was found
    if any([dur == 'n/a' for dur in length_recordings]):
        length_recordings = None

    template_dict = {
        'n_scans': n_scans,
        'manufacturer': list(manufacturers),
        'sfreq': sfreqs,
        'powerlinefreq': powerlinefreqs,
        'software_filters': software_filters,
        'length_recordings': length_recordings,
    }
    return template_dict
示例#9
0
文件: dig.py 项目: behinger/mne-bids
def _write_dig_bids(electrodes_path,
                    coordsystem_path,
                    root,
                    raw,
                    datatype,
                    overwrite=False,
                    verbose=True):
    """Write BIDS formatted DigMontage from Raw instance.

    Handles coordinatesystem.json and electrodes.tsv writing
    from DigMontage.

    Parameters
    ----------
    electrodes_path : str
        Filename to save the electrodes.tsv to.
    coordsystem_path : str
        Filename to save the coordsystem.json to.
    root : str | pathlib.Path
        Path to the data directory
    raw : instance of Raw
        The data as MNE-Python Raw object.
    datatype : str
        Type of the data recording. Can be ``meg``, ``eeg``,
        or ``ieeg``.
    overwrite : bool
        Whether to overwrite the existing file.
        Defaults to False.
    verbose : bool
        Set verbose output to true or false.
    """
    # write electrodes data for iEEG and EEG
    unit = "m"  # defaults to meters

    params = get_entities_from_fname(electrodes_path)
    subject_id = params['subject']
    session_id = params['session']
    acquisition = params['acquisition']

    # get coordinate frame from digMontage
    digpoint = raw.info['dig'][0]
    if any(digpoint['coord_frame'] != _digpoint['coord_frame']
           for _digpoint in raw.info['dig']):
        warn("Not all digpoints have the same coordinate frame. "
             "Skipping electrodes.tsv writing...")
        return

    # get the accepted mne-python coordinate frames
    coord_frame_int = int(digpoint['coord_frame'])
    mne_coord_frame = MNE_FRAME_TO_STR.get(coord_frame_int, None)
    coord_frame = MNE_TO_BIDS_FRAMES.get(mne_coord_frame, None)

    if verbose:
        print("Writing electrodes file to... ", electrodes_path)
        print("Writing coordsytem file to... ", coordsystem_path)

    if datatype == "ieeg":
        if coord_frame is not None:
            # XXX: To improve when mne-python allows coord_frame='unknown'
            if coord_frame not in BIDS_IEEG_COORDINATE_FRAMES:
                coordsystem_path = BIDSPath(subject=subject_id,
                                            session=session_id,
                                            acquisition=acquisition,
                                            space=coord_frame,
                                            suffix='coordsystem',
                                            extension='.json',
                                            root=root)
                electrodes_path = BIDSPath(subject=subject_id,
                                           session=session_id,
                                           acquisition=acquisition,
                                           space=coord_frame,
                                           suffix='electrodes',
                                           extension='.tsv',
                                           root=root)
                coord_frame = 'Other'

            # Now write the data to the elec coords and the coordsystem
            _electrodes_tsv(raw, electrodes_path, datatype, overwrite, verbose)
            _coordsystem_json(raw, unit, 'n/a', coord_frame, coordsystem_path,
                              datatype, overwrite, verbose)
        else:
            # default coordinate frame to mri if not available
            warn("Coordinate frame of iEEG coords missing/unknown "
                 "for {}. Skipping reading "
                 "in of montage...".format(electrodes_path))
    elif datatype == 'eeg':
        # We only write EEG electrodes.tsv and coordsystem.json
        # if we have LPA, RPA, and NAS available to rescale to a known
        # coordinate system frame
        coords = _extract_landmarks(raw.info['dig'])
        landmarks = set(['RPA', 'NAS', 'LPA']) == set(list(coords.keys()))

        # XXX: to be improved to allow rescaling if landmarks are present
        # mne-python automatically converts unknown coord frame to head
        if coord_frame_int == FIFF.FIFFV_COORD_HEAD and landmarks:
            # Now write the data
            _electrodes_tsv(raw, electrodes_path, datatype, overwrite, verbose)
            _coordsystem_json(raw, 'm', 'RAS', 'CapTrak', coordsystem_path,
                              datatype, overwrite, verbose)
        else:
            warn("Skipping EEG electrodes.tsv... "
                 "Setting montage not possible if anatomical "
                 "landmarks (NAS, LPA, RPA) are missing, "
                 "and coord_frame is not 'head'.")