예제 #1
0
def run_maxwell_filter(subject, session=None):
    if config.proc and 'sss' in config.proc and config.use_maxwell_filter:
        raise ValueError(f'You cannot set use_maxwell_filter to True '
                         f'if data have already processed with Maxwell-filter.'
                         f' Got proc={config.proc}.')

    bids_path_in = BIDSPath(subject=subject,
                            session=session,
                            task=config.get_task(),
                            acquisition=config.acq,
                            processing=config.proc,
                            recording=config.rec,
                            space=config.space,
                            suffix=config.get_datatype(),
                            datatype=config.get_datatype(),
                            root=config.bids_root)
    bids_path_out = bids_path_in.copy().update(suffix='raw',
                                               root=config.deriv_root,
                                               check=False)

    # Load dev_head_t and digitization points from MaxFilter reference run.
    # Re-use in all runs and for processing empty-room recording.
    if config.use_maxwell_filter:
        reference_run = config.get_mf_reference_run()
        msg = f'Loading reference run: {reference_run}.'
        logger.info(
            gen_log_message(message=msg,
                            step=1,
                            subject=subject,
                            session=session))
        bids_path_in.update(run=reference_run)
        info = mne.io.read_info(bids_path_in.fpath)
        dev_head_t = info['dev_head_t']
        dig = info['dig']
        del reference_run, info

    for run_idx, run in enumerate(config.get_runs()):
        bids_path_in.update(run=run)
        bids_path_out.update(run=run)
        raw = load_data(bids_path_in)

        # Fix stimulation artifact
        if config.fix_stim_artifact:
            events, _ = mne.events_from_annotations(raw)
            raw = mne.preprocessing.fix_stim_artifact(
                raw,
                events=events,
                event_id=None,
                tmin=config.stim_artifact_tmin,
                tmax=config.stim_artifact_tmax,
                mode='linear')

        # Auto-detect bad channels.
        if config.find_flat_channels_meg or config.find_noisy_channels_meg:
            find_bad_channels(raw=raw,
                              subject=subject,
                              session=session,
                              task=config.get_task(),
                              run=run)

        # Maxwell-filter experimental data.
        if config.use_maxwell_filter:
            msg = 'Applying Maxwell filter to experimental data.'
            logger.info(
                gen_log_message(message=msg,
                                step=1,
                                subject=subject,
                                session=session))

            # Warn if no bad channels are set before Maxwell filter
            if not raw.info['bads']:
                msg = '\nFound no bad channels. \n '
                logger.warning(
                    gen_log_message(message=msg,
                                    subject=subject,
                                    step=1,
                                    session=session))

            if config.mf_st_duration:
                msg = '    st_duration=%d' % (config.mf_st_duration)
                logger.info(
                    gen_log_message(message=msg,
                                    step=1,
                                    subject=subject,
                                    session=session))

            # Keyword arguments shared between Maxwell filtering of the
            # experimental and the empty-room data.
            common_mf_kws = dict(calibration=get_mf_cal_fname(
                subject, session),
                                 cross_talk=get_mf_ctc_fname(subject, session),
                                 st_duration=config.mf_st_duration,
                                 origin=config.mf_head_origin,
                                 coord_frame='head',
                                 destination=dev_head_t)

            raw_sss = mne.preprocessing.maxwell_filter(raw, **common_mf_kws)
            raw_out = raw_sss
            raw_fname_out = (bids_path_out.copy().update(processing='sss',
                                                         extension='.fif'))
        elif config.ch_types == ['eeg']:
            msg = 'Not applying Maxwell filter to EEG data.'
            logger.info(
                gen_log_message(message=msg,
                                step=1,
                                subject=subject,
                                session=session))
            raw_out = raw
            raw_fname_out = bids_path_out.copy().update(extension='.fif')
        else:
            msg = ('Not applying Maxwell filter.\nIf you wish to apply it, '
                   'set use_maxwell_filter=True in your configuration.')
            logger.info(
                gen_log_message(message=msg,
                                step=1,
                                subject=subject,
                                session=session))
            raw_out = raw
            raw_fname_out = bids_path_out.copy().update(extension='.fif')

        # Save only the channel types we wish to analyze (including the
        # channels marked as "bad").
        # We do not run `raw_out.pick()` here because it uses too much memory.
        chs_to_include = config.get_channels_to_analyze(raw_out.info)
        raw_out.save(raw_fname_out,
                     picks=chs_to_include,
                     overwrite=True,
                     split_naming='bids')
        del raw_out
        if config.interactive:
            # Load the data we have just written, because it contains only
            # the relevant channels.
            raw = mne.io.read_raw_fif(raw_fname_out, allow_maxshield=True)
            raw.plot(n_channels=50, butterfly=True)

        # Empty-room processing.
        #
        # We pick the empty-room recording closest in time to the first run
        # of the experimental session.
        if run_idx == 0 and config.process_er:
            msg = 'Processing empty-room recording …'
            logger.info(
                gen_log_message(step=1,
                                subject=subject,
                                session=session,
                                message=msg))

            bids_path_er_in = bids_path_in.find_empty_room()
            raw_er = load_data(bids_path_er_in)
            raw_er.info['bads'] = [
                ch for ch in raw.info['bads'] if ch.startswith('MEG')
            ]

            # Maxwell-filter empty-room data.
            if config.use_maxwell_filter:
                msg = 'Applying Maxwell filter to empty-room recording'
                logger.info(
                    gen_log_message(message=msg,
                                    step=1,
                                    subject=subject,
                                    session=session))

                # We want to ensure we use the same coordinate frame origin in
                # empty-room and experimental data processing. To do this, we
                # inject the sensor locations and the head <> device transform
                # into the empty-room recording's info, and leave all other
                # parameters the same as for the experimental data. This is not
                # very clean, as we normally should not alter info manually,
                # except for info['bads']. Will need improvement upstream in
                # MNE-Python.
                raw_er.info['dig'] = dig
                raw_er.info['dev_head_t'] = dev_head_t
                raw_er_sss = mne.preprocessing.maxwell_filter(
                    raw_er, **common_mf_kws)

                # Perform a sanity check: empty-room rank should match the
                # experimental data rank after Maxwell filtering.
                rank_exp = mne.compute_rank(raw, rank='info')['meg']
                rank_er = mne.compute_rank(raw_er, rank='info')['meg']
                if not np.isclose(rank_exp, rank_er):
                    msg = (f'Experimental data rank {rank_exp:.1f} does not '
                           f'match empty-room data rank {rank_er:.1f} after '
                           f'Maxwell filtering. This indicates that the data '
                           f'were processed  differenlty.')
                    raise RuntimeError(msg)

                raw_er_out = raw_er_sss
                raw_er_fname_out = bids_path_out.copy().update(
                    processing='sss')
            else:
                raw_er_out = raw_er
                raw_er_fname_out = bids_path_out.copy()

            raw_er_fname_out = raw_er_fname_out.update(task='noise',
                                                       extension='.fif',
                                                       run=None)

            # Save only the channel types we wish to analyze
            # (same as for experimental data above).
            raw_er_out.save(raw_er_fname_out,
                            picks=chs_to_include,
                            overwrite=True,
                            split_naming='bids')
            del raw_er_out
