Example #1
0
def test_copyfile_kit():
    """Test copying and renaming KIT files to a new location."""
    output_path = _TempDir()
    data_path = op.join(base_path, 'kit', 'tests', 'data')
    raw_fname = op.join(data_path, 'test.sqd')
    hpi_fname = op.join(data_path, 'test_mrk.sqd')
    electrode_fname = op.join(data_path, 'test.elp')
    headshape_fname = op.join(data_path, 'test.hsp')
    subject_id = '01'
    session_id = '01'
    run = '01'
    acq = '01'
    task = 'testing'

    bids_basename = make_bids_basename(
        subject=subject_id, session=session_id, run=run, acquisition=acq,
        task=task)

    kit_bids_basename = bids_basename.copy().update(acquisition=None,
                                                    prefix=output_path)

    raw = mne.io.read_raw_kit(
        raw_fname, mrk=hpi_fname, elp=electrode_fname,
        hsp=headshape_fname)
    _, ext = _parse_ext(raw_fname, verbose=True)
    kind = _handle_kind(raw)
    bids_fname = str(bids_basename.copy().update(suffix=f'{kind}{ext}',
                                                 prefix=output_path))

    copyfile_kit(raw_fname, bids_fname, subject_id, session_id,
                 task, run, raw._init_kwargs)
    assert op.exists(bids_fname)
    _, ext = _parse_ext(hpi_fname, verbose=True)
    if ext == '.sqd':
        kit_bids_basename.suffix = 'markers.sqd'
        assert op.exists(kit_bids_basename)
    elif ext == '.mrk':
        kit_bids_basename.suffix = 'markers.mrk'
        assert op.exists(kit_bids_basename)

    if op.exists(electrode_fname):
        task, run, key = None, None, 'ELP'
        elp_ext = '.pos'
        elp_fname = make_bids_basename(
            subject=subject_id, session=session_id, task=task, run=run,
            acquisition=key, suffix='headshape%s' % elp_ext,
            prefix=output_path)
        assert op.exists(elp_fname)

    if op.exists(headshape_fname):
        task, run, key = None, None, 'HSP'
        hsp_ext = '.pos'
        hsp_fname = make_bids_basename(
            subject=subject_id, session=session_id, task=task, run=run,
            acquisition=key, suffix='headshape%s' % hsp_ext,
            prefix=output_path)
        assert op.exists(hsp_fname)
Example #2
0
def test_parse_ext():
    """Test the file extension extraction."""
    f = 'sub-05_task-matchingpennies.vhdr'
    fname, ext = _parse_ext(f)
    assert fname == 'sub-05_task-matchingpennies'
    assert ext == '.vhdr'

    # Test for case where no extension: assume BTI format
    f = 'sub-01_task-rest'
    fname, ext = _parse_ext(f)
    assert fname == f
    assert ext == '.pdf'

    # Get a .nii.gz file
    f = 'sub-01_task-rest.nii.gz'
    fname, ext = _parse_ext(f)
    assert fname == 'sub-01_task-rest'
    assert ext == '.nii.gz'
Example #3
0
def _read_raw(raw_fpath,
              electrode=None,
              hsp=None,
              hpi=None,
              config=None,
              verbose=None,
              **kwargs):
    """Read a raw file into MNE, making inferences based on extension."""
    _, ext = _parse_ext(raw_fpath)

    # KIT systems
    if ext in ['.con', '.sqd']:
        raw = io.read_raw_kit(raw_fpath,
                              elp=electrode,
                              hsp=hsp,
                              mrk=hpi,
                              preload=False,
                              **kwargs)

    # BTi systems
    elif ext == '.pdf':
        raw = io.read_raw_bti(raw_fpath,
                              config_fname=config,
                              head_shape_fname=hsp,
                              preload=False,
                              verbose=verbose,
                              **kwargs)

    elif ext == '.fif':
        raw = reader[ext](raw_fpath, **kwargs)

    elif ext in ['.ds', '.vhdr', '.set']:
        raw = reader[ext](raw_fpath, **kwargs)

    # EDF (european data format) or BDF (biosemi) format
    # TODO: integrate with lines above once MNE can read
    # annotations with preload=False
    elif ext in ['.edf', '.bdf']:
        raw = reader[ext](raw_fpath, preload=True, **kwargs)

    # MEF and NWB are allowed, but not yet implemented
    elif ext in ['.mef', '.nwb']:
        raise ValueError(
            'Got "{}" as extension. This is an allowed extension '
            'but there is no IO support for this file format yet.'.format(ext))

    # No supported data found ...
    # ---------------------------
    else:
        raise ValueError('Raw file name extension must be one of {}\n'
                         'Got {}'.format(ALLOWED_EXTENSIONS, ext))
    return raw
Example #4
0
def get_matched_empty_room(bids_fname, bids_root):
    """Get matching empty room file.

    Parameters
    ----------
    bids_fname : str
        The filename for which to find the matching empty room file.
    bids_root : str
        Path to the BIDS root folder.

    Returns
    -------
    er_fname : str | None.
        The filename corresponding to the empty room.
        Returns None if no file found.
    """
    bids_fname = op.basename(bids_fname)
    _, ext = _parse_ext(bids_fname)

    raw = read_raw_bids(bids_fname, bids_root)
    if raw.info['meas_date'] is None:
        raise ValueError('Measurement date not available. Cannot get matching'
                         ' empty room file')

    ref_date = raw.info['meas_date']
    if not isinstance(ref_date, datetime):
        # for MNE < v0.20
        ref_date = datetime.fromtimestamp(raw.info['meas_date'][0])
    search_path = make_bids_folders(bids_root=bids_root, subject='emptyroom',
                                    session='**', make_dir=False)
    search_path = op.join(search_path, '**', '**%s' % ext)
    er_fnames = glob.glob(search_path)

    best_er_fname = None
    min_seconds = np.inf
    for er_fname in er_fnames:
        params = _parse_bids_filename(er_fname, verbose=False)
        dt = datetime.strptime(params['ses'], '%Y%m%d')
        dt = dt.replace(tzinfo=ref_date.tzinfo)
        delta_t = dt - ref_date
        if abs(delta_t.total_seconds()) < min_seconds:
            min_seconds = abs(delta_t.total_seconds())
            best_er_fname = er_fname

    return best_er_fname
