예제 #1
0
def test_edf(_bids_validate):
    """Test write_raw_bids conversion for European Data Format data."""
    output_path = _TempDir()
    data_path = op.join(testing.data_path(), 'EDF')
    raw_fname = op.join(data_path, 'test_reduced.edf')

    raw = mne.io.read_raw_edf(raw_fname, preload=True)
    # XXX: hack that should be fixed later. Annotation reading is
    # broken for this file with preload=False and read_annotations_edf
    raw.preload = False

    raw.rename_channels({raw.info['ch_names'][0]: 'EOG'})
    raw.info['chs'][0]['coil_type'] = FIFF.FIFFV_COIL_EEG_BIPOLAR
    raw.rename_channels({raw.info['ch_names'][1]: 'EMG'})
    raw.set_channel_types({'EMG': 'emg'})

    write_raw_bids(raw, bids_basename, output_path)

    # Reading the file back should raise an error, because we renamed channels
    # in `raw` and used that information to write a channels.tsv. Yet, we
    # saved the unchanged `raw` in the BIDS folder, so channels in the TSV and
    # in raw clash
    with pytest.raises(RuntimeError, match='Channels do not correspond'):
        read_raw_bids(bids_basename + '_eeg.edf', output_path)

    bids_fname = bids_basename.replace('run-01', 'run-%s' % run2)
    write_raw_bids(raw, bids_fname, output_path, overwrite=True)
    _bids_validate(output_path)

    # ensure there is an EMG channel in the channels.tsv:
    channels_tsv = make_bids_basename(subject=subject_id,
                                      session=session_id,
                                      task=task,
                                      run=run,
                                      suffix='channels.tsv',
                                      acquisition=acq,
                                      prefix=op.join(output_path, 'sub-01',
                                                     'ses-01', 'eeg'))
    data = _from_tsv(channels_tsv)
    assert 'ElectroMyoGram' in data['description']

    # check that the scans list contains two scans
    scans_tsv = make_bids_basename(subject=subject_id,
                                   session=session_id,
                                   suffix='scans.tsv',
                                   prefix=op.join(output_path, 'sub-01',
                                                  'ses-01'))
    data = _from_tsv(scans_tsv)
    assert len(list(data.values())[0]) == 2

    # Also cover iEEG
    # We use the same data and pretend that eeg channels are ecog
    raw.set_channel_types(
        {raw.ch_names[i]: 'ecog'
         for i in mne.pick_types(raw.info, eeg=True)})
    output_path = _TempDir()
    write_raw_bids(raw, bids_basename, output_path)
    _bids_validate(output_path)
예제 #2
0
def test_tsv_handler(tmp_path):
    """Test the TSV handling."""
    # create some dummy data
    d = odict(a=[1, 2, 3, 4], b=['five', 'six', 'seven', 'eight'])
    assert _contains_row(d, {'a': 1, 'b': 'five'})
    d2 = odict(a=[5], b=['nine'])
    d = _combine_rows(d, d2)
    assert 5 in d['a']
    d2 = odict(a=[5])
    d = _combine_rows(d, d2)
    assert 'n/a' in d['b']
    d2 = odict(a=[5], b=['ten'])
    d = _combine_rows(d, d2, drop_column='a')
    # make sure that the repeated data was dropped
    assert 'nine' not in d['b']
    print(_tsv_to_str(d))

    d_path = tmp_path / 'output.tsv'

    # write the data to an output tsv file
    _to_tsv(d, d_path)
    # now read it back
    d = _from_tsv(d_path)
    # test reading the file in with the incorrect number of datatypes raises
    # an Error
    with pytest.raises(ValueError):
        d = _from_tsv(d_path, dtypes=[str])
    # we can also pass just a single data type and it will be applied to all
    # columns
    d = _from_tsv(d_path, str)

    # remove any rows with 2 or 5 in them
    d = _drop(d, [2, 5], 'a')
    assert 2 not in d['a']

    # test combining data with differing numbers of columns
    d = odict(a=[1, 2], b=['three', 'four'])
    d2 = odict(a=[4], b=['five'], c=[3.1415])
    # raise error if a new column is tried to be added
    with pytest.raises(KeyError):
        d = _combine_rows(d, d2)
    d2 = odict(a=[5])
    d = _combine_rows(d, d2)
    assert d['b'] == ['three', 'four', 'n/a']
    assert _contains_row(d, {'a': 5})

    # test reading a single column
    _to_tsv(odict(a=[1, 2, 3, 4]), d_path)
    d = _from_tsv(d_path)
    assert d['a'] == ['1', '2', '3', '4']
예제 #3
0
def test_read_participants_data(tmpdir):
    """Test reading information from a BIDS sidecar.json file."""
    bids_path = _bids_path.copy().update(root=tmpdir, datatype='meg')
    raw = _read_raw_fif(raw_fname, verbose=False)

    # if subject info was set, we don't roundtrip birthday
    # due to possible anonymization in mne-bids
    subject_info = {
        'hand': 1,
        'sex': 2,
    }
    raw.info['subject_info'] = subject_info
    write_raw_bids(raw, bids_path, overwrite=True, verbose=False)
    raw = read_raw_bids(bids_path=bids_path)
    print(raw.info['subject_info'])
    assert raw.info['subject_info']['hand'] == 1
    assert raw.info['subject_info']['sex'] == 2
    assert raw.info['subject_info'].get('birthday', None) is None
    assert raw.info['subject_info']['his_id'] == f'sub-{bids_path.subject}'
    assert 'participant_id' not in raw.info['subject_info']

    # if modifying participants tsv, then read_raw_bids reflects that
    participants_tsv_fpath = tmpdir / 'participants.tsv'
    participants_tsv = _from_tsv(participants_tsv_fpath)
    participants_tsv['hand'][0] = 'n/a'
    _to_tsv(participants_tsv, participants_tsv_fpath)
    raw = read_raw_bids(bids_path=bids_path)
    assert raw.info['subject_info']['hand'] == 0
    assert raw.info['subject_info']['sex'] == 2
    assert raw.info['subject_info'].get('birthday', None) is None

    # make sure things are read even if the entries don't make sense
    participants_tsv = _from_tsv(participants_tsv_fpath)
    participants_tsv['hand'][0] = 'righty'
    participants_tsv['sex'][0] = 'malesy'
    _to_tsv(participants_tsv, participants_tsv_fpath)
    with pytest.warns(RuntimeWarning, match='Unable to map'):
        raw = read_raw_bids(bids_path=bids_path)
        assert raw.info['subject_info']['hand'] is None
        assert raw.info['subject_info']['sex'] is None

    # make sure to read in if no participants file
    raw = _read_raw_fif(raw_fname, verbose=False)
    write_raw_bids(raw, bids_path, overwrite=True, verbose=False)
    os.remove(participants_tsv_fpath)
    with pytest.warns(RuntimeWarning, match='Participants file not found'):
        raw = read_raw_bids(bids_path=bids_path)
        assert raw.info['subject_info'] is None
예제 #4
0
def _handle_electrodes_reading(electrodes_fname, coord_frame, coord_unit):
    """Read associated electrodes.tsv and populate raw.

    Handle xyz coordinates and coordinate frame of each channel.
    """
    logger.info('Reading electrode '
                'coords from {}.'.format(electrodes_fname))
    electrodes_dict = _from_tsv(electrodes_fname)
    ch_names_tsv = electrodes_dict['name']

    def _float_or_nan(val):
        if val == "n/a":
            return np.nan
        else:
            return float(val)

    # convert coordinates to float and create list of tuples
    electrodes_dict['x'] = [_float_or_nan(x) for x in electrodes_dict['x']]
    electrodes_dict['y'] = [_float_or_nan(x) for x in electrodes_dict['y']]
    electrodes_dict['z'] = [_float_or_nan(x) for x in electrodes_dict['z']]
    ch_names_raw = [
        x for i, x in enumerate(ch_names_tsv)
        if electrodes_dict['x'][i] != "n/a"
    ]
    ch_locs = np.c_[electrodes_dict['x'], electrodes_dict['y'],
                    electrodes_dict['z']]

    # convert coordinates to meters
    ch_locs = _scale_coord_to_meters(ch_locs, coord_unit)

    # create mne.DigMontage
    ch_pos = dict(zip(ch_names_raw, ch_locs))
    montage = mne.channels.make_dig_montage(ch_pos=ch_pos,
                                            coord_frame=coord_frame)
    return montage