예제 #2
0
def test_find_empty_room(return_bids_test_dir):
    """Test reading of empty room data."""
    data_path = testing.data_path()
    raw_fname = op.join(data_path, 'MEG', 'sample',
                        'sample_audvis_trunc_raw.fif')
    bids_root = _TempDir()
    tmp_dir = _TempDir()

    raw = _read_raw_fif(raw_fname)
    bids_path = BIDSPath(subject='01',
                         session='01',
                         task='audiovisual',
                         run='01',
                         root=bids_root,
                         suffix='meg')
    write_raw_bids(raw, bids_path, overwrite=True)

    # No empty-room data present.
    er_basename = bids_path.find_empty_room()
    assert er_basename is None

    # Now create data resembling an empty-room recording.
    # The testing data has no "noise" recording, so save the actual data
    # as named as if it were noise. We first need to write the FIFF file
    # before reading it back in.
    er_raw_fname = op.join(tmp_dir, 'ernoise_raw.fif')
    raw.copy().crop(0, 10).save(er_raw_fname, overwrite=True)
    er_raw = _read_raw_fif(er_raw_fname)

    if not isinstance(er_raw.info['meas_date'], datetime):
        # mne < v0.20
        er_date = datetime.fromtimestamp(er_raw.info['meas_date'][0])
    else:
        er_date = er_raw.info['meas_date']

    er_date = er_date.strftime('%Y%m%d')
    er_bids_path = BIDSPath(subject='emptyroom',
                            task='noise',
                            session=er_date,
                            suffix='meg',
                            root=bids_root)
    write_raw_bids(er_raw, er_bids_path, overwrite=True)

    recovered_er_bids_path = bids_path.find_empty_room()
    assert er_bids_path == recovered_er_bids_path

    # assert that we get best emptyroom if there are multiple available
    sh.rmtree(op.join(bids_root, 'sub-emptyroom'))
    dates = ['20021204', '20021201', '20021001']
    for date in dates:
        er_bids_path.update(session=date)
        er_meas_date = datetime.strptime(date, '%Y%m%d')
        er_meas_date = er_meas_date.replace(tzinfo=timezone.utc)

        if check_version('mne', '0.20'):
            er_raw.set_meas_date(er_meas_date)
        else:
            er_raw.info['meas_date'] = (er_meas_date.timestamp(), 0)
        write_raw_bids(er_raw, er_bids_path)

    best_er_basename = bids_path.find_empty_room()
    assert best_er_basename.session == '20021204'

    with pytest.raises(ValueError,
                       match='The root of the "bids_path" must be set'):
        bids_path.copy().update(root=None).find_empty_room()

    # assert that we get error if meas_date is not available.
    raw = read_raw_bids(bids_path=bids_path)
    if check_version('mne', '0.20'):
        raw.set_meas_date(None)
    else:
        raw.info['meas_date'] = None
        raw.annotations.orig_time = None
    anonymize_info(raw.info)
    write_raw_bids(raw, bids_path, overwrite=True)
    with pytest.raises(ValueError,
                       match='The provided recording does not '
                       'have a measurement date set'):
        bids_path.find_empty_room()