Example #5
0
    def __init__(self, bids_root, bids_fname: str, verbose: bool = True):
        super(BidsRun, self).__init__(bids_root=bids_root)

        # ensures just base path
        self.bids_fname = os.path.basename(bids_fname)

        # what is the modality -- meg, eeg, ieeg to read
        self.bids_basename = "_".join(bids_fname.split("_")[:-1])
        self.kind = bids_fname.split("_")[-1].split(".")[0]

        # extract BIDS parameters from the bids filename to be loaded/modified
        # gets: subjectid, sessionid, acquisition type, task name, runid, file extension
        params = _parse_bids_filename(self.bids_fname, verbose=verbose)
        self.subject_id, self.session_id = params["sub"], params["ses"]
        self.acquisition, self.task, self.run = (
            params["acq"],
            params["task"],
            params["run"],
        )
        _, self.ext = _parse_ext(self.bids_fname)

        self.result_fpath = None

        # instantiate a loader/writer
        self.loader = BidsLoader(
            bids_root=self.bids_root,
            bids_basename=self.bids_basename,
            kind=self.kind,
            datatype=self.ext,
        )
        self.writer = BidsWriter(
            bids_root=self.bids_root,
            bids_basename=self.bids_basename,
            kind=self.kind,
            datatype=self.ext,
        )

        if not os.path.exists(self.loader.datafile_fpath):
            raise RuntimeError(
                f"Bids dataset run does not exist at {self.loader.datafile_fpath}. "
                f"Please first create the dataset in BIDS format."
            )
Example #6
0
def get_head_mri_trans(bids_basename, bids_root):
    """Produce transformation matrix from MEG and MRI landmark points.

    Will attempt to read the landmarks of Nasion, LPA, and RPA from the sidecar
    files of (i) the MEG and (ii) the T1 weighted MRI data. The two sets of
    points will then be used to calculate a transformation matrix from head
    coordinates to MRI coordinates.

    Parameters
    ----------
    bids_basename : str | BIDSPath
        The base filename of the BIDS-compatible file. Typically, this can be
        generated using :func:`mne_bids.make_bids_basename`.
    bids_root : str | pathlib.Path
        Path to root of the BIDS folder

    Returns
    -------
    trans : instance of mne.transforms.Transform
        The data transformation matrix from head to MRI coordinates

    """
    if not has_nibabel():  # pragma: no cover
        raise ImportError('This function requires nibabel.')
    import nibabel as nib

    # convert to BIDS Path
    if isinstance(bids_basename, str):
        params = _parse_bids_filename(bids_basename, False)
        bids_basename = BIDSPath(subject=params.get('sub'),
                                 session=params.get('ses'),
                                 recording=params.get('rec'),
                                 acquisition=params.get('acq'),
                                 processing=params.get('proc'),
                                 space=params.get('space'),
                                 run=params.get('run'),
                                 task=params.get('task'))

    # Get the sidecar file for MRI landmarks
    bids_fname = bids_basename.get_bids_fname(kind='meg', bids_root=bids_root)
    t1w_json_path = _find_matching_sidecar(bids_fname, bids_root, 'T1w.json')

    # Get MRI landmarks from the JSON sidecar
    with open(t1w_json_path, 'r') as f:
        t1w_json = json.load(f)
    mri_coords_dict = t1w_json.get('AnatomicalLandmarkCoordinates', dict())
    mri_landmarks = np.asarray(
        (mri_coords_dict.get('LPA',
                             np.nan), mri_coords_dict.get('NAS', np.nan),
         mri_coords_dict.get('RPA', np.nan)))
    if np.isnan(mri_landmarks).any():
        raise RuntimeError(
            'Could not parse T1w sidecar file: "{}"\n\n'
            'The sidecar file MUST contain a key '
            '"AnatomicalLandmarkCoordinates" pointing to a '
            'dict with keys "LPA", "NAS", "RPA". '
            'Yet, the following structure was found:\n\n"{}"'.format(
                t1w_json_path, t1w_json))

    # The MRI landmarks are in "voxels". We need to convert the to the
    # neuromag RAS coordinate system in order to compare the with MEG landmarks
    # see also: `mne_bids.write.write_anat`
    t1w_path = t1w_json_path.replace('.json', '.nii')
    if not op.exists(t1w_path):
        t1w_path += '.gz'  # perhaps it is .nii.gz? ... else raise an error
    if not op.exists(t1w_path):
        raise RuntimeError(
            'Could not find the T1 weighted MRI associated '
            'with "{}". Tried: "{}" but it does not exist.'.format(
                t1w_json_path, t1w_path))
    t1_nifti = nib.load(t1w_path)
    # Convert to MGH format to access vox2ras method
    t1_mgh = nib.MGHImage(t1_nifti.dataobj, t1_nifti.affine)

    # now extract transformation matrix and put back to RAS coordinates of MRI
    vox2ras_tkr = t1_mgh.header.get_vox2ras_tkr()
    mri_landmarks = apply_trans(vox2ras_tkr, mri_landmarks)
    mri_landmarks = mri_landmarks * 1e-3

    # Get MEG landmarks from the raw file
    _, ext = _parse_ext(bids_basename)
    extra_params = None
    if ext == '.fif':
        extra_params = dict(allow_maxshield=True)

    raw = read_raw_bids(bids_basename=bids_basename,
                        bids_root=bids_root,
                        extra_params=extra_params,
                        kind='meg')
    meg_coords_dict = _extract_landmarks(raw.info['dig'])
    meg_landmarks = np.asarray((meg_coords_dict['LPA'], meg_coords_dict['NAS'],
                                meg_coords_dict['RPA']))

    # Given the two sets of points, fit the transform
    trans_fitted = fit_matched_points(src_pts=meg_landmarks,
                                      tgt_pts=mri_landmarks)
    trans = mne.transforms.Transform(fro='head', to='mri', trans=trans_fitted)
    return trans
