Source code for meeg_utils.preprocessing.line_noise

"""Line noise removal using zapline methods."""

from __future__ import annotations

import contextlib
import io

import mne
import numpy as np
from loguru import logger
from meegkit import dss
from mne.io import BaseRaw


[docs] def remove_line_noise_eeg( raw: BaseRaw, fline: float = 50.0, ) -> BaseRaw: """Remove line noise from EEG data using zapline iterative method. Parameters ---------- raw : BaseRaw Raw EEG data. fline : float, optional Line noise frequency in Hz. Default is 50.0. Returns ------- BaseRaw Raw data with line noise removed. """ logger.trace(f"Removing {fline} Hz line noise from EEG using zapline_iter...") # Pick EEG channels raw_eeg = raw.copy().pick("eeg") # Check if we have enough data if len(raw_eeg.ch_names) < 2: logger.warning("Not enough EEG channels for line noise removal. Skipping.") return raw_eeg # Prepare data for zapline data = raw_eeg.get_data().T # (n_samples, n_channels) data = np.expand_dims(data, axis=2) # (n_samples, n_channels, n_trials=1) sfreq = raw_eeg.info["sfreq"] # Apply zapline iterative try: with contextlib.redirect_stdout(io.StringIO()): # Suppress meegkit output cleaned_data, _ = dss.dss_line_iter(data, fline, sfreq=sfreq, nfft=400) # Reconstruct raw cleaned_data = cleaned_data.T.squeeze() # Back to (n_channels, n_samples) cleaned_raw = mne.io.RawArray(cleaned_data, raw_eeg.info) cleaned_raw.set_annotations(raw_eeg.annotations) logger.trace("Line noise removal completed for EEG.") return cleaned_raw except Exception as e: logger.error(f"Zapline failed: {e}. Returning original data.") return raw_eeg
[docs] def remove_line_noise_meg( raw: BaseRaw, fline: float = 50.0, removing_ratio: float = 0.22, ) -> BaseRaw: """Remove line noise from MEG data using zapline method. Parameters ---------- raw : BaseRaw Raw MEG data. fline : float, optional Line noise frequency in Hz. Default is 50.0. removing_ratio : float, optional Ratio of components to remove. Default is 0.22. Returns ------- BaseRaw Raw data with line noise removed. """ logger.trace(f"Removing {fline} Hz line noise from MEG using zapline...") # Pick MEG channels try: raw_meg = raw.copy().pick(["mag", "grad", "planar1", "planar2"], exclude=[]) except Exception as e: logger.warning(f"Failed to pick MEG channels: {e}. Returning original data.") return raw.copy() # Check if we have enough channels if len(raw_meg.ch_names) < 2: logger.warning("Not enough MEG channels for line noise removal. Skipping.") return raw_meg # Prepare data data = raw_meg.get_data().T data = np.expand_dims(data, axis=2) sfreq = raw_meg.info["sfreq"] nremove = max(1, int(len(raw_meg.ch_names) * removing_ratio)) # Apply zapline try: with contextlib.redirect_stdout(io.StringIO()): cleaned_data, _ = dss.dss_line( data, fline=fline, sfreq=sfreq, nremove=nremove, blocksize=1000, show=False, ) # Reconstruct raw cleaned_data = cleaned_data.T.squeeze() cleaned_raw = mne.io.RawArray(cleaned_data, raw_meg.info) cleaned_raw.set_annotations(raw_meg.annotations) logger.trace("Line noise removal completed for MEG.") return cleaned_raw except Exception as e: logger.error(f"Zapline failed: {e}. Returning original data.") return raw_meg