Skip to content
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

ENH: muscle artifact detection #7407

Merged
merged 35 commits into from
Apr 7, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7c5e680
muscle artifact detection
AdoNunes Mar 9, 2020
e42ed5c
docstrings
AdoNunes Mar 9, 2020
b705ae7
docstrings
AdoNunes Mar 9, 2020
73351db
Alex suggestions
AdoNunes Mar 9, 2020
24da9f6
example
AdoNunes Mar 9, 2020
97f6089
Merge branch 'master' of git://github.com/mne-tools/mne-python into d…
AdoNunes Mar 9, 2020
72c8c4d
example
AdoNunes Mar 9, 2020
97d3ac4
Merge branch 'detect_muscle' of https://github.com/AdoNunes/mne-pytho…
AdoNunes Mar 9, 2020
39be3d3
for eeg
AdoNunes Mar 10, 2020
ab04ad7
skip_by_annotation
AdoNunes Mar 11, 2020
2a66910
new func added
AdoNunes Mar 17, 2020
d477c60
docstring
AdoNunes Mar 17, 2020
62a00eb
cross ref
AdoNunes Mar 17, 2020
8f289c5
cross ref2
AdoNunes Mar 17, 2020
a0722fc
Alex suggestions
AdoNunes Mar 17, 2020
1eaee82
new parameters
AdoNunes Mar 17, 2020
41420b3
n_jobs
AdoNunes Mar 17, 2020
a9d5d4d
docstring
AdoNunes Mar 17, 2020
38a328b
API name change, zscore
AdoNunes Mar 20, 2020
b232e4f
test update
AdoNunes Mar 20, 2020
d9412db
sqrt nchans
AdoNunes Mar 23, 2020
b5e378e
handle nans zscore
AdoNunes Mar 23, 2020
b055b53
example note
AdoNunes Mar 23, 2020
5c36c03
Apply suggestions from code review drammock
AdoNunes Mar 24, 2020
ee8e871
revision changes
AdoNunes Mar 24, 2020
0953d9a
docstring
AdoNunes Mar 24, 2020
647b9ab
docstring
AdoNunes Mar 24, 2020
973c1d7
ch_type param
AdoNunes Mar 25, 2020
14ccd5c
Merge branch 'master' into detect_muscle
AdoNunes Mar 25, 2020
5701cb0
logger
AdoNunes Mar 26, 2020
416ab47
docstrings
AdoNunes Mar 26, 2020
269277e
ch type
AdoNunes Mar 30, 2020
d82c614
test no meeg data
AdoNunes Apr 7, 2020
2e1531d
test no meeg data
AdoNunes Apr 7, 2020
d1d448d
fix docstring
AdoNunes Apr 7, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/python_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ Projections:
ICA
Xdawn
annotate_movement
annotate_muscle
compute_average_dev_head_t
compute_current_source_density
compute_proj_ecg
Expand Down
3 changes: 2 additions & 1 deletion mne/preprocessing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@
from .xdawn import Xdawn
from ._csd import compute_current_source_density
from . import nirs
from .artifact_detection import (annotate_movement, compute_average_dev_head_t)
from .artifact_detection import (annotate_movement, compute_average_dev_head_t,
annotate_muscle)
62 changes: 61 additions & 1 deletion mne/preprocessing/artifact_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,74 @@


import numpy as np

from scipy.stats import zscore
from scipy.ndimage.measurements import label
from ..annotations import (Annotations, _annotations_starts_stops)
from ..transforms import (quat_to_rot, _average_quats, _angle_between_quats,
apply_trans, _quat_to_affine)
from ..filter import filter_data
from .. import Transform
from ..utils import (_mask_to_onsets_offsets, logger)


def annotate_muscle(raw, threshold=1.5, picks=None, min_length_good=.1):
"""Detect segments with muscle artifacts.

Detects segments periods that contains high frequency activity beyond the
specified threshold. Muscle artifacts are most notable in the range of 110-
140Hz.

Raw data is band pass filtered between 110 and 140 Hz, the signal envelope
computed, z-scored across samples, channel averaged and low-pass
filtered to smooth transient peaks.

Parameters
----------
raw : instance of Raw
Data to compute head position.
threshold : float
The threshod for selecting segments with muscle activity artifacts.
picks : array
Channels to use for artifact detection.
min_length_good : int | float | None
The minimal good segment length between annotations, smaller segments
will be included in the movement annotation.

Returns
-------
annot : mne.Annotations
Periods with muscle artifacts.
scores_muscle : array
Z-score values averaged accros channels for each sample.
"""
raw_copy = raw.copy()
raw_copy.pick(picks)
raw_copy.pick_types(ref_meg=False) # Remove ref chans just in case
# Only one type of channel, otherwise z-score will be biased
assert(len(set(raw_copy.get_channel_types())) == 1), 'Different channel ' \
'types, pick one type'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throw an explicit ValueError error instead of AssertionError


raw_copy.filter(110, 140, fir_design='firwin')
raw_copy.apply_hilbert(envelope=True)
sfreq = raw_copy.info['sfreq']

art_scores = zscore(raw_copy._data, axis=1)
scores_muscle = filter_data(art_scores.mean(axis=0), sfreq, None, 4)
art_mask = scores_muscle > threshold

# remove artifact free periods shorter than min_length_good
idx_min = min_length_good * sfreq
comps, num_comps = label(art_mask == 0)
for l in range(1, num_comps + 1):
l_idx = np.nonzero(comps == l)[0]
if len(l_idx) < idx_min:
art_mask[l_idx] = True

annot = _annotations_from_mask(raw_copy.times, art_mask, 'BAD_muscle')

return annot, scores_muscle


def annotate_movement(raw, pos, rotation_velocity_limit=None,
translation_velocity_limit=None,
mean_distance_limit=None):
Expand Down
14 changes: 13 additions & 1 deletion mne/preprocessing/tests/test_artifact_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from mne.chpi import read_head_pos
from mne.datasets import testing
from mne.io import read_raw_fif
from mne.preprocessing import (annotate_movement, compute_average_dev_head_t)
from mne.preprocessing import (annotate_movement, compute_average_dev_head_t,
annotate_muscle)

data_path = testing.data_path(download=False)
sss_path = op.join(data_path, 'SSS')
Expand Down Expand Up @@ -47,3 +48,14 @@ def test_movement_annotation_head_correction():
[0., 0., 0., 1.]])

assert_allclose(dev_head_t_ori, dev_head_t['trans'], rtol=1e-5, atol=0)


@testing.requires_testing_data
def test_muscle_annotation():
"""Test correct detection muscle artifacts."""
raw = read_raw_fif(raw_fname, allow_maxshield='yes').load_data()
raw.pick_types(meg='grad', ref_meg=False)
raw.notch_filter([50, 110, 150])
# Check 2 muscle segments are detected
annot_muscle, scores = annotate_muscle(raw, threshold=1)
assert(annot_muscle.duration.size == 2)