예제 #3
0
def test_find_empty_room(return_bids_test_dir, tmpdir):
    """Test reading of empty room data."""
    data_path = testing.data_path()
    raw_fname = op.join(data_path, 'MEG', 'sample',
                        'sample_audvis_trunc_raw.fif')
    bids_root = tmpdir.mkdir("bids")
    tmp_dir = tmpdir.mkdir("tmp")

    raw = _read_raw_fif(raw_fname)
    bids_path = BIDSPath(subject='01',
                         session='01',
                         task='audiovisual',
                         run='01',
                         root=bids_root,
                         suffix='meg')
    write_raw_bids(raw, bids_path, overwrite=True)

    # No empty-room data present.
    er_basename = bids_path.find_empty_room()
    assert er_basename is None

    # Now create data resembling an empty-room recording.
    # The testing data has no "noise" recording, so save the actual data
    # as named as if it were noise. We first need to write the FIFF file
    # before reading it back in.
    er_raw_fname = op.join(tmp_dir, 'ernoise_raw.fif')
    raw.copy().crop(0, 10).save(er_raw_fname, overwrite=True)
    er_raw = _read_raw_fif(er_raw_fname)

    if not isinstance(er_raw.info['meas_date'], datetime):  # pragma: no cover
        # mne < v0.20
        er_date = datetime.fromtimestamp(er_raw.info['meas_date'][0])
    else:
        er_date = er_raw.info['meas_date']

    er_date = er_date.strftime('%Y%m%d')
    er_bids_path = BIDSPath(subject='emptyroom',
                            task='noise',
                            session=er_date,
                            suffix='meg',
                            root=bids_root)
    write_raw_bids(er_raw, er_bids_path, overwrite=True)

    recovered_er_bids_path = bids_path.find_empty_room()
    assert er_bids_path == recovered_er_bids_path

    # assert that we get best emptyroom if there are multiple available
    sh.rmtree(op.join(bids_root, 'sub-emptyroom'))
    dates = ['20021204', '20021201', '20021001']
    for date in dates:
        er_bids_path.update(session=date)
        er_meas_date = datetime.strptime(date, '%Y%m%d')
        er_meas_date = er_meas_date.replace(tzinfo=timezone.utc)
        er_raw.set_meas_date(er_meas_date)
        write_raw_bids(er_raw, er_bids_path)

    best_er_basename = bids_path.find_empty_room()
    assert best_er_basename.session == '20021204'

    with pytest.raises(ValueError,
                       match='The root of the "bids_path" must be set'):
        bids_path.copy().update(root=None).find_empty_room()

    # assert that we get an error if meas_date is not available.
    raw = read_raw_bids(bids_path=bids_path)
    raw.set_meas_date(None)
    anonymize_info(raw.info)
    write_raw_bids(raw, bids_path, overwrite=True)
    with pytest.raises(ValueError,
                       match='The provided recording does not '
                       'have a measurement date set'):
        bids_path.find_empty_room()

    # test that the `AssociatedEmptyRoom` key in MEG sidecar is respected

    bids_root = tmpdir.mkdir('associated-empty-room')
    raw = _read_raw_fif(raw_fname)
    meas_date = datetime(year=2020, month=1, day=10, tzinfo=timezone.utc)
    er_date = datetime(year=2010, month=1, day=1, tzinfo=timezone.utc)
    raw.set_meas_date(meas_date)

    er_raw_matching_date = er_raw.copy().set_meas_date(meas_date)
    er_raw_associated = er_raw.copy().set_meas_date(er_date)

    # First write empty-room data
    # We write two empty-room recordings: one with a date matching exactly the
    # experimental measurement date, and one dated approx. 10 years earlier
    # We will want to enforce using the older recording via
    # `AssociatedEmptyRoom` (without AssociatedEmptyRoom, find_empty_room()
    # would return the recording with the matching date instead)
    er_matching_date_bids_path = BIDSPath(subject='emptyroom',
                                          session='20200110',
                                          task='noise',
                                          root=bids_root,
                                          datatype='meg',
                                          suffix='meg',
                                          extension='.fif')
    write_raw_bids(er_raw_matching_date, bids_path=er_matching_date_bids_path)

    er_associated_bids_path = (er_matching_date_bids_path.copy().update(
        session='20100101'))
    write_raw_bids(er_raw_associated, bids_path=er_associated_bids_path)

    # Now we write experimental data and associate it with the earlier
    # empty-room recording
    bids_path = (er_matching_date_bids_path.copy().update(subject='01',
                                                          session=None,
                                                          task='task'))
    write_raw_bids(raw,
                   bids_path=bids_path,
                   empty_room=er_associated_bids_path)

    # Retrieve empty-room BIDSPath
    assert bids_path.find_empty_room() == er_associated_bids_path

    # Should only work for MEG
    with pytest.raises(ValueError, match='only supported for MEG'):
        bids_path.copy().update(datatype='eeg').find_empty_room()

    # Don't create `AssociatedEmptyRoom` entry in sidecar – we should now
    # retrieve the empty-room recording closer in time
    write_raw_bids(raw, bids_path=bids_path, empty_room=None, overwrite=True)
    assert bids_path.find_empty_room() == er_matching_date_bids_path