Example #7
0
def get_matched_empty_room(bids_basename, bids_root):
    """Get matching empty-room file for an MEG recording.

    Parameters
    ----------
    bids_basename : str | BIDSPath
        The base filename of the BIDS-compatible file. Typically, this can be
        generated using :func:`mne_bids.make_bids_basename`.
    bids_root : str | pathlib.Path
        Path to the BIDS root folder.

    Returns
    -------
    er_basename : str | None.
        The basename corresponding to the best-matching empty-room measurement.
        Returns None if none was found.
    """
    # convert to BIDS Path
    if isinstance(bids_basename, str):
        params = _parse_bids_filename(bids_basename, False)
        bids_basename = BIDSPath(subject=params.get('sub'),
                                 session=params.get('ses'),
                                 recording=params.get('rec'),
                                 acquisition=params.get('acq'),
                                 processing=params.get('proc'),
                                 space=params.get('space'),
                                 run=params.get('run'),
                                 task=params.get('task'))

    kind = 'meg'  # We're only concerned about MEG data here
    bids_fname = bids_basename.get_bids_fname(kind=kind, bids_root=bids_root)
    _, ext = _parse_ext(bids_fname)
    if ext == '.fif':
        extra_params = dict(allow_maxshield=True)
    else:
        extra_params = None

    raw = read_raw_bids(bids_basename=bids_basename,
                        bids_root=bids_root,
                        kind=kind,
                        extra_params=extra_params)
    if raw.info['meas_date'] is None:
        raise ValueError('The provided recording does not have a measurement '
                         'date set. Cannot get matching empty-room file.')

    ref_date = raw.info['meas_date']
    if not isinstance(ref_date, datetime):
        # for MNE < v0.20
        ref_date = datetime.fromtimestamp(raw.info['meas_date'][0])

    emptyroom_dir = pathlib.Path(
        make_bids_folders(bids_root=bids_root,
                          subject='emptyroom',
                          make_dir=False))

    if not emptyroom_dir.exists():
        return None

    # Find the empty-room recording sessions.
    emptyroom_session_dirs = [
        x for x in emptyroom_dir.iterdir()
        if x.is_dir() and str(x.name).startswith('ses-')
    ]
    if not emptyroom_session_dirs:  # No session sub-directories found
        emptyroom_session_dirs = [emptyroom_dir]

    # Now try to discover all recordings inside the session directories.

    allowed_extensions = list(reader.keys())
    # `.pdf` is just a "virtual" extension for BTi data (which is stored inside
    # a dedicated directory that doesn't have an extension)
    del allowed_extensions[allowed_extensions.index('.pdf')]

    candidate_er_fnames = []
    for session_dir in emptyroom_session_dirs:
        dir_contents = glob.glob(
            op.join(session_dir, kind, f'sub-emptyroom_*_{kind}*'))
        for item in dir_contents:
            item = pathlib.Path(item)
            if ((item.suffix in allowed_extensions)
                    or (not item.suffix and item.is_dir())):  # Hopefully BTi?
                candidate_er_fnames.append(item.name)

    # Walk through recordings, trying to extract the recording date:
    # First, from the filename; and if that fails, from `info['meas_date']`.
    best_er_basename = None
    min_delta_t = np.inf
    date_tie = False

    failed_to_get_er_date_count = 0
    for er_fname in candidate_er_fnames:
        params = _parse_bids_filename(er_fname, verbose=False)
        er_meas_date = None

        er_bids_path = BIDSPath(subject='emptyroom',
                                session=params.get('ses', None),
                                task=params.get('task', None),
                                acquisition=params.get('acq', None),
                                run=params.get('run', None),
                                processing=params.get('proc', None),
                                recording=params.get('rec', None),
                                space=params.get('space', None))
        er_basename = str(er_bids_path)

        # Try to extract date from filename.
        if params['ses'] is not None:
            try:
                er_meas_date = datetime.strptime(params['ses'], '%Y%m%d')
            except (ValueError, TypeError):
                # There is a session in the filename, but it doesn't encode a
                # valid date.
                pass

        if er_meas_date is None:  # No luck so far! Check info['meas_date']
            _, ext = _parse_ext(er_fname)
            if ext == '.fif':
                extra_params = dict(allow_maxshield=True)
            else:
                extra_params = None

            er_raw = read_raw_bids(bids_basename=er_basename,
                                   bids_root=bids_root,
                                   kind=kind,
                                   extra_params=extra_params)

            er_meas_date = er_raw.info['meas_date']
            if er_meas_date is None:  # There's nothing we can do.
                failed_to_get_er_date_count += 1
                continue

        er_meas_date = er_meas_date.replace(tzinfo=ref_date.tzinfo)
        delta_t = er_meas_date - ref_date

        if abs(delta_t.total_seconds()) == min_delta_t:
            date_tie = True
        elif abs(delta_t.total_seconds()) < min_delta_t:
            min_delta_t = abs(delta_t.total_seconds())
            best_er_basename = er_basename
            date_tie = False

    if failed_to_get_er_date_count > 0:
        msg = (f'Could not retrieve the empty-room measurement date from '
               f'a total of {failed_to_get_er_date_count} recording(s).')
        warn(msg)

    if date_tie:
        msg = ('Found more than one matching empty-room measurement with the '
               'same recording date. Selecting the first match.')
        warn(msg)

    return best_er_basename