예제 #5
0
def test_bdf(_bids_validate):
    """Test write_raw_bids conversion for Biosemi data."""
    output_path = _TempDir()
    data_path = op.join(base_path, 'edf', 'tests', 'data')
    raw_fname = op.join(data_path, 'test.bdf')

    raw = mne.io.read_raw_bdf(raw_fname)
    with pytest.warns(UserWarning, match='No line frequency found'):
        write_raw_bids(raw, bids_basename, output_path, overwrite=False)
    _bids_validate(output_path)

    # Test also the reading of channel types from channels.tsv
    # the first channel in the raw data is not MISC right now
    test_ch_idx = 0
    assert coil_type(raw.info, test_ch_idx) != 'misc'

    # we will change the channel type to MISC and overwrite the channels file
    bids_fname = bids_basename + '_eeg.bdf'
    channels_fname = _find_matching_sidecar(bids_fname, output_path,
                                            'channels.tsv')
    channels_dict = _from_tsv(channels_fname)
    channels_dict['type'][test_ch_idx] = 'MISC'
    _to_tsv(channels_dict, channels_fname)

    # Now read the raw data back from BIDS, with the tampered TSV, to show
    # that the channels.tsv truly influences how read_raw_bids sets ch_types
    # in the raw data object
    raw = read_raw_bids(bids_fname, output_path)
    assert coil_type(raw.info, test_ch_idx) == 'misc'

    # Test cropped assertion error
    raw = mne.io.read_raw_bdf(raw_fname)
    raw.crop(0, raw.times[-2])
    with pytest.raises(AssertionError, match='cropped'):
        write_raw_bids(raw, bids_basename, output_path)
예제 #6
0
파일: read.py 프로젝트: eort/mne-bids
def _handle_participants_reading(participants_fname,
                                 raw,
                                 subject,
                                 verbose=None):
    participants_tsv = _from_tsv(participants_fname)
    subjects = participants_tsv['participant_id']
    row_ind = subjects.index(subject)

    # set data from participants tsv into subject_info
    for infokey, infovalue in participants_tsv.items():
        if infokey == 'sex' or infokey == 'hand':
            value = _map_options(what=infokey,
                                 key=infovalue[row_ind],
                                 fro='bids',
                                 to='mne')
            # We don't know how to translate to MNE, so skip.
            if value is None:
                if infokey == 'sex':
                    info_str = 'subject sex'
                else:
                    info_str = 'subject handedness'
                warn(f'Unable to map `{infokey}` value to MNE. '
                     f'Not setting {info_str}.')
        else:
            value = infovalue[row_ind]
        # add data into raw.Info
        if raw.info['subject_info'] is None:
            raw.info['subject_info'] = dict()
        raw.info['subject_info'][infokey] = value

    return raw
예제 #7
0
def test_vhdr(_bids_validate):
    """Test write_raw_bids conversion for BrainVision data."""
    output_path = _TempDir()
    data_path = op.join(base_path, 'brainvision', 'tests', 'data')
    raw_fname = op.join(data_path, 'test.vhdr')

    raw = mne.io.read_raw_brainvision(raw_fname)

    # inject a bad channel
    assert not raw.info['bads']
    injected_bad = ['FP1']
    raw.info['bads'] = injected_bad

    # write with injected bad channels
    write_raw_bids(raw, bids_basename_minimal, output_path, overwrite=False)
    _bids_validate(output_path)

    # read and also get the bad channels
    raw = read_raw_bids(bids_basename_minimal + '_eeg.vhdr', output_path)

    with pytest.raises(TypeError, match="unexpected keyword argument 'foo'"):
        read_raw_bids(bids_basename_minimal + '_eeg.vhdr', output_path,
                      extra_params=dict(foo='bar'))

    # Check that injected bad channel shows up in raw after reading
    np.testing.assert_array_equal(np.asarray(raw.info['bads']),
                                  np.asarray(injected_bad))

    # Test that correct channel units are written ... and that bad channel
    # is in channels.tsv
    channels_tsv_name = op.join(output_path, 'sub-{}'.format(subject_id),
                                'eeg', bids_basename_minimal + '_channels.tsv')
    data = _from_tsv(channels_tsv_name)
    assert data['units'][data['name'].index('FP1')] == 'µV'
    assert data['units'][data['name'].index('CP5')] == 'n/a'
    assert data['status'][data['name'].index(injected_bad[0])] == 'bad'

    # check events.tsv is written
    events_tsv_fname = channels_tsv_name.replace('channels', 'events')
    assert op.exists(events_tsv_fname)

    # create another bids folder with the overwrite command and check
    # no files are in the folder
    data_path = make_bids_folders(subject=subject_id, kind='eeg',
                                  output_path=output_path, overwrite=True)
    assert len([f for f in os.listdir(data_path) if op.isfile(f)]) == 0

    # test anonymize and convert
    raw = mne.io.read_raw_brainvision(raw_fname)
    _test_anonymize(raw, bids_basename)

    # Also cover iEEG
    # We use the same data and pretend that eeg channels are ecog
    raw = mne.io.read_raw_brainvision(raw_fname)
    raw.set_channel_types({raw.ch_names[i]: 'ecog'
                           for i in mne.pick_types(raw.info, eeg=True)})
    output_path = _TempDir()
    write_raw_bids(raw, bids_basename, output_path, overwrite=False)
    _bids_validate(output_path)
예제 #8
0
def _handle_electrodes_reading(electrodes_fname, coord_frame, raw, verbose):
    """Read associated electrodes.tsv and populate raw.

    Handle xyz coordinates and coordinate frame of each channel.
    Assumes units of coordinates are in 'm'.
    """
    logger.info('Reading electrode '
                'coords from {}.'.format(electrodes_fname))
    electrodes_dict = _from_tsv(electrodes_fname)
    # First, make sure that ordering of names in channels.tsv matches the
    # ordering of names in the raw data. The "name" column is mandatory in BIDS
    ch_names_raw = list(raw.ch_names)
    ch_names_tsv = electrodes_dict['name']

    if ch_names_raw != ch_names_tsv:
        msg = ('Channels do not correspond between raw data and the '
               'channels.tsv file. For MNE-BIDS, the channel names in the '
               'tsv MUST be equal and in the same order as the channels in '
               'the raw data.\n\n'
               '{} channels in tsv file: "{}"\n\n --> {}\n\n'
               '{} channels in raw file: "{}"\n\n --> {}\n\n'
               .format(len(ch_names_tsv), electrodes_fname, ch_names_tsv,
                       len(ch_names_raw), raw.filenames, ch_names_raw)
               )

        # XXX: this could be due to MNE inserting a 'STI 014' channel as the
        # last channel: In that case, we can work. --> Can be removed soon,
        # because MNE will stop the synthesis of stim channels in the near
        # future
        if not (ch_names_raw[-1] == 'STI 014' and
                ch_names_raw[:-1] == ch_names_tsv):
            raise RuntimeError(msg)

    if verbose:
        print("The read in electrodes file is: \n", electrodes_dict)

    # convert coordinates to float and create list of tuples
    ch_names_raw = [x for i, x in enumerate(ch_names_raw)
                    if electrodes_dict['x'][i] != "n/a"]
    electrodes_dict['x'] = [float(x) for x in electrodes_dict['x']
                            if x != "n/a"]
    electrodes_dict['y'] = [float(x) for x in electrodes_dict['y']
                            if x != "n/a"]
    electrodes_dict['z'] = [float(x) for x in electrodes_dict['z']
                            if x != "n/a"]

    ch_locs = list(zip(electrodes_dict['x'],
                       electrodes_dict['y'],
                       electrodes_dict['z']))
    ch_pos = dict(zip(ch_names_raw, ch_locs))

    # create mne.DigMontage
    montage = mne.channels.make_dig_montage(ch_pos=ch_pos,
                                            coord_frame=coord_frame)
    raw.set_montage(montage)

    return raw