예제 #4
0
###############################################################################
# Just to illustrate, we can save more than one empty room file for different
# dates. Here, they will all contain the same data but in your study, they
# will be different on different days.
dates = ['20021204', '20021201', '20021001']

for date in dates:
    er_bids_path.update(session=date)
    er_meas_date = datetime.strptime(date, '%Y%m%d')
    er_raw.set_meas_date(er_meas_date.replace(tzinfo=timezone.utc))
    write_raw_bids(er_raw, er_bids_path, overwrite=True)

###############################################################################
# Let us look at the directory structure

print_dir_tree(bids_root)

###############################################################################
# To get an accurate estimate of the noise, it is important that the empty
# room recording be as close in date as the raw data.
# We can retrieve the basename corresponding to the empty room
# recording that is closest in time to the experimental measurement.

er_bids_path = bids_path.find_empty_room()
print(er_bids_path)

###############################################################################
# Finally, we can read the empty room file using
raw = read_raw_bids(bids_path=er_bids_path)
print(raw)
예제 #5
0
raw = read_raw_bids(bids_path=bids_path)

# %%
# The resulting data is already in a convenient form to create epochs and
# evoked data.

events, event_id = mne.events_from_annotations(raw)
epochs = mne.Epochs(raw, events, event_id)
epochs['Auditory'].average().plot()

# %%
# We can easily get the :class:`mne_bids.BIDSPath` of the empty-room recording
# that was associated with the experimental data while writing. The empty-room
# data can then be loaded with :func:`read_raw_bids`.

er_bids_path = bids_path.find_empty_room(use_sidecar_only=True)
er_data = read_raw_bids(er_bids_path)
er_data

# %%
# It is trivial to retrieve the path of the fine-calibration and crosstalk
# files, too.

print(bids_path.meg_calibration_fpath)
print(bids_path.meg_crosstalk_fpath)

# %%
# The README created by :func:`write_raw_bids` also takes care of the citation
# for mne-bids. If you are preparing a manuscript, please make sure to also
# cite MNE-BIDS there.
readme = op.join(output_path, 'README')