-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[WIP] Add CSD whitening to DICS beamformer #7021
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7c53b5b
34a1578
56320f4
1e65b19
275f68b
46efd8d
da95615
9965058
444ec4c
0dc4b00
b112081
6556ff6
a132bb3
60066c8
ce8bd08
1ffebaf
d7d792d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,9 +8,10 @@ | |
| # License: BSD (3-clause) | ||
| import numpy as np | ||
|
|
||
| from ..io.pick import pick_info | ||
| from ..utils import (logger, verbose, warn, _check_one_ch_type, | ||
| _check_channels_spatial_filter, _check_rank, | ||
| _check_option) | ||
| _check_option, _check_info_inv) | ||
| from ..forward import _subject_from_forward | ||
| from ..minimum_norm.inverse import combine_xyz, _check_reference | ||
| from ..source_estimate import _make_stc, _get_src_type | ||
|
|
@@ -21,8 +22,8 @@ | |
|
|
||
|
|
||
| @verbose | ||
| def make_dics(info, forward, csd, reg=0.05, label=None, pick_ori=None, | ||
| rank=None, inversion='single', weight_norm=None, | ||
| def make_dics(info, forward, csd, reg=0.05, noise_csd=None, label=None, | ||
| pick_ori=None, rank=None, inversion='single', weight_norm=None, | ||
| normalize_fwd=True, real_filter=False, reduce_rank=False, | ||
| verbose=None): | ||
| """Compute a Dynamic Imaging of Coherent Sources (DICS) spatial filter. | ||
|
|
@@ -49,6 +50,14 @@ def make_dics(info, forward, csd, reg=0.05, label=None, pick_ori=None, | |
| 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 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My reasoning with the grammar was that as you are supplying the CSDs are a parameter to this function, they already have been computed. |
||
| frequencies as the data CSDs. Providing noise CSDs is mandatory if you | ||
| mix sensor types, e.g. gradiometers with magnetometers or EEG with | ||
| MEG. | ||
wmvanvliet marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| .. versionadded:: 0.20 | ||
| label : Label | None | ||
| Restricts the solution to a given label. | ||
| pick_ori : None | 'normal' | 'max-power' | ||
|
|
@@ -191,19 +200,27 @@ def make_dics(info, forward, csd, reg=0.05, label=None, pick_ori=None, | |
| exp = None # turn off depth weighting entirely | ||
| combine_xyz = False | ||
|
|
||
| _check_one_ch_type('dics', info, forward) | ||
| _check_one_ch_type('dics', info, forward, csd, noise_csd) | ||
|
|
||
| # pick info, get gain matrix, etc. | ||
| _, info, proj, vertices, G, _, nn, orient_std = _prepare_beamformer_input( | ||
| info, forward, label, pick_ori, | ||
| combine_xyz=combine_xyz, exp=exp) | ||
| subject = _subject_from_forward(forward) | ||
| src_type = _get_src_type(forward['src'], vertices) | ||
| del forward | ||
| ch_names = list(info['ch_names']) | ||
|
|
||
| csd_picks = [csd.ch_names.index(ch) for ch in ch_names] | ||
|
|
||
| if noise_csd is not None: | ||
| # 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 | ||
|
|
||
| _, _, proj, vertices, G, whitener, nn, orient_std = \ | ||
| _prepare_beamformer_input( | ||
| info, forward, label, pick_ori, noise_cov=noise_csd, rank=rank, | ||
| pca=False, combine_xyz=combine_xyz, exp=exp) | ||
|
|
||
| logger.info('Computing DICS spatial filters...') | ||
| Ws = [] | ||
| for i, freq in enumerate(frequencies): | ||
|
|
@@ -212,13 +229,15 @@ def make_dics(info, forward, csd, reg=0.05, label=None, pick_ori=None, | |
| (freq, i + 1, n_freqs)) | ||
|
|
||
| Cm = csd.get_data(index=i) | ||
|
|
||
| if real_filter: | ||
| Cm = Cm.real | ||
|
|
||
| # Ensure the CSD is in the same order as the leadfield | ||
| Cm = Cm[csd_picks, :][:, csd_picks] | ||
|
|
||
| # Whiten the CSD | ||
| Cm = np.dot(whitener, np.dot(Cm, whitener.conj().T)) | ||
|
|
||
| # compute spatial filter | ||
| W = _compute_beamformer(G, Cm, reg, n_orient, weight_norm, pick_ori, | ||
| reduce_rank, rank=rank, inversion=inversion, | ||
|
|
@@ -227,12 +246,13 @@ def make_dics(info, forward, csd, reg=0.05, label=None, pick_ori=None, | |
|
|
||
| Ws = np.array(Ws) | ||
|
|
||
| src_type = _get_src_type(forward['src'], vertices) | ||
| filters = Beamformer( | ||
| kind='DICS', weights=Ws, csd=csd, ch_names=ch_names, proj=proj, | ||
| vertices=vertices, subject=subject, pick_ori=pick_ori, | ||
| inversion=inversion, weight_norm=weight_norm, | ||
| normalize_fwd=bool(normalize_fwd), src_type=src_type, | ||
| n_orient=n_orient if pick_ori is None else 1) | ||
| n_orient=n_orient if pick_ori is None else 1, whitener=whitener) | ||
|
|
||
| return filters | ||
|
|
||
|
|
@@ -434,6 +454,7 @@ def apply_dics_csd(csd, filters, verbose=None): | |
| vertices = filters['vertices'] | ||
| n_orient = filters['n_orient'] | ||
| subject = filters['subject'] | ||
| whitener = filters['whitener'] | ||
| n_sources = np.sum([len(v) for v in vertices]) | ||
|
|
||
| # If CSD is summed over multiple frequencies, take the average frequency | ||
|
|
@@ -455,6 +476,9 @@ def apply_dics_csd(csd, filters, verbose=None): | |
| 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]') | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hooray!