예제 #9
0
def test_write_read_fif_split_file(tmpdir):
    """Test split files are read correctly."""
    # load raw test file, extend it to be larger than 2gb, and save it
    bids_root = tmpdir.mkdir('bids')
    tmp_dir = tmpdir.mkdir('tmp')
    bids_path = _bids_path.copy().update(root=bids_root, datatype='meg')
    raw = _read_raw_fif(raw_fname, verbose=False)
    bids_path.update(acquisition=None)
    write_raw_bids(raw, bids_path, verbose=False)
    bids_path.update(acquisition='01')
    n_channels = len(raw.ch_names)
    n_times = int(2.2e9 / (n_channels * 4))  # enough to produce a split
    data = np.empty((n_channels, n_times), dtype=np.float32)
    raw = mne.io.RawArray(data, raw.info)
    big_fif_fname = pathlib.Path(tmp_dir) / 'test_raw.fif'
    raw.save(big_fif_fname)
    raw = _read_raw_fif(big_fif_fname, verbose=False)
    write_raw_bids(raw, bids_path, verbose=False)

    # test whether split raw files were read correctly
    raw1 = read_raw_bids(bids_path=bids_path)
    assert 'split-01' in str(bids_path.fpath)
    bids_path.update(split='01')
    raw2 = read_raw_bids(bids_path=bids_path)
    bids_path.update(split='02')
    raw3 = read_raw_bids(bids_path=bids_path)
    assert len(raw) == len(raw1)
    assert len(raw) == len(raw2)
    assert len(raw) > len(raw3)

    # check that split files both appear in scans.tsv
    scans_tsv = BIDSPath(subject=subject_id,
                         session=session_id,
                         suffix='scans',
                         extension='.tsv',
                         root=bids_root)
    scan_data = _from_tsv(scans_tsv)
    scan_fnames = scan_data['filename']
    scan_acqtime = scan_data['acq_time']

    assert len(scan_fnames) == 3
    assert 'split-01' in scan_fnames[0] and 'split-02' in scan_fnames[1]
    # check that the acq_times in scans.tsv are the same
    assert scan_acqtime[0] == scan_acqtime[1]
    # check the recordings are in the correct order
    assert raw2.first_time < raw3.first_time

    # check whether non-matching acq_times are caught
    scan_data['acq_time'][0] = scan_acqtime[0].split('.')[0]
    _to_tsv(scan_data, scans_tsv)
    with pytest.raises(ValueError,
                       match='Split files must have the same acq_time.'):
        read_raw_bids(bids_path)

    # reset scans.tsv file for downstream tests
    scan_data['acq_time'][0] = scan_data['acq_time'][1]
    _to_tsv(scan_data, scans_tsv)
예제 #10
0
def _update_electrodes_tsv(electrodes_tsv_fpath, elec_labels_anat, atlas_depth):
    electrodes_tsv = _from_tsv(electrodes_tsv_fpath)

    if atlas_depth not in electrodes_tsv.keys():
        electrodes_tsv[atlas_depth] = ["n/a"] * len(elec_labels_anat)
    for i in range(len(elec_labels_anat)):
        ch_name = electrodes_tsv["name"][i]
        print(ch_name, elec_labels_anat[i])
        electrodes_tsv[atlas_depth][i] = elec_labels_anat[i]
    _to_tsv(electrodes_tsv, electrodes_tsv_fpath)

    return electrodes_tsv
예제 #11
0
파일: read.py 프로젝트: eort/mne-bids
def _handle_channels_reading(channels_fname, raw):
    """Read associated channels.tsv and populate raw.

    Updates status (bad) and types of channels.
    """
    logger.info('Reading channel info from {}.'.format(channels_fname))
    channels_dict = _from_tsv(channels_fname)
    ch_names_tsv = channels_dict['name']

    # Now we can do some work.
    # The "type" column is mandatory in BIDS. We can use it to set channel
    # types in the raw data using a mapping between channel types
    channel_type_dict = dict()

    # Get the best mapping we currently have from BIDS to MNE nomenclature
    bids_to_mne_ch_types = _get_ch_type_mapping(fro='bids', to='mne')
    ch_types_json = channels_dict['type']
    for ch_name, ch_type in zip(ch_names_tsv, ch_types_json):

        # Try to map from BIDS nomenclature to MNE, leave channel type
        # untouched if we are uncertain
        updated_ch_type = bids_to_mne_ch_types.get(ch_type, None)

        if updated_ch_type is None:
            # XXX Try again with uppercase spelling – this should be removed
            # XXX once https://github.com/bids-standard/bids-validator/issues/1018  # noqa:E501
            # XXX has been resolved.
            # XXX x-ref https://github.com/mne-tools/mne-bids/issues/481
            updated_ch_type = bids_to_mne_ch_types.get(ch_type.upper(), None)
            if updated_ch_type is not None:
                msg = ('The BIDS dataset contains channel types in lowercase '
                       'spelling. This violates the BIDS specification and '
                       'will raise an error in the future.')
                warn(msg)

        if updated_ch_type is not None:
            channel_type_dict[ch_name] = updated_ch_type

    # Rename channels in loaded Raw to match those read from the BIDS sidecar
    for bids_ch_name, raw_ch_name in zip(ch_names_tsv, raw.ch_names.copy()):
        if bids_ch_name != raw_ch_name:
            raw.rename_channels({raw_ch_name: bids_ch_name})

    # Set the channel types in the raw data according to channels.tsv
    raw.set_channel_types(channel_type_dict)

    # Set bad channels based on _channels.tsv sidecar
    if 'status' in channels_dict:
        bads = _get_bads_from_tsv_data(channels_dict)
        raw.info['bads'] = bads

    return raw
예제 #12
0
def load_data(elecfile):
    """
    Load electrodes file.

    Parameters
    ----------
    elecfile: str
        Path to space-delimited text file of contact labels and contact
        coordinates in mm space.

    Returns
    -------
        ct_img: NiBabel image object
            NiBabel image object of CT scan input.

        brainmask_ct: NiBabel image object
            NiBabel image object of brain mask in CT.

        elecinitfile: dict()
            A dictionary of contact coordinates in mm space. Keys are
            individual contact labels, and values are the corresponding
            coordinates in mm space.
    """

    elec_coords_mm = {}

    if elecfile.endswith(".txt"):
        with open(elecfile) as f:
            for l in f:
                row = l.split()
                elec_coords_mm[row[0]] = np.array(list(map(float, row[1:])))
    elif elecfile.endswith(".tsv"):
        electrodes_tsv = _from_tsv(elecfile)
        ch_names = electrodes_tsv["name"]
        elecmatrix = []
        for i in range(len(ch_names)):
            elecmatrix.append([electrodes_tsv[x][i] for x in ["x", "y", "z"]])
        elecmatrix = np.array(elecmatrix, dtype=float)

        for ch_name, coord in zip(ch_names, elecmatrix):
            elec_coords_mm[ch_name] = coord
    else:
        matreader = MatReader()
        data = matreader.loadmat(elecfile)
        elec_coords_mm = data["data"]

    print(
        f"\n\nFinished loading electrode locations data ({len(elec_coords_mm)}: "
    )
    print(list(elec_coords_mm.keys()))
    return elec_coords_mm
예제 #13
0
def print_channel_status(bids_root, subject_id, session_id, acquisition_id):
    """
    Print the channel and status of every channel for a certain acquisition type.

    Parameters
    ----------
    bids_root : Union[Path or str]
        The directory where data is being stored.
    subject_id : str
        The unique identifier of the subject
    session_id: str
        The identifier of the recording session, usually 'seizure' or 'interictal'
    acquisition_id : str
        One of 'eeg', 'ecog', or 'seeg'

    """
    channel_fpaths = _get_subject_channels_tsvs(
        bids_root, subject_id, session_id, acquisition_id
    )
    logger.info(f"Found channel files for subject {subject_id}: {channel_fpaths}")
    if len(channel_fpaths) == 0:
        logger.error(f"No channel files for subject: {subject_id}")
        raise RuntimeError(f"No channel files for subject: {subject_id}.")
    channel_fpath = channel_fpaths[0]
    logger.info(f"Using channel file '{channel_fpath}'")

    # Read in current channel statuses
    channel_tsv = _from_tsv(channel_fpath)
    bad_chs = []
    good_chs = []
    for i, ch in enumerate(channel_tsv["name"]):
        if channel_tsv["status"][i] == "bad":
            bad_chs.append(ch)
        else:
            good_chs.append(ch)

    # create string of channels to display to user
    if len(bad_chs) == 0:
        bad_channels_str = "None"
    else:
        bad_channels_str = ", ".join(bad_chs)
    if len(bad_chs) == 0:
        good_channels_str = "None"
    else:
        good_channels_str = ", ".join(good_chs)

    click.echo(f"Bad channels for {subject_id}:")
    click.echo(bad_channels_str)
    click.echo(f"Channels included in analysis for {subject_id}:")
    click.echo(good_channels_str)
예제 #14
0
def _test_anonymize(raw, bids_basename, events_fname=None, event_id=None):
    bids_root = _TempDir()
    write_raw_bids(raw, bids_basename, bids_root,
                   events_data=events_fname,
                   event_id=event_id, anonymize=dict(daysback=33000),
                   overwrite=False)
    scans_tsv = make_bids_basename(
        subject=subject_id, session=session_id, suffix='scans.tsv',
        prefix=op.join(bids_root, 'sub-01', 'ses-01'))
    data = _from_tsv(scans_tsv)
    if data['acq_time'] is not None and data['acq_time'][0] != 'n/a':
        assert datetime.strptime(data['acq_time'][0],
                                 '%Y-%m-%dT%H:%M:%S').year < 1925
    return bids_root
