Exemplo n.º 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
def run_maxwell_filter(*, cfg, subject, session=None):
    if cfg.proc and 'sss' in cfg.proc and cfg.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_out = BIDSPath(subject=subject,
                             session=session,
                             task=cfg.task,
                             acquisition=cfg.acq,
                             processing='sss',
                             recording=cfg.rec,
                             space=cfg.space,
                             suffix='raw',
                             extension='.fif',
                             datatype=cfg.datatype,
                             root=cfg.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.
    msg = f'Loading reference run: {cfg.mf_reference_run}.'
    logger.info(**gen_log_kwargs(message=msg, subject=subject,
                                 session=session))

    reference_run_info = get_reference_run_info(
        subject=subject, session=session, run=cfg.mf_reference_run
    )
    dev_head_t = reference_run_info['dev_head_t']
    dig = reference_run_info['dig']
    del reference_run_info

    for run in cfg.runs:
        bids_path_out.update(run=run)

        raw = import_experimental_data(
            cfg=cfg,
            subject=subject,
            session=session,
            run=run,
            save=False
        )

        # Maxwell-filter experimental data.
        msg = 'Applying Maxwell filter to experimental data.'
        logger.info(**gen_log_kwargs(message=msg, subject=subject,
                                     session=session, run=run))

        # Warn if no bad channels are set before Maxwell filter
        # Create a copy, we'll need this later for setting the bads of the
        # empty-room recording
        bads = raw.info['bads'].copy()
        if not bads:
            msg = 'Found no bad channels.'
            logger.warning(**gen_log_kwargs(message=msg, subject=subject,
                                            session=session, run=run))

        if cfg.mf_st_duration:
            msg = '    st_duration=%d' % (cfg.mf_st_duration)
            logger.info(**gen_log_kwargs(message=msg,
                                         subject=subject, session=session,
                                         run=run))

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

        raw_sss = mne.preprocessing.maxwell_filter(raw, **common_mf_kws)

        # Save only the channel types we wish to analyze (including the
        # channels marked as "bad").
        # We do not run `raw_sss.pick()` here because it uses too much memory.
        picks = config.get_channels_to_analyze(raw.info)
        raw_sss.save(bids_path_out, picks=picks, split_naming='bids',
                     overwrite=True)
        del raw_sss

        if cfg.interactive:
            # Load the data we have just written, because it contains only
            # the relevant channels.
            raw = mne.io.read_raw_fif(bids_path_out, allow_maxshield=True)
            raw.plot(n_channels=50, butterfly=True)

        # Empty-room processing.
        # Only process empty-room data once – we ensure this by simply checking
        # if the current run is the reference run, and only then initiate
        # empty-room processing. No sophisticated logic behind this – it's just
        # convenient to code it this way.
        if cfg.process_er and run == cfg.mf_reference_run:
            msg = 'Processing empty-room recording …'
            logger.info(**gen_log_kwargs(subject=subject,
                                         session=session, message=msg))

            raw_er = import_er_data(
                cfg=cfg,
                subject=subject,
                session=session,
                bads=bads,
                save=False
            )

            # Maxwell-filter empty-room data.
            msg = 'Applying Maxwell filter to empty-room recording'
            logger.info(**gen_log_kwargs(message=msg,
                                         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.
            raw_sss = mne.io.read_raw_fif(bids_path_out)
            rank_exp = mne.compute_rank(raw_sss, rank='info')['meg']
            rank_er = mne.compute_rank(raw_er_sss, 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  differently.')
                raise RuntimeError(msg)

            raw_er_fname_out = bids_path_out.copy().update(
                task='noise',
                run=None,
                processing='sss'
            )

            # Save only the channel types we wish to analyze
            # (same as for experimental data above).
            raw_er_sss.save(raw_er_fname_out, picks=picks,
                            overwrite=True, split_naming='bids')
            del raw_er_sss