Source code for meeg_utils.preprocessing.bad_channels

"""Bad channel detection for EEG and MEG data."""

from __future__ import annotations

from loguru import logger
from mne.io import BaseRaw
from mne.preprocessing import find_bad_channels_maxwell
from pyprep.find_noisy_channels import NoisyChannels


[docs] def detect_bad_channels_eeg( raw: BaseRaw, random_state: int = 42, ) -> list[str]: """Detect bad EEG channels using PREP pipeline. Parameters ---------- raw : BaseRaw Raw EEG data. random_state : int, optional Random seed for reproducibility. Default is 42. Returns ------- list[str] List of bad channel names. """ logger.trace("Detecting bad EEG channels using PREP...") # Pick only EEG channels raw_eeg = raw.copy().pick("eeg") # Use PREP NoisyChannels finder try: finder = NoisyChannels(raw_eeg, random_state=random_state) # Run detection methods with error handling try: finder.find_bad_by_correlation() except Exception as e: logger.warning(f"Correlation-based detection failed: {e}") try: finder.find_bad_by_deviation() except Exception as e: logger.warning(f"Deviation-based detection failed: {e}") try: finder.find_bad_by_ransac() except Exception as e: logger.warning(f"RANSAC-based detection failed: {e}") bad_channels = list(finder.get_bads()) except Exception as e: logger.error(f"PREP pipeline failed: {e}. Returning empty bad channels list.") bad_channels = [] logger.trace(f"Found {len(bad_channels)} bad EEG channels: {bad_channels}") return bad_channels
[docs] def detect_bad_channels_meg( raw: BaseRaw, origin: tuple[float, float, float] = (0.0, 0.0, 0.04), ) -> list[str]: """Detect bad MEG channels using Maxwell filtering. Parameters ---------- raw : BaseRaw Raw MEG data. origin : tuple, optional Origin for Maxwell filtering. Default is (0.0, 0.0, 0.04). Returns ------- list[str] List of bad channel names. """ logger.trace("Detecting bad MEG channels using Maxwell filtering...") # Copy raw for detection raw_copy = raw.copy() # Check if required info is available if raw_copy.info.get("dev_head_t") is None: logger.warning( "Cannot run Maxwell filtering: dev_head_t is None. Returning empty bad channels list." ) return [] # CTF data may need compensation adjustment if hasattr(raw_copy, "compensation_grade") and raw_copy.compensation_grade != 0: logger.trace( f"CTF data has compensation grade {raw_copy.compensation_grade}, " "applying 0-compensation for bad channel detection." ) try: raw_copy.apply_gradient_compensation(0) except Exception as e: logger.warning(f"Failed to apply gradient compensation: {e}") # Pick MEG channels try: raw_copy.pick(["mag", "grad", "planar1", "planar2"], exclude=[]) except Exception as e: logger.warning(f"Failed to pick MEG channels: {e}") return [] # Find bad channels try: auto_noisy, auto_flat, _ = find_bad_channels_maxwell( raw=raw_copy, return_scores=True, origin=origin, cross_talk=None, calibration=None, verbose=False, ) bad_channels = list(set(auto_noisy + auto_flat)) logger.trace(f"Found {len(bad_channels)} bad MEG channels: {bad_channels}") except Exception as e: logger.error(f"Maxwell filtering failed: {e}. Returning empty bad channels list.") bad_channels = [] return bad_channels