예제 #15
0
def _handle_events_reading(events_fname, raw,combine_trialType_value=True):
    """Read associated events.tsv and populate raw.

    Handle onset, duration, and description of each event.
    """
    logger.info('Reading events from {}.'.format(events_fname))
    events_dict = _from_tsv(events_fname)

    # Get the descriptions of the events
    if 'trial_type' in events_dict:
        if combine_trialType_value and  ('value' in events_dict):
          descriptions = np.asarray([a+':'+b for a,b in zip(events_dict["trial_type"],events_dict["value"])], dtype=str)  
        # Drop events unrelated to a trial type
        events_dict = _drop(events_dict, 'n/a', 'trial_type')
        descriptions = np.asarray(events_dict['trial_type'], dtype=str)

          
    # If we don't have a proper description of the events, perhaps we have
    # at least an event value?
    elif 'value' in events_dict:
        # Drop events unrelated to value
        events_dict = _drop(events_dict, 'n/a', 'value')
        descriptions = np.asarray(events_dict['value'], dtype=str)

    # Worst case, we go with 'n/a' for all events
    else:
        descriptions = 'n/a'

    # Deal with "n/a" strings before converting to float
    ons = [np.nan if on == 'n/a' else on for on in events_dict['onset']]
    dus = [0 if du == 'n/a' else du for du in events_dict['duration']]
    onsets = np.asarray(ons, dtype=float)
    durations = np.asarray(dus, dtype=float)

    # Keep only events where onset is known
    good_events_idx = ~np.isnan(onsets)
    onsets = onsets[good_events_idx]
    durations = durations[good_events_idx]
    descriptions = descriptions[good_events_idx]
    del good_events_idx

    # Add Events to raw as annotations
    annot_from_events = mne.Annotations(onset=onsets,
                                        duration=durations,
                                        description=descriptions,
                                        orig_time=None)
    raw.set_annotations(annot_from_events)
    return raw
예제 #16
0
파일: write.py 프로젝트: HemuManju/mne-bids
def _scans_tsv(raw, raw_fname, fname, overwrite=False, verbose=True):
    """Create a scans.tsv file and save it.

    Parameters
    ----------
    raw : instance of Raw
        The data as MNE-Python Raw object.
    raw_fname : str
        Relative path to the raw data file.
    fname : str
        Filename to save the scans.tsv to.
    overwrite : bool
        Defaults to False.
        Whether to overwrite the existing data in the file.
        If there is already data for the given `fname` and overwrite is False,
        an error will be raised.
    verbose : bool
        Set verbose output to true or false.

    """
    # get measurement date from the data info
    meas_date = raw.info['meas_date']
    if isinstance(meas_date, (tuple, list, np.ndarray)):
        meas_date = meas_date[0]
        acq_time = datetime.fromtimestamp(meas_date).strftime(
            '%Y-%m-%dT%H:%M:%S')
    else:
        acq_time = 'n/a'

    data = OrderedDict([('filename', ['%s' % raw_fname.replace(os.sep, '/')]),
                        ('acq_time', [acq_time])])

    if os.path.exists(fname):
        orig_data = _from_tsv(fname)
        # if the file name is already in the file raise an error
        if raw_fname in orig_data['filename'] and not overwrite:
            raise FileExistsError(
                '"%s" already exists in the scans list. '  # noqa: E501 F821
                'Please set overwrite to True.' % raw_fname)
        # otherwise add the new data
        data = _combine(orig_data, data, 'filename')

    # overwrite is forced to True as all issues with overwrite == False have
    # been handled by this point
    _write_tsv(fname, data, True, verbose)

    return fname
예제 #17
0
def _handle_scans_reading(scans_fname, raw, bids_path, verbose=False):
    """Read associated scans.tsv and set meas_date."""
    scans_tsv = _from_tsv(scans_fname)
    fname = bids_path.fpath.name

    if fname.endswith('.pdf'):
        # for BTI files, the scan is an entire directory
        fname = fname.split('.')[0]

    # get the row corresponding to the file
    # use string concatenation instead of os.path
    # to work nicely with windows
    data_fname = bids_path.datatype + '/' + fname
    fnames = scans_tsv['filename']
    acq_times = scans_tsv['acq_time']
    row_ind = fnames.index(data_fname)

    # check whether all split files have the same acq_time
    # and throw an error if they don't
    if '_split-' in fname:
        split_idx = fname.find('split-')
        pattern = re.compile(bids_path.datatype + '/' +
                             bids_path.basename[:split_idx] +
                             r'split-\d+_' + bids_path.datatype +
                             bids_path.fpath.suffix)
        split_fnames = list(filter(pattern.match, fnames))
        split_acq_times = []
        for split_f in split_fnames:
            split_acq_times.append(acq_times[fnames.index(split_f)])
        if len(set(split_acq_times)) != 1:
            raise ValueError("Split files must have the same acq_time.")

    # extract the acquisition time from scans file
    acq_time = acq_times[row_ind]
    if acq_time != 'n/a':
        # microseconds in the acquisition time is optional
        if '.' not in acq_time:
            # acquisition time ends with '.%fZ' microseconds string
            acq_time += '.0Z'
        acq_time = datetime.strptime(acq_time, '%Y-%m-%dT%H:%M:%S.%fZ')
        acq_time = acq_time.replace(tzinfo=timezone.utc)

        if verbose:
            logger.debug(f'Loaded {scans_fname} scans file to set '
                         f'acq_time as {acq_time}.')
        raw.set_meas_date(acq_time)
    return raw
예제 #18
0
def test_handle_coords_reading():
    """Test reading coordinates from BIDS files."""
    bids_root = _TempDir()

    data_path = op.join(testing.data_path(), 'EDF')
    raw_fname = op.join(data_path, 'test_reduced.edf')

    raw = mne.io.read_raw_edf(raw_fname)

    # ensure we are writing 'ecog'/'ieeg' data
    raw.set_channel_types({ch: 'ecog' for ch in raw.ch_names})

    # set a `random` montage
    ch_names = raw.ch_names
    elec_locs = np.random.random((len(ch_names), 3)).astype(float)
    ch_pos = dict(zip(ch_names, elec_locs))
    montage = mne.channels.make_dig_montage(ch_pos=ch_pos, coord_frame="mri")
    raw.set_montage(montage)
    write_raw_bids(raw, bids_basename, bids_root, overwrite=True)

    # read in the data and assert montage is the same
    bids_fname = bids_basename + "_ieeg.edf"
    raw_test = read_raw_bids(bids_fname, bids_root)

    # obtain the sensor positions
    orig_locs = raw.info['dig'][1]
    test_locs = raw_test.info['dig'][1]
    assert orig_locs == test_locs
    assert not object_diff(raw.info['chs'], raw_test.info['chs'])

    # test error message if electrodes don't match
    electrodes_fname = _find_matching_sidecar(bids_fname,
                                              bids_root,
                                              "electrodes.tsv",
                                              allow_fail=True)
    electrodes_dict = _from_tsv(electrodes_fname)
    # pop off 5 channels
    for key in electrodes_dict.keys():
        for i in range(5):
            electrodes_dict[key].pop()
    _to_tsv(electrodes_dict, electrodes_fname)
    with pytest.raises(RuntimeError, match='Channels do not correspond'):
        raw_test = read_raw_bids(bids_fname, bids_root)
예제 #19
0
def test_read_participants_handedness_and_sex_mapping(hand_bids, hand_mne,
                                                      sex_bids, sex_mne):
    """Test we're correctly mapping handedness and sex between BIDS and MNE."""
    bids_root = _TempDir()
    bids_path = _bids_path.copy().update(root=bids_root, datatype='meg')
    participants_tsv_fpath = op.join(bids_root, 'participants.tsv')
    raw = _read_raw_fif(raw_fname, verbose=False)

    # Avoid that we end up with subject information stored in the raw data.
    raw.info['subject_info'] = {}
    write_raw_bids(raw, bids_path, overwrite=True, verbose=False)

    participants_tsv = _from_tsv(participants_tsv_fpath)
    participants_tsv['hand'][0] = hand_bids
    participants_tsv['sex'][0] = sex_bids
    _to_tsv(participants_tsv, participants_tsv_fpath)

    raw = read_raw_bids(bids_path=bids_path)
    assert raw.info['subject_info']['hand'] is hand_mne
    assert raw.info['subject_info']['sex'] is sex_mne
예제 #20
0
def test_handle_channel_type_casing(tmpdir):
    """Test that non-uppercase entries in the `type` column are accepted."""
    bids_path = _bids_path.copy().update(root=tmpdir)
    raw = _read_raw_fif(raw_fname, verbose=False)

    write_raw_bids(raw, bids_path, overwrite=True, verbose=False)

    ch_path = bids_path.copy().update(root=tmpdir,
                                      datatype='meg',
                                      suffix='channels',
                                      extension='.tsv')
    bids_channels_fname = ch_path.fpath

    # Convert all channel type entries to lowercase.
    channels_data = _from_tsv(bids_channels_fname)
    channels_data['type'] = [t.lower() for t in channels_data['type']]
    _to_tsv(channels_data, bids_channels_fname)

    with pytest.warns(RuntimeWarning, match='lowercase spelling'):
        read_raw_bids(bids_path)
