Spaces:
Running
Running
| # Authors: The MNE-Python contributors. | |
| # License: BSD-3-Clause | |
| # Copyright the MNE-Python contributors. | |
| import math | |
| import numpy as np | |
| from scipy.signal import lfilter | |
| from .._fiff.pick import pick_info | |
| from ..cov import Covariance, compute_whitener | |
| from ..epochs import BaseEpochs | |
| from ..evoked import Evoked | |
| from ..forward import apply_forward | |
| from ..io import BaseRaw | |
| from ..utils import _check_preload, _validate_type, check_random_state, logger, verbose | |
| def simulate_evoked( | |
| fwd, | |
| stc, | |
| info, | |
| cov=None, | |
| nave=30, | |
| iir_filter=None, | |
| random_state=None, | |
| use_cps=True, | |
| verbose=None, | |
| ): | |
| """Generate noisy evoked data. | |
| .. note:: No projections from ``info`` will be present in the | |
| output ``evoked``. You can use e.g. | |
| :func:`evoked.add_proj <mne.Evoked.add_proj>` or | |
| :func:`evoked.set_eeg_reference <mne.Evoked.set_eeg_reference>` | |
| to add them afterward as necessary. | |
| Parameters | |
| ---------- | |
| fwd : instance of Forward | |
| A forward solution. | |
| stc : SourceEstimate object | |
| The source time courses. | |
| %(info_not_none)s Used to generate the evoked. | |
| cov : Covariance object | None | |
| The noise covariance. If None, no noise is added. | |
| nave : int | |
| Number of averaged epochs (defaults to 30). | |
| .. versionadded:: 0.15.0 | |
| iir_filter : None | array | |
| IIR filter coefficients (denominator) e.g. [1, -1, 0.2]. | |
| %(random_state)s | |
| %(use_cps)s | |
| .. versionadded:: 0.15 | |
| %(verbose)s | |
| Returns | |
| ------- | |
| evoked : Evoked object | |
| The simulated evoked data. | |
| See Also | |
| -------- | |
| simulate_raw | |
| simulate_stc | |
| simulate_sparse_stc | |
| Notes | |
| ----- | |
| To make the equivalence between snr and nave, when the snr is given | |
| instead of nave:: | |
| nave = (1 / 10 ** ((actual_snr - snr)) / 20) ** 2 | |
| where actual_snr is the snr to the generated noise before scaling. | |
| .. versionadded:: 0.10.0 | |
| """ | |
| evoked = apply_forward(fwd, stc, info, use_cps=use_cps) | |
| if cov is None: | |
| return evoked | |
| if nave < np.inf: | |
| noise = _simulate_noise_evoked(evoked, cov, iir_filter, random_state) | |
| evoked.data += noise.data / math.sqrt(nave) | |
| evoked.nave = np.int64(nave) | |
| if cov.get("projs", None): | |
| evoked.add_proj(cov["projs"]).apply_proj() | |
| return evoked | |
| def _simulate_noise_evoked(evoked, cov, iir_filter, random_state): | |
| noise = evoked.copy() | |
| noise.data[:] = 0 | |
| return _add_noise(noise, cov, iir_filter, random_state, allow_subselection=False) | |
| def add_noise(inst, cov, iir_filter=None, random_state=None, verbose=None): | |
| """Create noise as a multivariate Gaussian. | |
| The spatial covariance of the noise is given from the cov matrix. | |
| Parameters | |
| ---------- | |
| inst : instance of Evoked, Epochs, or Raw | |
| Instance to which to add noise. | |
| cov : instance of Covariance | |
| The noise covariance. | |
| iir_filter : None | array-like | |
| IIR filter coefficients (denominator). | |
| %(random_state)s | |
| %(verbose)s | |
| Returns | |
| ------- | |
| inst : instance of Evoked, Epochs, or Raw | |
| The instance, modified to have additional noise. | |
| Notes | |
| ----- | |
| Only channels in both ``inst.info['ch_names']`` and | |
| ``cov['names']`` will have noise added to them. | |
| This function operates inplace on ``inst``. | |
| .. versionadded:: 0.18.0 | |
| """ | |
| # We always allow subselection here | |
| return _add_noise(inst, cov, iir_filter, random_state) | |
| def _add_noise(inst, cov, iir_filter, random_state, allow_subselection=True): | |
| """Add noise, possibly with channel subselection.""" | |
| _validate_type(cov, Covariance, "cov") | |
| _validate_type( | |
| inst, (BaseRaw, BaseEpochs, Evoked), "inst", "Raw, Epochs, or Evoked" | |
| ) | |
| _check_preload(inst, "Adding noise") | |
| data = inst._data | |
| assert data.ndim in (2, 3) | |
| if data.ndim == 2: | |
| data = data[np.newaxis] | |
| # Subselect if necessary | |
| info = inst.info | |
| info._check_consistency() | |
| picks = gen_picks = slice(None) | |
| if allow_subselection: | |
| use_chs = list(set(info["ch_names"]) & set(cov["names"])) | |
| picks = np.where(np.isin(info["ch_names"], use_chs))[0] | |
| logger.info( | |
| "Adding noise to %d/%d channels (%d channels in cov)", | |
| len(picks), | |
| len(info["chs"]), | |
| len(cov["names"]), | |
| ) | |
| info = pick_info(inst.info, picks) | |
| info._check_consistency() | |
| gen_picks = np.arange(info["nchan"]) | |
| for epoch in data: | |
| epoch[picks] += _generate_noise( | |
| info, cov, iir_filter, random_state, epoch.shape[1], picks=gen_picks | |
| )[0] | |
| return inst | |
| def _generate_noise( | |
| info, cov, iir_filter, random_state, n_samples, zi=None, picks=None | |
| ): | |
| """Create spatially colored and temporally IIR-filtered noise.""" | |
| rng = check_random_state(random_state) | |
| _, _, colorer = compute_whitener( | |
| cov, info, pca=True, return_colorer=True, picks=picks, verbose=False | |
| ) | |
| noise = np.dot(colorer, rng.standard_normal((colorer.shape[1], n_samples))) | |
| if iir_filter is not None: | |
| if zi is None: | |
| zi = np.zeros((len(colorer), len(iir_filter) - 1)) | |
| noise, zf = lfilter([1], iir_filter, noise, axis=-1, zi=zi) | |
| else: | |
| zf = None | |
| return noise, zf | |