def assess_bads(raw_fname, is_eroom=False): '''Code sampled from MNE python website https://mne.tools/dev/auto_tutorials/preprocessing/\ plot_60_maxwell_filtering_sss.html''' from mne.preprocessing import find_bad_channels_maxwell raw = mne.io.read_raw_fif(raw_fname) if raw.times[-1] > 60.0: raw.crop(tmax=60) raw.info['bads'] = [] raw_check = raw.copy() if is_eroom == False: auto_noisy_chs, auto_flat_chs, auto_scores = find_bad_channels_maxwell( raw_check, cross_talk=None, calibration=None, return_scores=True, verbose=True) else: auto_noisy_chs, auto_flat_chs, auto_scores = find_bad_channels_maxwell( raw_check, cross_talk=None, calibration=None, return_scores=True, verbose=True, coord_frame="meg") return {'noisy': auto_noisy_chs, 'flat': auto_flat_chs}
def find_bad_channels_maxwell_util(raw_): """ Inplace update of bad channels. Find noisy and flat channels according to the Maxwell filter. """ raw_.info['bads'] = [] raw_check = raw_.copy() auto_noisy_chs, auto_flat_chs, auto_scores = find_bad_channels_maxwell( raw_check, # cross_talk=crosstalk_file, calibration=fine_cal_file, return_scores=True, verbose=True, coord_frame="meg") print("auto_noisy_chs", auto_noisy_chs) print("auto_flat_chs", auto_flat_chs) bads = raw_.info['bads'] + auto_noisy_chs + auto_flat_chs raw_.info['bads'] = bads raw_.auto_scores = auto_scores
def find_bad_channels(raw, subject, session, task, run): if (config.find_flat_channels_meg and not config.find_noisy_channels_meg): msg = 'Finding flat channels.' elif (config.find_noisy_channels_meg and not config.find_flat_channels_meg): msg = 'Finding noisy channels using Maxwell filtering.' else: msg = ('Finding flat channels, and noisy channels using ' 'Maxwell filtering.') logger.info( gen_log_message(message=msg, step=1, subject=subject, session=session)) bids_path = BIDSPath(subject=subject, session=session, task=task, run=run, acquisition=config.acq, processing=config.proc, recording=config.rec, space=config.space, suffix=config.get_datatype(), datatype=config.get_datatype(), root=config.deriv_root) auto_noisy_chs, auto_flat_chs, auto_scores = find_bad_channels_maxwell( raw=raw, calibration=get_mf_cal_fname(subject, session), cross_talk=get_mf_ctc_fname(subject, session), return_scores=True) preexisting_bads = raw.info['bads'].copy() bads = preexisting_bads.copy() if config.find_flat_channels_meg: msg = f'Found {len(auto_flat_chs)} flat channels.' logger.info( gen_log_message(message=msg, step=1, subject=subject, session=session)) bads.extend(auto_flat_chs) if config.find_noisy_channels_meg: msg = f'Found {len(auto_noisy_chs)} noisy channels.' logger.info( gen_log_message(message=msg, step=1, subject=subject, session=session)) bads.extend(auto_noisy_chs) bads = sorted(set(bads)) raw.info['bads'] = bads msg = f'Marked {len(raw.info["bads"])} channels as bad.' logger.info( gen_log_message(message=msg, step=1, subject=subject, session=session)) if config.find_noisy_channels_meg: auto_scores_fname = bids_path.copy().update(suffix='scores', extension='.json', check=False) with open(auto_scores_fname, 'w') as f: json_tricks.dump(auto_scores, fp=f, allow_nan=True, sort_keys=False) if config.interactive: import matplotlib.pyplot as plt config.plot_auto_scores(auto_scores) plt.show() # Write the bad channels to disk. bads_tsv_fname = bids_path.copy().update(suffix='bads', extension='.tsv', check=False) bads_for_tsv = [] reasons = [] if config.find_flat_channels_meg: bads_for_tsv.extend(auto_flat_chs) reasons.extend(['auto-flat'] * len(auto_flat_chs)) preexisting_bads = set(preexisting_bads) - set(auto_flat_chs) if config.find_noisy_channels_meg: bads_for_tsv.extend(auto_noisy_chs) reasons.extend(['auto-noisy'] * len(auto_noisy_chs)) preexisting_bads = set(preexisting_bads) - set(auto_noisy_chs) preexisting_bads = list(preexisting_bads) if preexisting_bads: bads_for_tsv.extend(preexisting_bads) reasons.extend(['pre-existing (before mne-study-template was run)'] * len(preexisting_bads)) tsv_data = pd.DataFrame(dict(name=bads_for_tsv, reason=reasons)) tsv_data = tsv_data.sort_values(by='name') tsv_data.to_csv(bads_tsv_fname, sep='\t', index=False)
raw_LD.info['bads'] = [] # For closer equivalence with MaxFilter, it's recommended to low-pass # filter your data prior to running this function raw_check_f = raw_fruit.copy().pick_types(exclude=()).load_data().filter( None, 48) raw_check_o = raw_odour.copy().pick_types(exclude=()).load_data().filter( None, 48) raw_check_m = raw_milk.copy().pick_types(exclude=()).load_data().filter( None, 48) raw_check_l = raw_LD.copy().pick_types(exclude=()).load_data().filter( None, 48) # Find MEG bad channels using Maxwell filtering auto_noisy_chs_f, auto_flat_chs_f = find_bad_channels_maxwell( raw_check_f, cross_talk=cross_talk, calibration=calibration, verbose=True) auto_noisy_chs_o, auto_flat_chs_o = find_bad_channels_maxwell( raw_check_o, cross_talk=cross_talk, calibration=calibration, verbose=True) auto_noisy_chs_m, auto_flat_chs_m = find_bad_channels_maxwell( raw_check_m, cross_talk=cross_talk, calibration=calibration, verbose=True) auto_noisy_chs_l, auto_flat_chs_l = find_bad_channels_maxwell( raw_check_l, cross_talk=cross_talk,
def find_bad_channels(raw, subject, session, task, run): if (config.find_flat_channels_meg and not config.find_noisy_channels_meg): msg = 'Finding flat channels.' elif (config.find_noisy_channels_meg and not config.find_flat_channels_meg): msg = 'Finding noisy channels using Maxwell filtering.' else: msg = ('Finding flat channels, and noisy channels using ' 'Maxwell filtering.') logger.info( gen_log_message(message=msg, step=1, subject=subject, session=session)) raw_lp_filtered_for_maxwell = (raw.copy().filter(l_freq=None, h_freq=40)) auto_noisy_chs, auto_flat_chs = find_bad_channels_maxwell( raw=raw_lp_filtered_for_maxwell, calibration=config.mf_cal_fname, cross_talk=config.mf_ctc_fname) del raw_lp_filtered_for_maxwell preexisting_bads = raw.info['bads'].copy() bads = preexisting_bads.copy() if config.find_flat_channels_meg: msg = f'Found {len(auto_flat_chs)} flat channels.' logger.info( gen_log_message(message=msg, step=1, subject=subject, session=session)) bads.extend(auto_flat_chs) if config.find_noisy_channels_meg: msg = f'Found {len(auto_noisy_chs)} noisy channels.' logger.info( gen_log_message(message=msg, step=1, subject=subject, session=session)) bads.extend(auto_noisy_chs) bads = sorted(set(bads)) raw.info['bads'] = bads msg = f'Marked {len(raw.info["bads"])} channels as bad.' logger.info( gen_log_message(message=msg, step=1, subject=subject, session=session)) # Write the bad channels to disk. deriv_path = config.get_subject_deriv_path(subject=subject, session=session, kind=config.get_kind()) bids_basename = make_bids_basename(subject=subject, session=session, task=config.get_task(), acquisition=config.acq, run=run, processing=config.proc, recording=config.rec, space=config.space) bads_tsv_fname = op.join(deriv_path, f'{bids_basename}_bad_channels.tsv') bads_for_tsv = [] reasons = [] if config.find_flat_channels_meg: bads_for_tsv.extend(auto_flat_chs) reasons.extend(['auto-flat'] * len(auto_flat_chs)) preexisting_bads = set(preexisting_bads) - set(auto_flat_chs) if config.find_noisy_channels_meg: bads_for_tsv.extend(auto_noisy_chs) reasons.extend(['auto-noisy'] * len(auto_noisy_chs)) preexisting_bads = set(preexisting_bads) - set(auto_noisy_chs) preexisting_bads = list(preexisting_bads) if preexisting_bads: bads_for_tsv.extend(preexisting_bads) reasons.extend(['pre-existing (before mne-study-template was run)'] * len(preexisting_bads)) tsv_data = pd.DataFrame(dict(name=bads_for_tsv, reason=reasons)) tsv_data = tsv_data.sort_values(by='name') tsv_data.to_csv(bads_tsv_fname, sep='\t', index=False)
# Before we perform SSS we'll look for bad channels — ``MEG 2443`` is quite # noisy. # # .. warning:: # # It is critical to mark bad channels in ``raw.info['bads']`` *before* # calling :func:`~mne.preprocessing.maxwell_filter` in order to prevent # bad channel noise from spreading. # # Let's see if we can automatically detect it. raw.info['bads'] = [] raw_check = raw.copy() auto_noisy_chs, auto_flat_chs, auto_scores = find_bad_channels_maxwell( raw_check, cross_talk=crosstalk_file, calibration=fine_cal_file, return_scores=True, verbose=True) print(auto_noisy_chs) # we should find them! print(auto_flat_chs) # none for this dataset ############################################################################### # # .. note:: `~mne.preprocessing.find_bad_channels_maxwell` needs to operate on # a signal without line noise or cHPI signals. By default, it simply # applies a low-pass filter with a cutoff frequency of 40 Hz to the # data, which should remove these artifacts. You may also specify a # different cutoff by passing the ``h_freq`` keyword argument. If you # set ``h_freq=None``, no filtering will be applied. This can be # useful if your data has already been preconditioned, for example # using :func:`mne.chpi.filter_chpi`,
def maxwellfilter( raw, crosstalk_file, fine_cal_file, subject, headpos_file=None, compute_motion_params=True, figdir="/tmp/", outdir="/tmp/", filtering=False, filter_args=None, ): """ :param raw: Raw data to apply SSS on :param crosstalk_file: crosstalk compensation file from the Elekta system to reduce interference between gradiometers and magnetometers :param fine_cal_file: site-specific sensor orientation and calibration :param subject: str, subject identifier, takes the form '001' :param headpos_file: str, path; If existing, read in head positions from a file :param compute_motion_params: Boolean, whether to perform motion correction :param figdir: str, path to directory to save figures in :param outdir: str, path to save bids compliant sss-corrected data in (derivatives directory) :param filtering: if True, a filter function is ran on the data after SSS. By default, it is a 40Hz low-pass filter. :param filter_args: dict; if filtering is True, initializes a filter with the arguments provided :return: """ from mne.preprocessing import find_bad_channels_maxwell if not compute_motion_params: if not headpos_file or not os.path.exists(headpos_file): logging.info( f"Could not find or read head position files under the supplied" f"path: {headpos_file}. Recalculating from scratch.") head_pos = motion_estimation(subject, raw, figdir) else: logging.info( f"Reading in head positions for subject sub-{subject} " f"from {headpos_file}.") head_pos = mne.chpi.read_head_pos(headpos_file) else: logging.info(f"Starting motion estimation for subject sub-{subject}.") head_pos = motion_estimation(subject, raw, figdir) raw.info["bads"] = [] raw_check = raw.copy() preconditioned = False # TODO handle this here atm. Needs to become global. if preconditioned: # preconditioned is a global variable that is set to True if some form # of filtering (CHPI and line noise removal or general filtering) has # been applied. # the data has been filtered, and we can pass h_freq=None logging.info("Performing bad channel detection without filtering") auto_noisy_chs, auto_flat_chs, auto_scores = find_bad_channels_maxwell( raw_check, cross_talk=crosstalk_file, calibration=fine_cal_file, return_scores=True, verbose=True, h_freq=None, ) else: # the data still contains line noise (50Hz) and CHPI coils. It will # filter the data before extracting bad channels auto_noisy_chs, auto_flat_chs, auto_scores = find_bad_channels_maxwell( raw_check, cross_talk=crosstalk_file, calibration=fine_cal_file, return_scores=True, verbose=True, ) logging.info(f"Found the following noisy channels: {auto_noisy_chs} \n " f"and the following flat channels: {auto_flat_chs} \n" f"for subject sub-{subject}") bads = raw.info["bads"] + auto_noisy_chs + auto_flat_chs raw.info["bads"] = bads # free up space del raw_check # plot as a sanity check for ch_type in ["grad", "mag"]: plot_noisy_channel_detection(auto_scores, ch_type=ch_type, subject=subject, outpath=figdir) logging.info(f"Signal Space Separation with movement compensation " f"starting for subject sub-{subject}") raw_sss = mne.preprocessing.maxwell_filter( raw, cross_talk=crosstalk_file, calibration=fine_cal_file, head_pos=head_pos, verbose=True, ) if filtering: logging.info( f"Filtering raw SSS data for subject {subject}. The following " f"additional parameters were passed: {filter_args}") raw_sss_filtered = raw_sss.copy() raw_sss_filtered = _filter_data(raw_sss_filtered, **filter_args) # TODO: Downsample plot_psd(raw_sss_filtered, subject, figdir, filtering) # TODO: save file return raw_sss_filtered plot_psd(raw_sss, subject, figdir, filtering) return raw_sss