예제 #21
0
파일: test_read.py 프로젝트: eort/mne-bids
def test_handle_scans_reading():
    """Test reading data from a BIDS scans.tsv file."""
    bids_root = _TempDir()
    raw = _read_raw_fif(raw_fname)
    suffix = "meg"

    # write copy of raw with line freq of 60
    # bids basename and fname
    bids_path = BIDSPath(subject='01',
                         session='01',
                         task='audiovisual',
                         run='01',
                         datatype=suffix,
                         root=bids_root)
    bids_path = write_raw_bids(raw, bids_path, overwrite=True)
    raw_01 = read_raw_bids(bids_path)

    # find sidecar scans.tsv file and alter the
    # acquisition time to not have the optional microseconds
    scans_path = BIDSPath(subject=bids_path.subject,
                          session=bids_path.session,
                          root=bids_root,
                          suffix='scans',
                          extension='.tsv')
    scans_tsv = _from_tsv(scans_path)
    acq_time_str = scans_tsv['acq_time'][0]
    acq_time = datetime.strptime(acq_time_str, '%Y-%m-%dT%H:%M:%S.%fZ')
    acq_time = acq_time.replace(tzinfo=timezone.utc)
    new_acq_time = acq_time_str.split('.')[0]
    assert acq_time == raw_01.info['meas_date']
    scans_tsv['acq_time'][0] = new_acq_time
    _to_tsv(scans_tsv, scans_path)

    # now re-load the data and it should be different
    # from the original date and the same as the newly altered date
    raw_02 = read_raw_bids(bids_path)
    new_acq_time += '.0Z'
    new_acq_time = datetime.strptime(new_acq_time, '%Y-%m-%dT%H:%M:%S.%fZ')
    new_acq_time = new_acq_time.replace(tzinfo=timezone.utc)
    assert raw_02.info['meas_date'] == new_acq_time
    assert new_acq_time != raw_01.info['meas_date']
예제 #22
0
def _handle_events_reading(events_fname, raw):
    """Read associated events.tsv and populate raw.

    Handle onset, duration, and description of each event.
    """
    logger.info('Reading events from {}.'.format(events_fname))
    events_dict = _from_tsv(events_fname)

    # Get the descriptions of the events
    if 'trial_type' in events_dict:
        # Drop events unrelated to a trial type
        events_dict = _drop(events_dict, 'n/a', 'trial_type')
        descriptions = np.asarray(events_dict['trial_type'], dtype=str)

    # If we don't have a proper description of the events, perhaps we have
    # at least an event value?
    elif 'value' in events_dict:
        # Drop events unrelated to value
        events_dict = _drop(events_dict, 'n/a', 'value')
        descriptions = np.asarray(events_dict['value'], dtype=str)

    # Worst case, we go with 'n/a' for all events
    else:
        descriptions = 'n/a'

    # Deal with "n/a" strings before converting to float
    ons = [np.nan if on == 'n/a' else on for on in events_dict['onset']]
    dus = [np.nan if du == 'n/a' else du for du in events_dict['duration']]

    # Add Events to raw as annotations
    onsets = np.asarray(ons, dtype=float)
    durations = np.asarray(dus, dtype=float)
    annot_from_events = mne.Annotations(onset=onsets,
                                        duration=durations,
                                        description=descriptions,
                                        orig_time=None)
    raw.set_annotations(annot_from_events)

    return raw
예제 #23
0
파일: read.py 프로젝트: eort/mne-bids
def _handle_scans_reading(scans_fname, raw, bids_path, verbose=False):
    """Read associated scans.tsv and set meas_date."""
    scans_tsv = _from_tsv(scans_fname)
    fname = bids_path.fpath.name

    if '_split-' in fname:
        # for split files, scans only stores the filename without ``split``
        extension = bids_path.fpath.suffix
        bids_path.update(split=None, extension=extension)
        fname = bids_path.basename
    elif fname.endswith('.pdf'):
        # for BTI files, the scan is an entire directory
        fname = fname.split('.')[0]

    # get the row corresponding to the file
    # use string concatenation instead of os.path
    # to work nicely with windows
    data_fname = bids_path.datatype + '/' + fname
    fnames = scans_tsv['filename']
    acq_times = scans_tsv['acq_time']
    row_ind = fnames.index(data_fname)

    # extract the acquisition time from scans file
    acq_time = acq_times[row_ind]
    if acq_time != 'n/a':
        # microseconds in the acquisition time is optional
        if '.' not in acq_time:
            # acquisition time ends with '.%fZ' microseconds string
            acq_time += '.0Z'
        acq_time = datetime.strptime(acq_time, '%Y-%m-%dT%H:%M:%S.%fZ')
        acq_time = acq_time.replace(tzinfo=timezone.utc)

        if verbose:
            logger.debug(f'Loaded {scans_fname} scans file to set '
                         f'acq_time as {acq_time}.')
        raw.set_meas_date(acq_time)
    return raw
예제 #24
0
def _handle_electrodes_reading(electrodes_fname, coord_frame,
                               coord_unit, raw, verbose):
    """Read associated electrodes.tsv and populate raw.

    Handle xyz coordinates and coordinate frame of each channel.
    Assumes units of coordinates are in 'm'.
    """
    logger.info('Reading electrode '
                'coords from {}.'.format(electrodes_fname))
    electrodes_dict = _from_tsv(electrodes_fname)
    # First, make sure that ordering of names in channels.tsv matches the
    # ordering of names in the raw data. The "name" column is mandatory in BIDS
    ch_names_raw = list(raw.ch_names)
    ch_names_tsv = electrodes_dict['name']

    if ch_names_raw != ch_names_tsv:
        msg = ('Channels do not correspond between raw data and the '
               'channels.tsv file. For MNE-BIDS, the channel names in the '
               'tsv MUST be equal and in the same order as the channels in '
               'the raw data.\n\n'
               '{} channels in tsv file: "{}"\n\n --> {}\n\n'
               '{} channels in raw file: "{}"\n\n --> {}\n\n'
               .format(len(ch_names_tsv), electrodes_fname, ch_names_tsv,
                       len(ch_names_raw), raw.filenames, ch_names_raw)
               )

        # XXX: this could be due to MNE inserting a 'STI 014' channel as the
        # last channel: In that case, we can work. --> Can be removed soon,
        # because MNE will stop the synthesis of stim channels in the near
        # future
        if not (ch_names_raw[-1] == 'STI 014' and
                ch_names_raw[:-1] == ch_names_tsv):
            raise RuntimeError(msg)

    if verbose:
        summary_str = [(ch, coord) for idx, (ch, coord)
                       in enumerate(electrodes_dict.items())
                       if idx < 5]
        print("The read in electrodes file is: \n", summary_str)

    def _float_or_nan(val):
        if val == "n/a":
            return np.nan
        else:
            return float(val)

    # convert coordinates to float and create list of tuples
    electrodes_dict['x'] = [_float_or_nan(x) for x in electrodes_dict['x']]
    electrodes_dict['y'] = [_float_or_nan(x) for x in electrodes_dict['y']]
    electrodes_dict['z'] = [_float_or_nan(x) for x in electrodes_dict['z']]
    ch_names_raw = [x for i, x in enumerate(ch_names_raw)
                    if electrodes_dict['x'][i] != "n/a"]
    ch_locs = np.c_[electrodes_dict['x'],
                    electrodes_dict['y'],
                    electrodes_dict['z']]

    # determine if there are problematic channels
    nan_chs = []
    for ch_name, ch_coord in zip(ch_names_raw, ch_locs):
        if any(np.isnan(ch_coord)) and ch_name not in raw.info['bads']:
            nan_chs.append(ch_name)
    if len(nan_chs) > 0:
        warn("There are channels without locations "
             "(n/a) that are not marked as bad: {}".format(nan_chs))

    # convert coordinates to meters
    ch_locs = _scale_coord_to_meters(ch_locs, coord_unit)

    # create mne.DigMontage
    ch_pos = dict(zip(ch_names_raw, ch_locs))
    montage = mne.channels.make_dig_montage(ch_pos=ch_pos,
                                            coord_frame=coord_frame)
    raw.set_montage(montage)
    return raw