Example #8
0
def write_raw_bids(raw, bids_basename, output_path, events_data=None,
                   event_id=None, overwrite=False, verbose=True):
    """Walk over a folder of files and create BIDS compatible folder.

    .. warning:: The original files are simply copied over if the original
                 file format is BIDS-supported for that modality. Otherwise,
                 this function will convert to a BIDS-supported file format
                 while warning the user. For EEG and iEEG data, conversion will
                 be to BrainVision format, for MEG conversion will be to FIF.

    Parameters
    ----------
    raw : instance of mne.io.Raw
        The raw data. It must be an instance of mne.Raw. The data should not be
        loaded on disk, i.e., raw.preload must be False.
    bids_basename : str
        The base filename of the BIDS compatible files. Typically, this can be
        generated using make_bids_basename.
        Example: `sub-01_ses-01_task-testing_acq-01_run-01`.
        This will write the following files in the correct subfolder of the
        output_path::

            sub-01_ses-01_task-testing_acq-01_run-01_meg.fif
            sub-01_ses-01_task-testing_acq-01_run-01_meg.json
            sub-01_ses-01_task-testing_acq-01_run-01_channels.tsv
            sub-01_ses-01_task-testing_acq-01_run-01_coordsystem.json

        and the following one if events_data is not None::

            sub-01_ses-01_task-testing_acq-01_run-01_events.tsv

        and add a line to the following files::

            participants.tsv
            scans.tsv

        Note that the modality 'meg' is automatically inferred from the raw
        object and extension '.fif' is copied from raw.filenames.
    output_path : str
        The path of the root of the BIDS compatible folder. The session and
        subject specific folders will be populated automatically by parsing
        bids_basename.
    events_data : str | array | None
        The events file. If a string, a path to the events file. If an array,
        the MNE events array (shape n_events, 3). If None, events will be
        inferred from the stim channel using `mne.find_events`.
    event_id : dict | None
        The event id dict used to create a 'trial_type' column in events.tsv
    overwrite : bool
        Whether to overwrite existing files or data in files.
        Defaults to False.
        If overwrite is True, any existing files with the same BIDS parameters
        will be overwritten with the exception of the `participants.tsv` and
        `scans.tsv` files. For these files, parts of pre-existing data that
        match the current data will be replaced.
        If overwrite is False, no existing data will be overwritten or
        replaced.
    verbose : bool
        If verbose is True, this will print a snippet of the sidecar files. If
        False, no content will be printed.

    Returns
    -------
    output_path : str
        The path of the root of the BIDS compatible folder.

    Notes
    -----
    For the participants.tsv file, the raw.info['subjects_info'] should be
    updated and raw.info['meas_date'] should not be None to compute the age
    of the participant correctly.

    """
    if not check_version('mne', '0.17'):
        raise ValueError('Your version of MNE is too old. '
                         'Please update to 0.17 or newer.')

    if not isinstance(raw, BaseRaw):
        raise ValueError('raw_file must be an instance of BaseRaw, '
                         'got %s' % type(raw))

    if not hasattr(raw, 'filenames') or raw.filenames[0] is None:
        raise ValueError('raw.filenames is missing. Please set raw.filenames'
                         'as a list with the full path of original raw file.')

    if raw.preload is not False:
        raise ValueError('The data should not be preloaded.')

    raw = raw.copy()

    raw_fname = raw.filenames[0]
    if '.ds' in op.dirname(raw.filenames[0]):
        raw_fname = op.dirname(raw.filenames[0])
    # point to file containing header info for multifile systems
    raw_fname = raw_fname.replace('.eeg', '.vhdr')
    raw_fname = raw_fname.replace('.fdt', '.set')
    _, ext = _parse_ext(raw_fname, verbose=verbose)

    raw_orig = reader[ext](**raw._init_kwargs)
    assert_array_equal(raw.times, raw_orig.times,
                       "raw.times should not have changed since reading"
                       " in from the file. It may have been cropped.")

    params = _parse_bids_filename(bids_basename, verbose)
    subject_id, session_id = params['sub'], params['ses']
    acquisition, task, run = params['acq'], params['task'], params['run']
    kind = _handle_kind(raw)

    bids_fname = bids_basename + '_%s%s' % (kind, ext)

    # check whether the info provided indicates that the data is emptyroom
    # data
    emptyroom = False
    if subject_id == 'emptyroom' and task == 'noise':
        emptyroom = True
        # check the session date provided is consistent with the value in raw
        meas_date = raw.info.get('meas_date', None)
        if meas_date is not None:
            er_date = datetime.fromtimestamp(
                raw.info['meas_date'][0]).strftime('%Y%m%d')
            if er_date != session_id:
                raise ValueError("Date provided for session doesn't match "
                                 "session date.")

    data_path = make_bids_folders(subject=subject_id, session=session_id,
                                  kind=kind, output_path=output_path,
                                  overwrite=False, verbose=verbose)
    if session_id is None:
        ses_path = os.sep.join(data_path.split(os.sep)[:-1])
    else:
        ses_path = make_bids_folders(subject=subject_id, session=session_id,
                                     output_path=output_path, make_dir=False,
                                     overwrite=False, verbose=verbose)

    # create filenames
    scans_fname = make_bids_basename(
        subject=subject_id, session=session_id, suffix='scans.tsv',
        prefix=ses_path)
    participants_tsv_fname = make_bids_basename(prefix=output_path,
                                                suffix='participants.tsv')
    participants_json_fname = make_bids_basename(prefix=output_path,
                                                 suffix='participants.json')
    coordsystem_fname = make_bids_basename(
        subject=subject_id, session=session_id, acquisition=acquisition,
        suffix='coordsystem.json', prefix=data_path)
    sidecar_fname = make_bids_basename(
        subject=subject_id, session=session_id, task=task, run=run,
        acquisition=acquisition, suffix='%s.json' % kind, prefix=data_path)
    events_fname = make_bids_basename(
        subject=subject_id, session=session_id, task=task,
        acquisition=acquisition, run=run, suffix='events.tsv',
        prefix=data_path)
    channels_fname = make_bids_basename(
        subject=subject_id, session=session_id, task=task, run=run,
        acquisition=acquisition, suffix='channels.tsv', prefix=data_path)
    if ext not in ['.fif', '.ds', '.vhdr', '.edf', '.bdf', '.set', '.con',
                   '.sqd']:
        bids_raw_folder = bids_fname.split('.')[0]
        bids_fname = op.join(bids_raw_folder, bids_fname)

    # Read in Raw object and extract metadata from Raw object if needed
    orient = ORIENTATION.get(ext, 'n/a')
    unit = UNITS.get(ext, 'n/a')
    manufacturer = MANUFACTURERS.get(ext, 'n/a')

    # save all meta data
    _participants_tsv(raw, subject_id, participants_tsv_fname, overwrite,
                      verbose)
    _participants_json(participants_json_fname, True, verbose)
    _scans_tsv(raw, op.join(kind, bids_fname), scans_fname, overwrite, verbose)

    # TODO: Implement coordystem.json and electrodes.tsv for EEG and  iEEG
    if kind == 'meg' and not emptyroom:
        _coordsystem_json(raw, unit, orient, manufacturer, coordsystem_fname,
                          overwrite, verbose)

    events, event_id = _read_events(events_data, event_id, raw, ext)
    if events is not None and len(events) > 0 and not emptyroom:
        _events_tsv(events, raw, events_fname, event_id, overwrite, verbose)

    make_dataset_description(output_path, name=" ", verbose=verbose)
    _sidecar_json(raw, task, manufacturer, sidecar_fname, kind, overwrite,
                  verbose)
    _channels_tsv(raw, channels_fname, overwrite, verbose)

    # set the raw file name to now be the absolute path to ensure the files
    # are placed in the right location
    bids_fname = op.join(data_path, bids_fname)
    if os.path.exists(bids_fname) and not overwrite:
        raise FileExistsError('"%s" already exists. Please set '  # noqa: F821
                              'overwrite to True.' % bids_fname)
    _mkdir_p(os.path.dirname(bids_fname))

    if verbose:
        print('Copying data files to %s' % op.splitext(bids_fname)[0])

    convert = ext not in ALLOWED_EXTENSIONS[kind]
    # Copy the imaging data files
    if convert:
        if kind == 'meg':
            raise ValueError('Got file extension %s for MEG data, ' +
                             'expected one of %s' % ALLOWED_EXTENSIONS['meg'])
        if verbose:
            warn('Converting data files to BrainVision format')
        if not check_version('pybv', '0.2'):
            raise ImportError('pybv >=0.2.0 is required for converting ' +
                              '%s files to Brainvision format' % ext)
        from pybv import write_brainvision
        events, event_id = events_from_annotations(raw)
        write_brainvision(raw.get_data(), raw.info['sfreq'],
                          raw.ch_names,
                          op.splitext(op.basename(bids_fname))[0],
                          op.dirname(bids_fname), events[:, [0, 2]],
                          resolution=1e-6)
    elif ext == '.fif':
        n_rawfiles = len(raw.filenames)
        if n_rawfiles > 1:
            split_naming = 'bids'
            raw.save(bids_fname, split_naming=split_naming, overwrite=True)
        else:
            # This ensures that single FIF files do not have the part param
            raw.save(bids_fname, split_naming='neuromag', overwrite=True)

    # CTF data is saved and renamed in a directory
    elif ext == '.ds':
        copyfile_ctf(raw_fname, bids_fname)
    # BrainVision is multifile, copy over all of them and fix pointers
    elif ext == '.vhdr':
        copyfile_brainvision(raw_fname, bids_fname)
    # EEGLAB .set might be accompanied by a .fdt - find out and copy it too
    elif ext == '.set':
        copyfile_eeglab(raw_fname, bids_fname)
    elif ext == '.pdf':
        copyfile_bti(raw_orig, op.join(data_path, bids_raw_folder))
    else:
        sh.copyfile(raw_fname, bids_fname)
    # KIT data requires the marker file to be copied over too
    if 'mrk' in raw._init_kwargs:
        hpi = raw._init_kwargs['mrk']
        acq_map = dict()
        if isinstance(hpi, list):
            if _get_mrk_meas_date(hpi[0]) > _get_mrk_meas_date(hpi[1]):
                raise ValueError('Markers provided in incorrect order.')
            _, marker_ext = _parse_ext(hpi[0])
            acq_map = dict(zip(['pre', 'post'], hpi))
        else:
            _, marker_ext = _parse_ext(hpi)
            acq_map[None] = hpi
        for key, value in acq_map.items():
            marker_fname = make_bids_basename(
                subject=subject_id, session=session_id, task=task, run=run,
                acquisition=key, suffix='markers%s' % marker_ext,
                prefix=data_path)
            sh.copyfile(value, marker_fname)

    return output_path
