initial commit
This commit is contained in:
648
mne/beamformer/_dics.py
Normal file
648
mne/beamformer/_dics.py
Normal file
@@ -0,0 +1,648 @@
|
||||
"""Dynamic Imaging of Coherent Sources (DICS)."""
|
||||
|
||||
# Authors: The MNE-Python contributors.
|
||||
# License: BSD-3-Clause
|
||||
# Copyright the MNE-Python contributors.
|
||||
|
||||
import numpy as np
|
||||
|
||||
from .._fiff.pick import pick_channels, pick_info
|
||||
from ..channels import equalize_channels
|
||||
from ..forward import _subject_from_forward
|
||||
from ..minimum_norm.inverse import _check_depth, _check_reference, combine_xyz
|
||||
from ..rank import compute_rank
|
||||
from ..source_estimate import _get_src_type, _make_stc
|
||||
from ..time_frequency import EpochsTFR
|
||||
from ..time_frequency.tfr import _check_tfr_complex
|
||||
from ..utils import (
|
||||
_check_channels_spatial_filter,
|
||||
_check_one_ch_type,
|
||||
_check_option,
|
||||
_check_rank,
|
||||
_validate_type,
|
||||
logger,
|
||||
verbose,
|
||||
warn,
|
||||
)
|
||||
from ._compute_beamformer import (
|
||||
Beamformer,
|
||||
_check_src_type,
|
||||
_compute_beamformer,
|
||||
_compute_power,
|
||||
_prepare_beamformer_input,
|
||||
_proj_whiten_data,
|
||||
)
|
||||
|
||||
|
||||
@verbose
|
||||
def make_dics(
|
||||
info,
|
||||
forward,
|
||||
csd,
|
||||
reg=0.05,
|
||||
noise_csd=None,
|
||||
label=None,
|
||||
pick_ori=None,
|
||||
rank=None,
|
||||
weight_norm=None,
|
||||
reduce_rank=False,
|
||||
depth=1.0,
|
||||
real_filter=True,
|
||||
inversion="matrix",
|
||||
verbose=None,
|
||||
):
|
||||
"""Compute a Dynamic Imaging of Coherent Sources (DICS) spatial filter.
|
||||
|
||||
This is a beamformer filter that can be used to estimate the source power
|
||||
at a specific frequency range :footcite:`GrossEtAl2001`. It does this by
|
||||
constructing a spatial filter for each source point.
|
||||
The computation of these filters is very similar to those of the LCMV
|
||||
beamformer (:func:`make_lcmv`), but instead of operating on a covariance
|
||||
matrix, the CSD matrix is used. When applying these filters to a CSD matrix
|
||||
(see :func:`apply_dics_csd`), the source power can be estimated for each
|
||||
source point.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
%(info_not_none)s
|
||||
forward : instance of Forward
|
||||
Forward operator.
|
||||
csd : instance of CrossSpectralDensity
|
||||
The data cross-spectral density (CSD) matrices. A source estimate is
|
||||
performed for each frequency or frequency-bin defined in the CSD
|
||||
object.
|
||||
reg : float
|
||||
The regularization to apply to the cross-spectral density before
|
||||
computing the inverse.
|
||||
noise_csd : instance of CrossSpectralDensity | None
|
||||
Noise cross-spectral density (CSD) matrices. If provided, whitening
|
||||
will be done. The noise CSDs need to have been computed for the same
|
||||
frequencies as the data CSDs. Providing noise CSDs is mandatory if you
|
||||
mix sensor types, e.g. gradiometers with magnetometers or EEG with
|
||||
MEG.
|
||||
|
||||
.. versionadded:: 0.20
|
||||
label : Label | None
|
||||
Restricts the solution to a given label.
|
||||
%(pick_ori_bf)s
|
||||
%(rank_none)s
|
||||
|
||||
.. versionadded:: 0.17
|
||||
%(weight_norm)s
|
||||
|
||||
Defaults to ``None``, in which case no normalization is performed.
|
||||
%(reduce_rank)s
|
||||
%(depth)s
|
||||
real_filter : bool
|
||||
If ``True``, take only the real part of the cross-spectral-density
|
||||
matrices to compute real filters.
|
||||
|
||||
.. versionchanged:: 0.23
|
||||
Version 0.23 an earlier used ``real_filter=False`` as the default,
|
||||
as of version 0.24 ``True`` is the default.
|
||||
%(inversion_bf)s
|
||||
|
||||
.. versionchanged:: 0.21
|
||||
Default changed to ``'matrix'``.
|
||||
%(verbose)s
|
||||
|
||||
Returns
|
||||
-------
|
||||
filters : instance of Beamformer
|
||||
Dictionary containing filter weights from DICS beamformer.
|
||||
Contains the following keys:
|
||||
|
||||
'kind' : str
|
||||
The type of beamformer, in this case 'DICS'.
|
||||
'weights' : ndarray, shape (n_frequencies, n_weights)
|
||||
For each frequency, the filter weights of the beamformer.
|
||||
'csd' : instance of CrossSpectralDensity
|
||||
The data cross-spectral density matrices used to compute the
|
||||
beamformer.
|
||||
'ch_names' : list of str
|
||||
Channels used to compute the beamformer.
|
||||
'proj' : ndarray, shape (n_channels, n_channels)
|
||||
Projections used to compute the beamformer.
|
||||
'vertices' : list of ndarray
|
||||
Vertices for which the filter weights were computed.
|
||||
'n_sources' : int
|
||||
Number of source location for which the filter weight were
|
||||
computed.
|
||||
'subject' : str
|
||||
The subject ID.
|
||||
'pick-ori' : None | 'max-power' | 'normal' | 'vector'
|
||||
The orientation in which the beamformer filters were computed.
|
||||
'inversion' : 'single' | 'matrix'
|
||||
Whether the spatial filters were computed for each dipole
|
||||
separately or jointly for all dipoles at each vertex using a
|
||||
matrix inversion.
|
||||
'weight_norm' : None | 'unit-noise-gain'
|
||||
The normalization of the weights.
|
||||
'src_type' : str
|
||||
Type of source space.
|
||||
'source_nn' : ndarray, shape (n_sources, 3)
|
||||
For each source location, the surface normal.
|
||||
'is_free_ori' : bool
|
||||
Whether the filter was computed in a fixed direction
|
||||
(pick_ori='max-power', pick_ori='normal') or not.
|
||||
'whitener' : None | ndarray, shape (n_channels, n_channels)
|
||||
Whitening matrix, provided if whitening was applied to the
|
||||
covariance matrix and leadfield during computation of the
|
||||
beamformer weights.
|
||||
'max-power-ori' : ndarray, shape (n_sources, 3) | None
|
||||
When pick_ori='max-power', this fields contains the estimated
|
||||
direction of maximum power at each source location.
|
||||
|
||||
See Also
|
||||
--------
|
||||
apply_dics_csd
|
||||
|
||||
Notes
|
||||
-----
|
||||
The original reference is :footcite:`GrossEtAl2001`. See
|
||||
:footcite:`vanVlietEtAl2018` for a tutorial style paper on the topic.
|
||||
|
||||
The DICS beamformer is very similar to the LCMV (:func:`make_lcmv`)
|
||||
beamformer and many of the parameters are shared. However,
|
||||
:func:`make_dics` and :func:`make_lcmv` currently have different defaults
|
||||
for these parameters, which were settled on separately through extensive
|
||||
practical use case testing (but not necessarily exhaustive parameter space
|
||||
searching), and it remains to be seen how functionally interchangeable they
|
||||
could be.
|
||||
|
||||
The default setting reproduce the DICS beamformer as described in
|
||||
:footcite:`vanVlietEtAl2018`::
|
||||
|
||||
inversion='single', weight_norm=None, depth=1.
|
||||
|
||||
To use the :func:`make_lcmv` defaults, use::
|
||||
|
||||
inversion='matrix', weight_norm='unit-noise-gain-invariant', depth=None
|
||||
|
||||
For more information about ``real_filter``, see the
|
||||
supplemental information from :footcite:`HippEtAl2011`.
|
||||
|
||||
References
|
||||
----------
|
||||
.. footbibliography::
|
||||
""" # noqa: E501
|
||||
rank = _check_rank(rank)
|
||||
_check_option("pick_ori", pick_ori, [None, "vector", "normal", "max-power"])
|
||||
_check_option("inversion", inversion, ["single", "matrix"])
|
||||
_validate_type(weight_norm, (str, None), "weight_norm")
|
||||
|
||||
frequencies = [np.mean(freq_bin) for freq_bin in csd.frequencies]
|
||||
n_freqs = len(frequencies)
|
||||
|
||||
_, _, allow_mismatch = _check_one_ch_type("dics", info, forward, csd, noise_csd)
|
||||
# remove bads so that equalize_channels only keeps all good
|
||||
info = pick_info(info, pick_channels(info["ch_names"], [], info["bads"]))
|
||||
info, forward, csd = equalize_channels([info, forward, csd])
|
||||
|
||||
csd, noise_csd = _prepare_noise_csd(csd, noise_csd, real_filter)
|
||||
|
||||
depth = _check_depth(depth, "depth_sparse")
|
||||
if inversion == "single":
|
||||
depth["combine_xyz"] = False
|
||||
|
||||
(
|
||||
is_free_ori,
|
||||
info,
|
||||
proj,
|
||||
vertices,
|
||||
G,
|
||||
whitener,
|
||||
nn,
|
||||
orient_std,
|
||||
) = _prepare_beamformer_input(
|
||||
info,
|
||||
forward,
|
||||
label,
|
||||
pick_ori,
|
||||
noise_cov=noise_csd,
|
||||
rank=rank,
|
||||
pca=False,
|
||||
**depth,
|
||||
)
|
||||
|
||||
# Compute ranks
|
||||
csd_int_rank = []
|
||||
if not allow_mismatch:
|
||||
noise_rank = compute_rank(noise_csd, info=info, rank=rank)
|
||||
for i in range(len(frequencies)):
|
||||
csd_rank = compute_rank(
|
||||
csd.get_data(index=i, as_cov=True), info=info, rank=rank
|
||||
)
|
||||
if not allow_mismatch:
|
||||
for key in csd_rank:
|
||||
if key not in noise_rank or csd_rank[key] != noise_rank[key]:
|
||||
raise ValueError(
|
||||
f"{key} data rank ({csd_rank[key]}) did not match the noise "
|
||||
f"rank ({noise_rank.get(key, None)})"
|
||||
)
|
||||
csd_int_rank.append(sum(csd_rank.values()))
|
||||
|
||||
del noise_csd
|
||||
ch_names = list(info["ch_names"])
|
||||
|
||||
logger.info("Computing DICS spatial filters...")
|
||||
Ws = []
|
||||
max_oris = []
|
||||
for i, freq in enumerate(frequencies):
|
||||
if n_freqs > 1:
|
||||
logger.info(
|
||||
" computing DICS spatial filter at "
|
||||
f"{round(freq, 2)} Hz ({i + 1}/{n_freqs})"
|
||||
)
|
||||
|
||||
Cm = csd.get_data(index=i)
|
||||
|
||||
# XXX: Weird that real_filter happens *before* whitening, which could
|
||||
# make things complex again...?
|
||||
if real_filter:
|
||||
Cm = Cm.real
|
||||
|
||||
# compute spatial filter
|
||||
n_orient = 3 if is_free_ori else 1
|
||||
W, max_power_ori = _compute_beamformer(
|
||||
G,
|
||||
Cm,
|
||||
reg,
|
||||
n_orient,
|
||||
weight_norm,
|
||||
pick_ori,
|
||||
reduce_rank,
|
||||
rank=csd_int_rank[i],
|
||||
inversion=inversion,
|
||||
nn=nn,
|
||||
orient_std=orient_std,
|
||||
whitener=whitener,
|
||||
)
|
||||
Ws.append(W)
|
||||
max_oris.append(max_power_ori)
|
||||
|
||||
Ws = np.array(Ws)
|
||||
if pick_ori == "max-power":
|
||||
max_oris = np.array(max_oris)
|
||||
else:
|
||||
max_oris = None
|
||||
|
||||
src_type = _get_src_type(forward["src"], vertices)
|
||||
subject = _subject_from_forward(forward)
|
||||
is_free_ori = is_free_ori if pick_ori in [None, "vector"] else False
|
||||
n_sources = np.sum([len(v) for v in vertices])
|
||||
|
||||
filters = Beamformer(
|
||||
kind="DICS",
|
||||
weights=Ws,
|
||||
csd=csd,
|
||||
ch_names=ch_names,
|
||||
proj=proj,
|
||||
vertices=vertices,
|
||||
n_sources=n_sources,
|
||||
subject=subject,
|
||||
pick_ori=pick_ori,
|
||||
inversion=inversion,
|
||||
weight_norm=weight_norm,
|
||||
src_type=src_type,
|
||||
source_nn=forward["source_nn"].copy(),
|
||||
is_free_ori=is_free_ori,
|
||||
whitener=whitener,
|
||||
max_power_ori=max_oris,
|
||||
)
|
||||
|
||||
return filters
|
||||
|
||||
|
||||
def _prepare_noise_csd(csd, noise_csd, real_filter):
|
||||
if noise_csd is not None:
|
||||
csd, noise_csd = equalize_channels([csd, noise_csd])
|
||||
# Use the same noise CSD for all frequencies
|
||||
if len(noise_csd.frequencies) > 1:
|
||||
noise_csd = noise_csd.mean()
|
||||
noise_csd = noise_csd.get_data(as_cov=True)
|
||||
if real_filter:
|
||||
noise_csd["data"] = noise_csd["data"].real
|
||||
return csd, noise_csd
|
||||
|
||||
|
||||
def _apply_dics(data, filters, info, tmin, tfr=False):
|
||||
"""Apply DICS spatial filter to data for source reconstruction."""
|
||||
if isinstance(data, np.ndarray) and data.ndim == (2 + tfr):
|
||||
data = [data]
|
||||
one_epoch = True
|
||||
else:
|
||||
one_epoch = False
|
||||
|
||||
Ws = filters["weights"]
|
||||
one_freq = len(Ws) == 1
|
||||
|
||||
subject = filters["subject"]
|
||||
# compatibility with 0.16, add src_type as None if not present:
|
||||
filters, warn_text = _check_src_type(filters)
|
||||
|
||||
for i, M in enumerate(data):
|
||||
if not one_epoch:
|
||||
logger.info(f"Processing epoch : {i + 1}")
|
||||
|
||||
# Apply SSPs
|
||||
if not tfr: # save computation, only compute once
|
||||
M_w = _proj_whiten_data(M, info["projs"], filters)
|
||||
|
||||
stcs = []
|
||||
for j, W in enumerate(Ws):
|
||||
if tfr: # must compute for each frequency
|
||||
M_w = _proj_whiten_data(M[:, j], info["projs"], filters)
|
||||
|
||||
# project to source space using beamformer weights
|
||||
sol = np.dot(W, M_w)
|
||||
|
||||
if filters["is_free_ori"] and filters["pick_ori"] != "vector":
|
||||
logger.info("combining the current components...")
|
||||
sol = combine_xyz(sol)
|
||||
|
||||
tstep = 1.0 / info["sfreq"]
|
||||
|
||||
stcs.append(
|
||||
_make_stc(
|
||||
sol,
|
||||
vertices=filters["vertices"],
|
||||
src_type=filters["src_type"],
|
||||
tmin=tmin,
|
||||
tstep=tstep,
|
||||
subject=subject,
|
||||
vector=(filters["pick_ori"] == "vector"),
|
||||
source_nn=filters["source_nn"],
|
||||
warn_text=warn_text,
|
||||
)
|
||||
)
|
||||
if one_freq:
|
||||
yield stcs[0]
|
||||
else:
|
||||
yield stcs
|
||||
|
||||
logger.info("[done]")
|
||||
|
||||
|
||||
@verbose
|
||||
def apply_dics(evoked, filters, verbose=None):
|
||||
"""Apply Dynamic Imaging of Coherent Sources (DICS) beamformer weights.
|
||||
|
||||
Apply Dynamic Imaging of Coherent Sources (DICS) beamformer weights
|
||||
on evoked data.
|
||||
|
||||
.. warning:: The result of this function is meant as an intermediate step
|
||||
for further processing (such as computing connectivity). If
|
||||
you are interested in estimating source time courses, use an
|
||||
LCMV beamformer (:func:`make_lcmv`, :func:`apply_lcmv`)
|
||||
instead. If you are interested in estimating spectral power at
|
||||
the source level, use :func:`apply_dics_csd`.
|
||||
.. warning:: This implementation has not been heavily tested so please
|
||||
report any issues or suggestions.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
evoked : Evoked
|
||||
Evoked data to apply the DICS beamformer weights to.
|
||||
filters : instance of Beamformer
|
||||
DICS spatial filter (beamformer weights)
|
||||
Filter weights returned from :func:`make_dics`.
|
||||
%(verbose)s
|
||||
|
||||
Returns
|
||||
-------
|
||||
stc : SourceEstimate | VolSourceEstimate | list
|
||||
Source time courses. If the DICS beamformer has been computed for more
|
||||
than one frequency, a list is returned containing for each frequency
|
||||
the corresponding time courses.
|
||||
|
||||
See Also
|
||||
--------
|
||||
apply_dics_epochs
|
||||
apply_dics_tfr_epochs
|
||||
apply_dics_csd
|
||||
""" # noqa: E501
|
||||
_check_reference(evoked)
|
||||
|
||||
info = evoked.info
|
||||
data = evoked.data
|
||||
tmin = evoked.times[0]
|
||||
|
||||
sel = _check_channels_spatial_filter(evoked.ch_names, filters)
|
||||
data = data[sel]
|
||||
|
||||
stc = _apply_dics(data=data, filters=filters, info=info, tmin=tmin)
|
||||
|
||||
return next(stc)
|
||||
|
||||
|
||||
@verbose
|
||||
def apply_dics_epochs(epochs, filters, return_generator=False, verbose=None):
|
||||
"""Apply Dynamic Imaging of Coherent Sources (DICS) beamformer weights.
|
||||
|
||||
Apply Dynamic Imaging of Coherent Sources (DICS) beamformer weights
|
||||
on single trial data.
|
||||
|
||||
.. warning:: The result of this function is meant as an intermediate step
|
||||
for further processing (such as computing connectivity). If
|
||||
you are interested in estimating source time courses, use an
|
||||
LCMV beamformer (:func:`make_lcmv`, :func:`apply_lcmv`)
|
||||
instead. If you are interested in estimating spectral power at
|
||||
the source level, use :func:`apply_dics_csd`.
|
||||
.. warning:: This implementation has not been heavily tested so please
|
||||
report any issue or suggestions.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
epochs : Epochs
|
||||
Single trial epochs.
|
||||
filters : instance of Beamformer
|
||||
DICS spatial filter (beamformer weights)
|
||||
Filter weights returned from :func:`make_dics`. The DICS filters must
|
||||
have been computed for a single frequency only.
|
||||
return_generator : bool
|
||||
Return a generator object instead of a list. This allows iterating
|
||||
over the stcs without having to keep them all in memory.
|
||||
%(verbose)s
|
||||
|
||||
Returns
|
||||
-------
|
||||
stc: list | generator of (SourceEstimate | VolSourceEstimate)
|
||||
The source estimates for all epochs.
|
||||
|
||||
See Also
|
||||
--------
|
||||
apply_dics
|
||||
apply_dics_tfr_epochs
|
||||
apply_dics_csd
|
||||
"""
|
||||
_check_reference(epochs)
|
||||
|
||||
if len(filters["weights"]) > 1:
|
||||
raise ValueError(
|
||||
"This function only works on DICS beamformer weights that have "
|
||||
"been computed for a single frequency. When calling make_dics(), "
|
||||
"make sure to use a CSD object with only a single frequency (or "
|
||||
"frequency-bin) defined."
|
||||
)
|
||||
|
||||
info = epochs.info
|
||||
tmin = epochs.times[0]
|
||||
|
||||
sel = _check_channels_spatial_filter(epochs.ch_names, filters)
|
||||
data = epochs.get_data(sel)
|
||||
|
||||
stcs = _apply_dics(data=data, filters=filters, info=info, tmin=tmin)
|
||||
|
||||
if not return_generator:
|
||||
stcs = list(stcs)
|
||||
|
||||
return stcs
|
||||
|
||||
|
||||
@verbose
|
||||
def apply_dics_tfr_epochs(epochs_tfr, filters, return_generator=False, verbose=None):
|
||||
"""Apply Dynamic Imaging of Coherent Sources (DICS) beamformer weights.
|
||||
|
||||
Apply Dynamic Imaging of Coherent Sources (DICS) beamformer weights
|
||||
on single trial time-frequency data.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
epochs_tfr : EpochsTFR
|
||||
Single trial time-frequency epochs.
|
||||
filters : instance of Beamformer
|
||||
DICS spatial filter (beamformer weights)
|
||||
Filter weights returned from :func:`make_dics`.
|
||||
return_generator : bool
|
||||
Return a generator object instead of a list. This allows iterating
|
||||
over the stcs without having to keep them all in memory.
|
||||
%(verbose)s
|
||||
|
||||
Returns
|
||||
-------
|
||||
stcs : list of list of (SourceEstimate | VectorSourceEstimate | VolSourceEstimate)
|
||||
The source estimates for all epochs (outside list) and for
|
||||
all frequencies (inside list).
|
||||
|
||||
See Also
|
||||
--------
|
||||
apply_dics
|
||||
apply_dics_epochs
|
||||
apply_dics_csd
|
||||
""" # noqa E501
|
||||
_validate_type(epochs_tfr, EpochsTFR)
|
||||
_check_tfr_complex(epochs_tfr)
|
||||
|
||||
if filters["pick_ori"] == "vector":
|
||||
warn(
|
||||
"Using a vector solution to compute power will lead to "
|
||||
"inaccurate directions (only in the first quadrent) "
|
||||
"because power is a strictly positive (squared) metric. "
|
||||
"Using singular value decomposition (SVD) to determine "
|
||||
"the direction is not yet supported in MNE."
|
||||
)
|
||||
|
||||
sel = _check_channels_spatial_filter(epochs_tfr.ch_names, filters)
|
||||
data = epochs_tfr.data[:, sel, :, :]
|
||||
|
||||
stcs = _apply_dics(data, filters, epochs_tfr.info, epochs_tfr.tmin, tfr=True)
|
||||
if not return_generator:
|
||||
stcs = [[stc for stc in tfr_stcs] for tfr_stcs in stcs]
|
||||
return stcs
|
||||
|
||||
|
||||
@verbose
|
||||
def apply_dics_csd(csd, filters, verbose=None):
|
||||
"""Apply Dynamic Imaging of Coherent Sources (DICS) beamformer weights.
|
||||
|
||||
Apply a previously computed DICS beamformer to a cross-spectral density
|
||||
(CSD) object to estimate source power in time and frequency windows
|
||||
specified in the CSD object :footcite:`GrossEtAl2001`.
|
||||
|
||||
.. note:: Only power can computed from the cross-spectral density, not
|
||||
complex phase-amplitude, so vector DICS filters will be
|
||||
converted to scalar source estimates since power is strictly
|
||||
positive and so 3D directions cannot be combined meaningfully
|
||||
(the direction would be confined to the positive quadrant).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
csd : instance of CrossSpectralDensity
|
||||
The data cross-spectral density (CSD) matrices. A source estimate is
|
||||
performed for each frequency or frequency-bin defined in the CSD
|
||||
object.
|
||||
filters : instance of Beamformer
|
||||
DICS spatial filter (beamformer weights)
|
||||
Filter weights returned from `make_dics`.
|
||||
%(verbose)s
|
||||
|
||||
Returns
|
||||
-------
|
||||
stc : SourceEstimate
|
||||
Source power with frequency instead of time.
|
||||
frequencies : list of float
|
||||
The frequencies for which the source power has been computed. If the
|
||||
data CSD object defines frequency-bins instead of exact frequencies,
|
||||
the mean of each bin is returned.
|
||||
|
||||
See Also
|
||||
--------
|
||||
apply_dics
|
||||
apply_dics_epochs
|
||||
apply_dics_tfr_epochs
|
||||
|
||||
References
|
||||
----------
|
||||
.. footbibliography::
|
||||
""" # noqa: E501
|
||||
ch_names = filters["ch_names"]
|
||||
vertices = filters["vertices"]
|
||||
n_orient = 3 if filters["is_free_ori"] else 1
|
||||
subject = filters["subject"]
|
||||
whitener = filters["whitener"]
|
||||
n_sources = filters["n_sources"]
|
||||
|
||||
# If CSD is summed over multiple frequencies, take the average frequency
|
||||
frequencies = [np.mean(dfreq) for dfreq in csd.frequencies]
|
||||
n_freqs = len(frequencies)
|
||||
|
||||
source_power = np.zeros((n_sources, len(csd.frequencies)))
|
||||
|
||||
# Ensure the CSD is in the same order as the weights
|
||||
csd_picks = [csd.ch_names.index(ch) for ch in ch_names]
|
||||
|
||||
logger.info("Computing DICS source power...")
|
||||
for i, freq in enumerate(frequencies):
|
||||
if n_freqs > 1:
|
||||
logger.info(
|
||||
" applying DICS spatial filter at "
|
||||
f"{round(freq, 2)} Hz ({i + 1}/{n_freqs})"
|
||||
)
|
||||
|
||||
Cm = csd.get_data(index=i)
|
||||
Cm = Cm[csd_picks, :][:, csd_picks]
|
||||
W = filters["weights"][i]
|
||||
|
||||
# Whiten the CSD
|
||||
Cm = np.dot(whitener, np.dot(Cm, whitener.conj().T))
|
||||
|
||||
source_power[:, i] = _compute_power(Cm, W, n_orient)
|
||||
|
||||
logger.info("[done]")
|
||||
|
||||
# compatibility with 0.16, add src_type as None if not present:
|
||||
filters, warn_text = _check_src_type(filters)
|
||||
|
||||
return (
|
||||
_make_stc(
|
||||
source_power,
|
||||
vertices=vertices,
|
||||
src_type=filters["src_type"],
|
||||
tmin=0.0,
|
||||
tstep=1.0,
|
||||
subject=subject,
|
||||
warn_text=warn_text,
|
||||
),
|
||||
frequencies,
|
||||
)
|
||||
Reference in New Issue
Block a user