예제 #25
0
파일: read.py 프로젝트: eort/mne-bids
def _handle_events_reading(events_fname, raw):
    """Read associated events.tsv and populate raw.

    Handle onset, duration, and description of each event.
    """
    logger.info('Reading events from {}.'.format(events_fname))
    events_dict = _from_tsv(events_fname)

    # Get the descriptions of the events
    if 'trial_type' in events_dict:
        trial_type_col_name = 'trial_type'
    elif 'stim_type' in events_dict:  # Backward-compat with old datasets.
        trial_type_col_name = 'stim_type'
        warn(f'The events file, {events_fname}, contains a "stim_type" '
             f'column. This column should be renamed to "trial_type" for '
             f'BIDS compatibility.')
    else:
        trial_type_col_name = None

    if trial_type_col_name is not None:
        # Drop events unrelated to a trial type
        events_dict = _drop(events_dict, 'n/a', trial_type_col_name)

        if 'value' in events_dict:
            # Check whether the `trial_type` <> `value` mapping is unique.
            trial_types = events_dict[trial_type_col_name]
            values = np.asarray(events_dict['value'], dtype=str)
            for trial_type in np.unique(trial_types):
                idx = np.where(trial_type == np.atleast_1d(trial_types))[0]
                matching_values = values[idx]

                if len(np.unique(matching_values)) > 1:
                    # Event type descriptors are ambiguous; create hierarchical
                    # event descriptors.
                    logger.info(
                        f'The event "{trial_type}" refers to multiple event '
                        f'values. Creating hierarchical event names.')
                    for ii in idx:
                        new_name = f'{trial_type}/{values[ii]}'
                        logger.info(f'    Renaming event: {trial_type} -> '
                                    f'{new_name}')
                        trial_types[ii] = new_name
            descriptions = np.asarray(trial_types, dtype=str)
        else:
            descriptions = np.asarray(events_dict[trial_type_col_name],
                                      dtype=str)
    elif 'value' in events_dict:
        # If we don't have a proper description of the events, perhaps we have
        # at least an event value?
        # Drop events unrelated to value
        events_dict = _drop(events_dict, 'n/a', 'value')
        descriptions = np.asarray(events_dict['value'], dtype=str)

    # Worst case, we go with 'n/a' for all events
    else:
        descriptions = np.array(['n/a'] * len(events_dict['onset']), dtype=str)

    # Deal with "n/a" strings before converting to float
    ons = [np.nan if on == 'n/a' else on for on in events_dict['onset']]
    dus = [0 if du == 'n/a' else du for du in events_dict['duration']]
    onsets = np.asarray(ons, dtype=float)
    durations = np.asarray(dus, dtype=float)

    # Keep only events where onset is known
    good_events_idx = ~np.isnan(onsets)
    onsets = onsets[good_events_idx]
    durations = durations[good_events_idx]
    descriptions = descriptions[good_events_idx]
    del good_events_idx

    # Add Events to raw as annotations
    annot_from_events = mne.Annotations(onset=onsets,
                                        duration=durations,
                                        description=descriptions,
                                        orig_time=None)
    raw.set_annotations(annot_from_events)
    return raw
예제 #26
0
        help="The Freesurfer LUT.",
        required=False,
        default=fs_lut_fpath,
    )
    parser.add_argument("-mask_img_fpath",
                        required=False,
                        default=mask_img_fpath.fpath)
    args = parser.parse_args()

    # Extract arguments from parser
    mri_xyzcoords_fpath = args.mri_xyzcoords_fpath
    output_electrodes_tsv_fpath = args.output_bids_electrodes_file
    fs_patient_dir = args.fs_patient_dir
    fs_lut_fpath = args.fs_lut_fpath
    mask_img_fpath = args.mask_img_fpath

    # load in the T1 MRI image and its affine
    print(f"Loading in mask file {mask_img_fpath}")
    t1_img = nb.load(mask_img_fpath)

    # load in the electrode coordinates
    print(f"Loading file {output_electrodes_tsv_fpath}")
    electrodes_tsv = _from_tsv(output_electrodes_tsv_fpath)

    # extract subject id from bids sidecar electrodes fname
    subject_id = (os.path.basename(output_electrodes_tsv_fpath).split("_")
                  [0].split("sub-")[1])

    # apply atlas
    apply_mask_on_electrodes(electrodes_tsv, t1_img)
예제 #27
0
def _write_electrodes_tsv(raw, fname, datatype, overwrite=False, verbose=True):
    """Create an electrodes.tsv file and save it.

    Parameters
    ----------
    raw : mne.io.Raw
        The data as MNE-Python Raw object.
    fname : str
        Filename to save the electrodes.tsv to.
    datatype : str
        Type of the data recording. Can be ``meg``, ``eeg``,
        or ``ieeg``.
    overwrite : bool
        Defaults to False.
        Whether to overwrite the existing data in the file.
        If there is already data for the given `fname` and overwrite is False,
        an error will be raised.
    verbose : bool
        Set verbose output to true or false.
    """
    # create list of channel coordinates and names
    x, y, z, names = list(), list(), list(), list()
    for ch in raw.info['chs']:
        if _check_ch_locs([ch]):
            x.append(ch['loc'][0])
            y.append(ch['loc'][1])
            z.append(ch['loc'][2])
        else:
            x.append('n/a')
            y.append('n/a')
            z.append('n/a')
        names.append(ch['ch_name'])

    # create OrderedDict to write to tsv file
    if datatype == "ieeg":
        # XXX: size should be included in the future
        sizes = ['n/a'] * len(names)
        data = OrderedDict([
            ('name', names),
            ('x', x),
            ('y', y),
            ('z', z),
            ('size', sizes),
        ])
    elif datatype == 'eeg':
        data = OrderedDict([
            ('name', names),
            ('x', x),
            ('y', y),
            ('z', z),
        ])
    else:  # pragma: no cover
        raise RuntimeError("datatype {} not supported.".format(datatype))

    # Add impedance values if available, currently only BrainVision:
    # https://github.com/mne-tools/mne-python/pull/7974
    if hasattr(raw, 'impedances'):
        data['impedance'] = _get_impedances(raw, names)

    # note that any coordsystem.json file shared within sessions
    # will be the same across all runs (currently). So
    # overwrite is set to True always
    # XXX: improve later when BIDS is updated
    # check that there already exists a coordsystem.json
    if Path(fname).exists() and not overwrite:
        electrodes_tsv = _from_tsv(fname)

        # cast values to str to make equality check work
        if any([
                list(map(str, vals1)) != list(vals2)
                for vals1, vals2 in zip(data.values(), electrodes_tsv.values())
        ]):
            raise RuntimeError(
                f'Trying to write electrodes.tsv, but it already '
                f'exists at {fname} and the contents do not match. '
                f'You must differentiate this electrodes.tsv file '
                f'from the existing one, or set "overwrite" to True.')
    _write_tsv(fname, data, overwrite=True, verbose=verbose)