Example #9
0
def _channels_tsv(raw, fname, overwrite=False, verbose=True):
    """Create a channels.tsv file and save it.

    Parameters
    ----------
    raw : instance of Raw
        The data as MNE-Python Raw object.
    fname : str
        Filename to save the channels.tsv to.
    overwrite : bool
        Whether to overwrite the existing file.
        Defaults to False.
    verbose : bool
        Set verbose output to true or false.

    """
    # Get channel type mappings between BIDS and MNE nomenclatures
    map_chs = _get_ch_type_mapping(fro='mne', to='bids')

    # Prepare the descriptions for each channel type
    map_desc = defaultdict(lambda: 'Other type of channel')
    map_desc.update(meggradaxial='Axial Gradiometer',
                    megrefgradaxial='Axial Gradiometer Reference',
                    meggradplanar='Planar Gradiometer',
                    megmag='Magnetometer',
                    megrefmag='Magnetometer Reference',
                    stim='Trigger',
                    eeg='ElectroEncephaloGram',
                    ecog='Electrocorticography',
                    seeg='StereoEEG',
                    ecg='ElectroCardioGram',
                    eog='ElectroOculoGram',
                    emg='ElectroMyoGram',
                    misc='Miscellaneous')
    get_specific = ('mag', 'ref_meg', 'grad')

    # get the manufacturer from the file in the Raw object
    manufacturer = None

    _, ext = _parse_ext(raw.filenames[0], verbose=verbose)
    manufacturer = MANUFACTURERS[ext]

    ignored_channels = IGNORED_CHANNELS.get(manufacturer, list())

    status, ch_type, description = list(), list(), list()
    for idx, ch in enumerate(raw.info['ch_names']):
        status.append('bad' if ch in raw.info['bads'] else 'good')
        _channel_type = channel_type(raw.info, idx)
        if _channel_type in get_specific:
            _channel_type = coil_type(raw.info, idx, _channel_type)
        ch_type.append(map_chs[_channel_type])
        description.append(map_desc[_channel_type])
    low_cutoff, high_cutoff = (raw.info['highpass'], raw.info['lowpass'])
    if raw._orig_units:
        units = [raw._orig_units.get(ch, 'n/a') for ch in raw.ch_names]
    else:
        units = [_unit2human.get(ch_i['unit'], 'n/a')
                 for ch_i in raw.info['chs']]
        units = [u if u not in ['NA'] else 'n/a' for u in units]
    n_channels = raw.info['nchan']
    sfreq = raw.info['sfreq']

    ch_data = OrderedDict([
        ('name', raw.info['ch_names']),
        ('type', ch_type),
        ('units', units),
        ('low_cutoff', np.full((n_channels), low_cutoff)),
        ('high_cutoff', np.full((n_channels), high_cutoff)),
        ('description', description),
        ('sampling_frequency', np.full((n_channels), sfreq)),
        ('status', status)])
    ch_data = _drop(ch_data, ignored_channels, 'name')

    _write_tsv(fname, ch_data, overwrite, verbose)

    return fname