예제 #28
0
def test_fif(_bids_validate):
    """Test functionality of the write_raw_bids conversion for fif."""
    output_path = _TempDir()
    data_path = testing.data_path()
    raw_fname = op.join(data_path, 'MEG', 'sample',
                        'sample_audvis_trunc_raw.fif')

    event_id = {
        'Auditory/Left': 1,
        'Auditory/Right': 2,
        'Visual/Left': 3,
        'Visual/Right': 4,
        'Smiley': 5,
        'Button': 32
    }
    events_fname = op.join(data_path, 'MEG', 'sample',
                           'sample_audvis_trunc_raw-eve.fif')

    raw = mne.io.read_raw_fif(raw_fname)
    write_raw_bids(raw,
                   bids_basename,
                   output_path,
                   events_data=events_fname,
                   event_id=event_id,
                   overwrite=False)

    # Read the file back in to check that the data has come through cleanly.
    # Events and bad channel information was read through JSON sidecar files.
    with pytest.raises(TypeError, match="unexpected keyword argument 'foo'"):
        read_raw_bids(bids_basename + '_meg.fif',
                      output_path,
                      extra_params=dict(foo='bar'))

    raw2 = read_raw_bids(bids_basename + '_meg.fif',
                         output_path,
                         extra_params=dict(allow_maxshield=True))
    assert set(raw.info['bads']) == set(raw2.info['bads'])
    events, _ = mne.events_from_annotations(raw2)
    events2 = mne.read_events(events_fname)
    events2 = events2[events2[:, 2] != 0]
    assert_array_equal(events2[:, 0], events[:, 0])

    # check if write_raw_bids works when there is no stim channel
    raw.set_channel_types({
        raw.ch_names[i]: 'misc'
        for i in mne.pick_types(raw.info, stim=True, meg=False)
    })
    output_path = _TempDir()
    with pytest.warns(UserWarning, match='No events found or provided.'):
        write_raw_bids(raw, bids_basename, output_path, overwrite=False)

    _bids_validate(output_path)

    # try with eeg data only (conversion to bv)
    output_path = _TempDir()
    raw = mne.io.read_raw_fif(raw_fname)
    raw.load_data()
    raw2 = raw.pick_types(meg=False, eeg=True, stim=True, eog=True, ecg=True)
    raw2.save(op.join(output_path, 'test-raw.fif'), overwrite=True)
    raw2 = mne.io.Raw(op.join(output_path, 'test-raw.fif'), preload=False)
    events = mne.find_events(raw2)
    event_id = {
        'auditory/left': 1,
        'auditory/right': 2,
        'visual/left': 3,
        'visual/right': 4,
        'smiley': 5,
        'button': 32
    }
    epochs = mne.Epochs(raw2,
                        events,
                        event_id=event_id,
                        tmin=-0.2,
                        tmax=0.5,
                        preload=True)
    with pytest.warns(UserWarning,
                      match='Converting data files to BrainVision format'):
        write_raw_bids(raw2,
                       bids_basename,
                       output_path,
                       events_data=events_fname,
                       event_id=event_id,
                       verbose=True,
                       overwrite=False)
    bids_dir = op.join(output_path, 'sub-%s' % subject_id,
                       'ses-%s' % session_id, 'eeg')
    for sidecar in [
            'channels.tsv', 'eeg.eeg', 'eeg.json', 'eeg.vhdr', 'eeg.vmrk',
            'events.tsv'
    ]:
        assert op.isfile(op.join(bids_dir, bids_basename + '_' + sidecar))

    raw2 = read_raw_bids(bids_basename + '_eeg.vhdr', output_path)
    os.remove(op.join(output_path, 'test-raw.fif'))

    events2 = mne.find_events(raw2)
    epochs2 = mne.Epochs(raw2,
                         events2,
                         event_id=event_id,
                         tmin=-0.2,
                         tmax=0.5,
                         preload=True)
    assert_array_almost_equal(raw.get_data(), raw2.get_data())
    assert_array_almost_equal(epochs.get_data(), epochs2.get_data(), decimal=4)
    _bids_validate(output_path)

    # write the same data but pretend it is empty room data:
    raw = mne.io.read_raw_fif(raw_fname)
    er_date = datetime.fromtimestamp(
        raw.info['meas_date'][0]).strftime('%Y%m%d')
    er_bids_basename = 'sub-emptyroom_ses-{0}_task-noise'.format(str(er_date))
    write_raw_bids(raw, er_bids_basename, output_path, overwrite=False)
    assert op.exists(
        op.join(output_path, 'sub-emptyroom', 'ses-{0}'.format(er_date), 'meg',
                'sub-emptyroom_ses-{0}_task-noise_meg.json'.format(er_date)))
    # test that an incorrect date raises an error.
    er_bids_basename_bad = 'sub-emptyroom_ses-19000101_task-noise'
    with pytest.raises(ValueError, match='Date provided'):
        write_raw_bids(raw, er_bids_basename_bad, output_path, overwrite=False)

    # give the raw object some fake participant data (potentially overwriting)
    raw = mne.io.read_raw_fif(raw_fname)
    raw.info['subject_info'] = {
        'his_id': subject_id2,
        'birthday': (1993, 1, 26),
        'sex': 1
    }
    write_raw_bids(raw,
                   bids_basename,
                   output_path,
                   events_data=events_fname,
                   event_id=event_id,
                   overwrite=True)
    # assert age of participant is correct
    participants_tsv = op.join(output_path, 'participants.tsv')
    data = _from_tsv(participants_tsv)
    assert data['age'][data['participant_id'].index('sub-01')] == '9'

    # try and write preloaded data
    raw = mne.io.read_raw_fif(raw_fname, preload=True)
    with pytest.raises(ValueError, match='preloaded'):
        write_raw_bids(raw,
                       bids_basename,
                       output_path,
                       events_data=events_fname,
                       event_id=event_id,
                       overwrite=False)

    # test anonymize
    raw = mne.io.read_raw_fif(raw_fname)
    raw.anonymize()

    data_path2 = _TempDir()
    raw_fname2 = op.join(data_path2, 'sample_audvis_raw.fif')
    raw.save(raw_fname2)

    bids_basename2 = bids_basename.replace(subject_id, subject_id2)
    raw = mne.io.read_raw_fif(raw_fname2)
    bids_output_path = write_raw_bids(raw,
                                      bids_basename2,
                                      output_path,
                                      events_data=events_fname,
                                      event_id=event_id,
                                      overwrite=False)

    # check that the overwrite parameters work correctly for the participant
    # data
    # change the gender but don't force overwrite.
    raw.info['subject_info'] = {
        'his_id': subject_id2,
        'birthday': (1994, 1, 26),
        'sex': 2
    }
    with pytest.raises(FileExistsError, match="already exists"):  # noqa: F821
        write_raw_bids(raw,
                       bids_basename2,
                       output_path,
                       events_data=events_fname,
                       event_id=event_id,
                       overwrite=False)
    # now force the overwrite
    write_raw_bids(raw,
                   bids_basename2,
                   output_path,
                   events_data=events_fname,
                   event_id=event_id,
                   overwrite=True)

    with pytest.raises(ValueError, match='raw_file must be'):
        write_raw_bids('blah', bids_basename, output_path)

    bids_basename2 = 'sub-01_ses-01_xyz-01_run-01'
    with pytest.raises(KeyError, match='Unexpected entity'):
        write_raw_bids(raw, bids_basename2, output_path)

    bids_basename2 = 'sub-01_run-01_task-auditory'
    with pytest.raises(ValueError, match='ordered correctly'):
        write_raw_bids(raw, bids_basename2, output_path, overwrite=True)

    del raw._filenames
    with pytest.raises(ValueError, match='raw.filenames is missing'):
        write_raw_bids(raw, bids_basename2, output_path)

    _bids_validate(output_path)

    assert op.exists(op.join(output_path, 'participants.tsv'))

    # asserting that single fif files do not include the part key
    files = glob(
        op.join(bids_output_path, 'sub-' + subject_id2, 'ses-' + subject_id2,
                'meg', '*.fif'))
    for ii, FILE in enumerate(files):
        assert 'part' not in FILE
    assert ii < 1

    # test keyword mne-bids anonymize
    raw = mne.io.read_raw_fif(raw_fname)
    with pytest.raises(ValueError, match='`daysback` argument required'):
        write_raw_bids(raw,
                       bids_basename,
                       output_path,
                       events_data=events_fname,
                       event_id=event_id,
                       anonymize=dict(),
                       overwrite=True)

    output_path = _TempDir()
    raw = mne.io.read_raw_fif(raw_fname)
    with pytest.warns(UserWarning, match='daysback` is too small'):
        write_raw_bids(raw,
                       bids_basename,
                       output_path,
                       events_data=events_fname,
                       event_id=event_id,
                       anonymize=dict(daysback=400),
                       overwrite=False)

    output_path = _TempDir()
    raw = mne.io.read_raw_fif(raw_fname)
    with pytest.raises(ValueError, match='`daysback` exceeds maximum value'):
        write_raw_bids(raw,
                       bids_basename,
                       output_path,
                       events_data=events_fname,
                       event_id=event_id,
                       anonymize=dict(daysback=40000),
                       overwrite=False)

    output_path = _TempDir()
    raw = mne.io.read_raw_fif(raw_fname)
    write_raw_bids(raw,
                   bids_basename,
                   output_path,
                   events_data=events_fname,
                   event_id=event_id,
                   anonymize=dict(daysback=30000, keep_his=True),
                   overwrite=False)
    scans_tsv = make_bids_basename(subject=subject_id,
                                   session=session_id,
                                   suffix='scans.tsv',
                                   prefix=op.join(output_path, 'sub-01',
                                                  'ses-01'))
    data = _from_tsv(scans_tsv)
    assert datetime.strptime(data['acq_time'][0],
                             '%Y-%m-%dT%H:%M:%S').year < 1925
    _bids_validate(output_path)

    # check that split files have part key
    raw = mne.io.read_raw_fif(raw_fname)
    data_path3 = _TempDir()
    raw_fname3 = op.join(data_path3, 'sample_audvis_raw.fif')
    raw.save(raw_fname3,
             buffer_size_sec=1.0,
             split_size='10MB',
             split_naming='neuromag',
             overwrite=True)
    raw = mne.io.read_raw_fif(raw_fname3)
    subject_id3 = '03'
    bids_basename3 = bids_basename.replace(subject_id, subject_id3)
    bids_output_path = write_raw_bids(raw,
                                      bids_basename3,
                                      output_path,
                                      overwrite=False)
    files = glob(
        op.join(bids_output_path, 'sub-' + subject_id3, 'ses-' + subject_id3,
                'meg', '*.fif'))
    for FILE in files:
        assert 'part' in FILE

    # test unknown extention
    raw = mne.io.read_raw_fif(raw_fname)
    raw._filenames = (raw.filenames[0].replace('.fif', '.foo'), )
    with pytest.raises(ValueError, match='Unrecognized file format'):
        write_raw_bids(raw, bids_basename, output_path)
예제 #29
0
def test_kit(_bids_validate):
    """Test functionality of the write_raw_bids conversion for KIT data."""
    output_path = _TempDir()
    data_path = op.join(base_path, 'kit', 'tests', 'data')
    raw_fname = op.join(data_path, 'test.sqd')
    events_fname = op.join(data_path, 'test-eve.txt')
    hpi_fname = op.join(data_path, 'test_mrk.sqd')
    hpi_pre_fname = op.join(data_path, 'test_mrk_pre.sqd')
    hpi_post_fname = op.join(data_path, 'test_mrk_post.sqd')
    electrode_fname = op.join(data_path, 'test_elp.txt')
    headshape_fname = op.join(data_path, 'test_hsp.txt')
    event_id = dict(cond=1)

    kit_bids_basename = bids_basename.replace('_acq-01', '')

    raw = mne.io.read_raw_kit(raw_fname,
                              mrk=hpi_fname,
                              elp=electrode_fname,
                              hsp=headshape_fname)
    write_raw_bids(raw,
                   kit_bids_basename,
                   output_path,
                   events_data=events_fname,
                   event_id=event_id,
                   overwrite=False)

    _bids_validate(output_path)
    assert op.exists(op.join(output_path, 'participants.tsv'))

    read_raw_bids(kit_bids_basename + '_meg.sqd', output_path)

    # test anonymize
    output_path = _test_anonymize(raw, kit_bids_basename, events_fname,
                                  event_id)
    _bids_validate(output_path)

    # ensure the channels file has no STI 014 channel:
    channels_tsv = make_bids_basename(subject=subject_id,
                                      session=session_id,
                                      task=task,
                                      run=run,
                                      suffix='channels.tsv',
                                      prefix=op.join(output_path, 'sub-01',
                                                     'ses-01', 'meg'))
    data = _from_tsv(channels_tsv)
    assert 'STI 014' not in data['name']

    # ensure the marker file is produced in the right place
    marker_fname = make_bids_basename(subject=subject_id,
                                      session=session_id,
                                      task=task,
                                      run=run,
                                      suffix='markers.sqd',
                                      prefix=op.join(output_path, 'sub-01',
                                                     'ses-01', 'meg'))
    assert op.exists(marker_fname)

    # test attempts at writing invalid event data
    event_data = np.loadtxt(events_fname)
    # make the data the wrong number of dimensions
    event_data_3d = np.atleast_3d(event_data)
    other_output_path = _TempDir()
    with pytest.raises(ValueError, match='two dimensions'):
        write_raw_bids(raw,
                       bids_basename,
                       other_output_path,
                       events_data=event_data_3d,
                       event_id=event_id,
                       overwrite=True)
    # remove 3rd column
    event_data = event_data[:, :2]
    with pytest.raises(ValueError, match='second dimension'):
        write_raw_bids(raw,
                       bids_basename,
                       other_output_path,
                       events_data=event_data,
                       event_id=event_id,
                       overwrite=True)
    # test correct naming of marker files
    raw = mne.io.read_raw_kit(raw_fname,
                              mrk=[hpi_pre_fname, hpi_post_fname],
                              elp=electrode_fname,
                              hsp=headshape_fname)
    write_raw_bids(raw,
                   kit_bids_basename.replace('sub-01', 'sub-%s' % subject_id2),
                   output_path,
                   events_data=events_fname,
                   event_id=event_id,
                   overwrite=False)

    _bids_validate(output_path)
    # ensure the marker files are renamed correctly
    marker_fname = make_bids_basename(subject=subject_id2,
                                      session=session_id,
                                      task=task,
                                      run=run,
                                      suffix='markers.sqd',
                                      acquisition='pre',
                                      prefix=os.path.join(
                                          output_path, 'sub-02', 'ses-01',
                                          'meg'))
    info = get_kit_info(marker_fname, False)[0]
    assert info['meas_date'] == get_kit_info(hpi_pre_fname,
                                             False)[0]['meas_date']
    marker_fname = marker_fname.replace('acq-pre', 'acq-post')
    info = get_kit_info(marker_fname, False)[0]
    assert info['meas_date'] == get_kit_info(hpi_post_fname,
                                             False)[0]['meas_date']

    # check that providing markers in the wrong order raises an error
    raw = mne.io.read_raw_kit(raw_fname,
                              mrk=[hpi_post_fname, hpi_pre_fname],
                              elp=electrode_fname,
                              hsp=headshape_fname)
    with pytest.raises(ValueError, match='Markers'):
        write_raw_bids(raw,
                       kit_bids_basename.replace('sub-01',
                                                 'sub-%s' % subject_id2),
                       output_path,
                       events_data=events_fname,
                       event_id=event_id,
                       overwrite=True)
예제 #30
0
def _handle_channels_reading(channels_fname, bids_fname, raw):
    """Read associated channels.tsv and populate raw.

    Updates status (bad) and types of channels.
    """
    logger.info('Reading channel info from {}.'.format(channels_fname))
    channels_dict = _from_tsv(channels_fname)

    # First, make sure that ordering of names in channels.tsv matches the
    # ordering of names in the raw data. The "name" column is mandatory in BIDS
    ch_names_raw = list(raw.ch_names)
    ch_names_tsv = channels_dict['name']
    if ch_names_raw != ch_names_tsv:

        msg = ('Channels do not correspond between raw data and the '
               'channels.tsv file. For MNE-BIDS, the channel names in the '
               'tsv MUST be equal and in the same order as the channels in '
               'the raw data.\n\n'
               '{} channels in tsv file: "{}"\n\n --> {}\n\n'
               '{} channels in raw file: "{}"\n\n --> {}\n\n'.format(
                   len(ch_names_tsv), channels_fname, ch_names_tsv,
                   len(ch_names_raw), bids_fname, ch_names_raw))

        # XXX: this could be due to MNE inserting a 'STI 014' channel as the
        # last channel: In that case, we can work. --> Can be removed soon,
        # because MNE will stop the synthesis of stim channels in the near
        # future
        if not (ch_names_raw[-1] == 'STI 014'
                and ch_names_raw[:-1] == ch_names_tsv):
            raise RuntimeError(msg)

    # Now we can do some work.
    # The "type" column is mandatory in BIDS. We can use it to set channel
    # types in the raw data using a mapping between channel types
    channel_type_dict = dict()

    # Get the best mapping we currently have from BIDS to MNE nomenclature
    bids_to_mne_ch_types = _get_ch_type_mapping(fro='bids', to='mne')
    ch_types_json = channels_dict['type']
    for ch_name, ch_type in zip(ch_names_tsv, ch_types_json):

        # Try to map from BIDS nomenclature to MNE, leave channel type
        # untouched if we are uncertain
        updated_ch_type = bids_to_mne_ch_types.get(ch_type, None)

        if updated_ch_type is None:
            # XXX Try again with uppercase spelling – this should be removed
            # XXX once https://github.com/bids-standard/bids-validator/issues/1018  # noqa:E501
            # XXX has been resolved.
            # XXX x-ref https://github.com/mne-tools/mne-bids/issues/481
            updated_ch_type = bids_to_mne_ch_types.get(ch_type.upper(), None)
            if updated_ch_type is not None:
                msg = ('The BIDS dataset contains channel types in lowercase '
                       'spelling. This violates the BIDS specification and '
                       'will raise an error in the future.')
                warn(msg)

        if updated_ch_type is not None:
            channel_type_dict[ch_name] = updated_ch_type

    # Set the channel types in the raw data according to channels.tsv
    raw.set_channel_types(channel_type_dict)

    # Check whether there is the optional "status" column from which to infer
    # good and bad channels
    if 'status' in channels_dict:
        # find bads from channels.tsv
        bads_from_tsv = _get_bads_from_tsv_data(channels_dict)

        if raw.info['bads'] and set(bads_from_tsv) != set(raw.info['bads']):
            warn(f'Encountered conflicting information on channel status '
                 f'between {op.basename(channels_fname)} and the associated '
                 f'raw data file.\n'
                 f'Channels marked as bad in '
                 f'{op.basename(channels_fname)}: {bads_from_tsv}\n'
                 f'Channels marked as bad in '
                 f'raw.info["bads"]: {raw.info["bads"]}\n'
                 f'Setting list of bad channels to: {bads_from_tsv}')

        raw.info['bads'] = bads_from_tsv
    elif raw.info['bads']:
        # We do have info['bads'], but no `status` in channels.tsv
        logger.info(f'No "status" column found in '
                    f'{op.basename(channels_fname)}; using list of bad '
                    f'channels found in raw.info["bads"]: {raw.info["bads"]}')

    return raw