Example #10
0
def read_raw_bids(bids_fname, bids_root, extra_params=None,
                  verbose=True):
    """Read BIDS compatible data.

    Will attempt to read associated events.tsv and channels.tsv files to
    populate the returned raw object with raw.annotations and raw.info['bads'].

    Parameters
    ----------
    bids_fname : str
        Full name of the data file
    bids_root : str
        Path to root of the BIDS folder
    extra_params : None | dict
        Extra parameters to be passed to MNE read_raw_* functions.
        If a dict, for example: ``extra_params=dict(allow_maxshield=True)``.
    verbose : bool
        The verbosity level

    Returns
    -------
    raw : instance of Raw
        The data as MNE-Python Raw object.

    """
    # Full path to data file is needed so that mne-bids knows
    # what is the modality -- meg, eeg, ieeg to read
    bids_fname = op.basename(bids_fname)
    bids_basename = '_'.join(bids_fname.split('_')[:-1])
    kind = bids_fname.split('_')[-1].split('.')[0]
    _, ext = _parse_ext(bids_fname)

    # Get the BIDS parameters (=entities)
    params = _parse_bids_filename(bids_basename, verbose)

    # Construct the path to the "kind" where the data is stored
    # Subject is mandatory ...
    kind_dir = op.join(bids_root, 'sub-{}'.format(params['sub']))
    # Session is optional ...
    if params['ses'] is not None:
        kind_dir = op.join(kind_dir, 'ses-{}'.format(params['ses']))
    # Kind is mandatory
    kind_dir = op.join(kind_dir, kind)

    config = None
    if ext in ('.fif', '.ds', '.vhdr', '.edf', '.bdf', '.set', '.sqd', '.con'):
        bids_fpath = op.join(kind_dir,
                             bids_basename + '_{}{}'.format(kind, ext))

    elif ext == '.pdf':
        bids_raw_folder = op.join(kind_dir, bids_basename + '_{}'.format(kind))
        bids_fpath = glob.glob(op.join(bids_raw_folder, 'c,rf*'))[0]
        config = op.join(bids_raw_folder, 'config')

    if extra_params is None:
        extra_params = dict()
    raw = _read_raw(bids_fpath, electrode=None, hsp=None, hpi=None,
                    config=config, verbose=None, **extra_params)

    # Try to find an associated events.tsv to get information about the
    # events in the recorded data
    events_fname = _find_matching_sidecar(bids_fname, bids_root, 'events.tsv',
                                          allow_fail=True)
    if events_fname is not None:
        raw = _handle_events_reading(events_fname, raw)

    # Try to find an associated channels.tsv to get information about the
    # status and type of present channels
    channels_fname = _find_matching_sidecar(bids_fname, bids_root,
                                            'channels.tsv', allow_fail=True)
    if channels_fname is not None:
        raw = _handle_channels_reading(channels_fname, bids_fname, raw)

    # Try to find an associated electrodes.tsv and coordsystem.json
    # to get information about the status and type of present channels
    electrodes_fname = _find_matching_sidecar(bids_fname, bids_root,
                                              'electrodes.tsv',
                                              allow_fail=True)
    coordsystem_fname = _find_matching_sidecar(bids_fname, bids_root,
                                               'coordsystem.json',
                                               allow_fail=True)
    if electrodes_fname is not None:
        if coordsystem_fname is None:
            raise RuntimeError("BIDS mandates that the coordsystem.json "
                               "should exist if electrodes.tsv does. "
                               "Please create coordsystem.json for"
                               "{}".format(bids_basename))
        # Get MRI landmarks from the JSON sidecar
        with open(coordsystem_fname, 'r') as fin:
            coordsystem_json = json.load(fin)

        # Get coordinate frames that electrode coordinates are in
        if kind == "meg":
            coord_frame = coordsystem_json['MEGCoordinateSystem']
        elif kind == "ieeg":
            coord_frame = coordsystem_json['iEEGCoordinateSystem']
        else:  # noqa
            raise RuntimeError("Kind {} not supported yet for "
                               "coordsystem.json and "
                               "electrodes.tsv.".format(kind))
        # read in electrode coordinates and attach to raw
        raw = _handle_electrodes_reading(electrodes_fname, coord_frame, raw,
                                         verbose)

    # Try to find an associated sidecar.json to get information about the
    # recording snapshot
    sidecar_fname = _find_matching_sidecar(bids_fname, bids_root,
                                           '{}.json'.format(kind),
                                           allow_fail=True)
    if sidecar_fname is not None:
        raw = _handle_info_reading(sidecar_fname, raw, verbose=verbose)

    return raw
Example #11
0
def read_raw_bids(bids_fname, bids_root, extra_params=None,
                  verbose=True):
    """Read BIDS compatible data.

    Will attempt to read associated events.tsv and channels.tsv files to
    populate the returned raw object with raw.annotations and raw.info['bads'].

    Parameters
    ----------
    bids_fname : str
        Full name of the data file
    bids_root : str
        Path to root of the BIDS folder
    extra_params : None | dict
        Extra parameters to be passed to MNE read_raw_* functions.
        If a dict, for example: ``extra_params=dict(allow_maxshield=True)``.
    verbose : bool
        The verbosity level

    Returns
    -------
    raw : instance of Raw
        The data as MNE-Python Raw object.

    """
    # Full path to data file is needed so that mne-bids knows
    # what is the modality -- meg, eeg, ieeg to read
    bids_fname = op.basename(bids_fname)
    bids_basename = '_'.join(bids_fname.split('_')[:-1])
    kind = bids_fname.split('_')[-1].split('.')[0]
    _, ext = _parse_ext(bids_fname)

    # Get the BIDS parameters (=entities)
    params = _parse_bids_filename(bids_basename, verbose)

    # Construct the path to the "kind" where the data is stored
    # Subject is mandatory ...
    kind_dir = op.join(bids_root, 'sub-{}'.format(params['sub']))
    # Session is optional ...
    if params['ses'] is not None:
        kind_dir = op.join(kind_dir, 'ses-{}'.format(params['ses']))
    # Kind is mandatory
    kind_dir = op.join(kind_dir, kind)

    config = None
    if ext in ('.fif', '.ds', '.vhdr', '.edf', '.bdf', '.set', '.sqd', '.con'):
        bids_fpath = op.join(kind_dir,
                             bids_basename + '_{}{}'.format(kind, ext))

    elif ext == '.pdf':
        bids_raw_folder = op.join(kind_dir, bids_basename + '_{}'.format(kind))
        bids_fpath = glob.glob(op.join(bids_raw_folder, 'c,rf*'))[0]
        config = op.join(bids_raw_folder, 'config')

    if extra_params is None:
        extra_params = dict()
    raw = _read_raw(bids_fpath, electrode=None, hsp=None, hpi=None,
                    config=config, verbose=None, **extra_params)

    # Try to find an associated events.tsv to get information about the
    # events in the recorded data
    events_fname = _find_matching_sidecar(bids_fname, bids_root, 'events.tsv',
                                          allow_fail=True)
    if events_fname is not None:
        raw = _handle_events_reading(events_fname, raw)

    # Try to find an associated channels.tsv to get information about the
    # status and type of present channels
    channels_fname = _find_matching_sidecar(bids_fname, bids_root,
                                            'channels.tsv', allow_fail=True)
    if channels_fname is not None:
        raw = _handle_channels_reading(channels_fname, bids_fname, raw)

    return raw
def read_raw_bids(bids_fname, bids_root, allow_maxshield=False,
                  verbose=True):
    """Read BIDS compatible data.
    Will attempt to read associated events.tsv and channels.tsv files to
    populate the returned raw object with raw.annotations and raw.info['bads'].
    Parameters
    ----------
    bids_fname : str
        Full name of the data file
    bids_root : str
        Path to root of the BIDS folder
    allow_maxshield : bool | str (default False)
        If True, allow loading of data that has been recorded with internal
        active compensation (MaxShield). Data recorded with MaxShield should
        generally not be loaded directly, but should first be processed
        using SSS/tSSS to remove the compensation signals that may also affect
        brain activity. Can also be “yes” to load without eliciting a warning.
    verbose : bool
        The verbosity level
    Returns
    -------
    raw : instance of Raw
        The data as MNE-Python Raw object.
    """
    # Full path to data file is needed so that mne-bids knows
    # what is the modality -- meg, eeg, ieeg to read
    bids_fname = op.basename(bids_fname)
    bids_basename = '_'.join(bids_fname.split('_')[:-1])
    kind = bids_fname.split('_')[-1].split('.')[0]
    _, ext = _parse_ext(bids_fname)

    # Get the BIDS parameters (=entities)
    params = _parse_bids_filename(bids_basename, verbose)

    # Construct the path to the "kind" where the data is stored
    # Subject is mandatory ...
    kind_dir = op.join(bids_root, 'sub-{}'.format(params['sub']))
    # Session is optional ...
    if params['ses'] is not None:
        kind_dir = op.join(kind_dir, 'ses-{}'.format(params['ses']))
    # Kind is mandatory
    kind_dir = op.join(kind_dir, kind)

    config = None
    if ext in ('.fif', '.ds', '.vhdr', '.edf', '.bdf', '.set', '.sqd', '.con'):
        bids_fpath = op.join(kind_dir,
                             bids_basename + '_{}{}'.format(kind, ext))

    elif ext == '.pdf':
        bids_raw_folder = op.join(kind_dir, bids_basename + '_{}'.format(kind))
        bids_fpath = glob.glob(op.join(bids_raw_folder, 'c,rf*'))[0]
        config = op.join(bids_raw_folder, 'config')

    raw = _read_raw(bids_fpath, electrode=None, hsp=None, hpi=None,
                    config=config, allow_maxshield=allow_maxshield,
                    verbose=None)

    # Try to find an associated events.tsv to get information about the
    # events in the recorded data
    events_fname = _find_matching_sidecar(bids_fname, bids_root, 'events.tsv',
                                          allow_fail=True)
    if events_fname is not None:
        raw = _handle_events_reading(events_fname, raw)

    # Try to find an associated channels.tsv to get information about the
    # status and type of present channels
    channels_fname = _find_matching_sidecar(bids_fname, bids_root,
                                            'channels.tsv', allow_fail=True)
    if channels_fname is not None:
        raw = _handle_channels_reading(channels_fname, bids_fname, raw)

    return raw
Example #13
0
    def convert_to_bids(
        edf_fpath,
        bids_root,
        bids_basename,
        coords_fpath=None,
        excluded_contacts=None,
        eog_contacts=None,
        misc_contacts=None,
        overwrite=False,
        line_freq=60.0,
    ):
        """
        Convert the passed edf file into the Bids format.

        # TODO:
        - Clean up how write_raw_bids is called
        - eliminate redundant writing/reading using temporaryDirectory
        
        Parameters
        ----------
        edf_fpath : Union[str, os.PathLike]
            The location the edf file.
        bids_root : Union[str, os.PathLike]
            The base directory for newly created bids files.
        bids_basename : str
            The base name of the new data files
        excluded_contacts : list
            Contacts to be excluded from conversion
        eog_contacts : list
            Contacts to be annotated as EOG.
        misc_contacts : list
            Contacts to be annotated  as Misc.
        overwrite : bool
            Whether to overwrite an existing converted file.

        Returns
        -------
        The path to the new data file.

        """
        if excluded_contacts is None:
            excluded_contacts = ["-", ""]
        raw = mne.io.read_raw_edf(
            edf_fpath,
            preload=False,
            verbose="ERROR",
            exclude=excluded_contacts,
            eog=eog_contacts,
            misc=misc_contacts,
        )
        if line_freq is not None:
            raw.info["line_freq"] = line_freq

        annonymize_dict = None
        # {
        #         "daysback": 10000,
        #         "keep_his": True,
        #     }

        # extract parameters from bids_basenmae
        params = _parse_bids_filename(bids_basename, True)
        subject, session = params["sub"], params["ses"]
        acquisition, kind = params["acq"], params["kind"]
        task = params["task"]

        # read in the events from the EDF file
        events_data, events_id = mne.events_from_annotations(raw)
        print(events_data, events_id)
        channel_scrub = ChannelScrub

        # convert the channel types based on acquisition if necessary
        if acquisition is not None:
            ch_modality_map = {ch: acquisition for ch in raw.ch_names}
            raw.set_channel_types(ch_modality_map)
            ch_type_mapping = channel_scrub.label_channel_types(raw.ch_names)
            raw.set_channel_types((ch_type_mapping))

        # reformat channel text if necessary
        channel_scrub.channel_text_scrub(raw)

        # look for bad channels that are obvious
        channel_names = raw.ch_names
        bad_channels = channel_scrub.look_for_bad_channels(channel_names)
        bad_channels_dict = {}
        for bad in bad_channels:
            bad_channels_dict[
                bad] = f"Scrubbed channels containing markers {', '.join(BAD_MARKERS)}"
        raw.info["bads"] = bad_channels

        if coords_fpath:
            ch_pos = dict()
            with open(coords_fpath, "r") as fp:
                # strip of newline character
                lines = [line.rstrip("\n") for line in fp]

                for line in lines:
                    ch_name = line.split(" ")[0]
                    coord = line.split(" ")[1:]
                    ch_pos[ch_name] = [float(x) for x in coord]
            unit = "mm"
            if unit != "m":
                ch_pos = {
                    ch_name: np.divide(coord, 1000)
                    for ch_name, coord in ch_pos.items()
                }
            montage = mne.channels.make_dig_montage(ch_pos=ch_pos,
                                                    coord_frame="head")

            # TODO: remove. purely for testing scenario
            # ch_names = raw.ch_names
            # elec = np.random.random_sample((len(ch_names), 3))  # assume in mm
            # elec = elec / 1000  # convert to meters
            # montage = mne.channels.make_dig_montage(ch_pos=dict(zip(ch_names, elec)),
            #                                         coord_frame='head')
        else:
            montage = None

        if montage is not None:
            if not isinstance(montage, mne.channels.DigMontage):
                raise TypeError("Montage passed in should be of type: "
                                "`mne.channels.DigMontage`.")
            raw.set_montage(montage)
            print("Set montage: ")
            print(len(raw.info["ch_names"]))
            print(raw.info["dig"])
            print(raw)

        # actually perform write_raw bids
        bids_root = write_raw_bids(
            raw,
            bids_basename,
            bids_root,
            events_data=events_data,
            event_id=events_id,
            overwrite=overwrite,
            # anonymize=annonymize_dict,
            verbose=False,
        )

        # save a fif copy and reload it
        kind = _handle_kind(raw)
        fif_data_path = make_bids_folders(
            subject=subject,
            session=session,
            kind=kind,
            output_path=bids_root,
            overwrite=False,
            verbose=True,
        )

        bids_fname = bids_basename + f"_{kind}.fif"
        deriv_bids_root = os.path.join(bids_root, "derivatives")

        print("Should be saving for: ", bids_fname)
        with tempfile.TemporaryDirectory() as tmp_bids_root:
            raw.save(os.path.join(tmp_bids_root, bids_fname),
                     overwrite=overwrite)
            raw = mne.io.read_raw_fif(os.path.join(tmp_bids_root, bids_fname))

            print(raw, bids_basename)
            print(raw.filenames)
            _, ext = _parse_ext(raw.filenames[0])
            print(ext)
            # actually perform write_raw bids
            bids_root = write_raw_bids(
                raw,
                bids_basename,
                deriv_bids_root,
                events_data=events_data,
                event_id=events_id,
                overwrite=overwrite,
                # anonymize=annonymize_dict,
                verbose=False,
            )
        return bids_root