accusleepy 0.5.0__py3-none-any.whl → 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- accusleepy/classification.py +49 -15
- accusleepy/config.json +15 -1
- accusleepy/constants.py +29 -2
- accusleepy/fileio.py +107 -33
- accusleepy/gui/images/primary_window.png +0 -0
- accusleepy/gui/images/viewer_window.png +0 -0
- accusleepy/gui/images/viewer_window_annotated.png +0 -0
- accusleepy/gui/main.py +220 -42
- accusleepy/gui/manual_scoring.py +38 -8
- accusleepy/gui/mplwidget.py +54 -29
- accusleepy/gui/primary_window.py +937 -254
- accusleepy/gui/primary_window.ui +3182 -2227
- accusleepy/gui/resources.qrc +1 -1
- accusleepy/gui/text/main_guide.md +18 -12
- accusleepy/gui/viewer_window.py +19 -7
- accusleepy/gui/viewer_window.ui +34 -2
- accusleepy/models.py +11 -1
- accusleepy/signal_processing.py +40 -17
- accusleepy/temperature_scaling.py +157 -0
- {accusleepy-0.5.0.dist-info → accusleepy-0.7.0.dist-info}/METADATA +11 -2
- accusleepy-0.7.0.dist-info/RECORD +41 -0
- {accusleepy-0.5.0.dist-info → accusleepy-0.7.0.dist-info}/WHEEL +1 -1
- accusleepy/gui/text/config_guide.txt +0 -29
- accusleepy-0.5.0.dist-info/RECORD +0 -41
accusleepy/gui/manual_scoring.py
CHANGED
|
@@ -32,7 +32,7 @@ from PySide6.QtWidgets import (
|
|
|
32
32
|
)
|
|
33
33
|
|
|
34
34
|
from accusleepy.constants import UNDEFINED_LABEL
|
|
35
|
-
from accusleepy.fileio import load_config, save_labels
|
|
35
|
+
from accusleepy.fileio import load_config, save_labels, EMGFilter
|
|
36
36
|
from accusleepy.gui.mplwidget import resample_x_ticks
|
|
37
37
|
from accusleepy.gui.viewer_window import Ui_ViewerWindow
|
|
38
38
|
from accusleepy.signal_processing import create_spectrogram, get_emg_power
|
|
@@ -99,8 +99,10 @@ class ManualScoringWindow(QDialog):
|
|
|
99
99
|
emg: np.array,
|
|
100
100
|
label_file: str,
|
|
101
101
|
labels: np.array,
|
|
102
|
+
confidence_scores: np.array,
|
|
102
103
|
sampling_rate: int | float,
|
|
103
104
|
epoch_length: int | float,
|
|
105
|
+
emg_filter: EMGFilter,
|
|
104
106
|
):
|
|
105
107
|
"""Initialize the manual scoring window
|
|
106
108
|
|
|
@@ -108,8 +110,10 @@ class ManualScoringWindow(QDialog):
|
|
|
108
110
|
:param emg: EMG signal
|
|
109
111
|
:param label_file: filename for labels
|
|
110
112
|
:param labels: brain state labels
|
|
113
|
+
:param confidence_scores: confidence scores
|
|
111
114
|
:param sampling_rate: sampling rate, in Hz
|
|
112
115
|
:param epoch_length: epoch length, in seconds
|
|
116
|
+
:param emg_filter: EMG filter parameters
|
|
113
117
|
"""
|
|
114
118
|
super(ManualScoringWindow, self).__init__()
|
|
115
119
|
|
|
@@ -117,8 +121,10 @@ class ManualScoringWindow(QDialog):
|
|
|
117
121
|
self.eeg = eeg
|
|
118
122
|
self.emg = emg
|
|
119
123
|
self.labels = labels
|
|
124
|
+
self.confidence_scores = confidence_scores
|
|
120
125
|
self.sampling_rate = sampling_rate
|
|
121
126
|
self.epoch_length = epoch_length
|
|
127
|
+
self.emg_filter = emg_filter
|
|
122
128
|
|
|
123
129
|
self.n_epochs = len(self.labels)
|
|
124
130
|
|
|
@@ -128,7 +134,7 @@ class ManualScoringWindow(QDialog):
|
|
|
128
134
|
self.setWindowTitle("AccuSleePy manual scoring window")
|
|
129
135
|
|
|
130
136
|
# load set of valid brain states
|
|
131
|
-
self.brain_state_set, _ = load_config()
|
|
137
|
+
self.brain_state_set, _, _, _, _, _, _ = load_config()
|
|
132
138
|
|
|
133
139
|
# initial setting for number of epochs to show in the lower plot
|
|
134
140
|
self.epochs_to_show = 5
|
|
@@ -150,7 +156,7 @@ class ManualScoringWindow(QDialog):
|
|
|
150
156
|
|
|
151
157
|
# calculate RMS of EMG for each epoch and apply a ceiling
|
|
152
158
|
self.upper_emg = create_upper_emg_signal(
|
|
153
|
-
self.emg, self.sampling_rate, self.epoch_length
|
|
159
|
+
self.emg, self.sampling_rate, self.epoch_length, self.emg_filter
|
|
154
160
|
)
|
|
155
161
|
|
|
156
162
|
# center and scale the EEG and EMG signals to fit the display
|
|
@@ -161,6 +167,8 @@ class ManualScoringWindow(QDialog):
|
|
|
161
167
|
self.label_img = create_label_img(
|
|
162
168
|
self.display_labels, self.label_display_options
|
|
163
169
|
)
|
|
170
|
+
# same sort of thing for confidence scores
|
|
171
|
+
self.confidence_img = create_confidence_img(self.confidence_scores)
|
|
164
172
|
|
|
165
173
|
# history of changes to the brain state labels
|
|
166
174
|
self.history = list()
|
|
@@ -172,6 +180,8 @@ class ManualScoringWindow(QDialog):
|
|
|
172
180
|
self.ui.upperfigure.setup_upper_figure(
|
|
173
181
|
self.n_epochs,
|
|
174
182
|
self.label_img,
|
|
183
|
+
self.confidence_scores,
|
|
184
|
+
self.confidence_img,
|
|
175
185
|
spectrogram,
|
|
176
186
|
spectrogram_frequencies,
|
|
177
187
|
self.upper_emg,
|
|
@@ -702,11 +712,11 @@ class ManualScoringWindow(QDialog):
|
|
|
702
712
|
|
|
703
713
|
def adjust_upper_figure_x_limits(self) -> None:
|
|
704
714
|
"""Update the x-axis limits of the upper figure subplots"""
|
|
705
|
-
for i in [0, 1,
|
|
715
|
+
for i in [0, 1, 2, 4]:
|
|
706
716
|
self.ui.upperfigure.canvas.axes[i].set_xlim(
|
|
707
717
|
(self.upper_left_epoch - 0.5, self.upper_right_epoch + 0.5)
|
|
708
718
|
)
|
|
709
|
-
self.ui.upperfigure.canvas.axes[
|
|
719
|
+
self.ui.upperfigure.canvas.axes[3].set_xlim(
|
|
710
720
|
(self.upper_left_epoch, self.upper_right_epoch + 1)
|
|
711
721
|
)
|
|
712
722
|
|
|
@@ -949,7 +959,7 @@ class ManualScoringWindow(QDialog):
|
|
|
949
959
|
# if it's on the spectrogram, we have to adjust it slightly
|
|
950
960
|
# since that uses a different x-axis range
|
|
951
961
|
ax_index = self.ui.upperfigure.canvas.axes.index(event.inaxes)
|
|
952
|
-
if ax_index ==
|
|
962
|
+
if ax_index == 3:
|
|
953
963
|
x -= 0.5
|
|
954
964
|
|
|
955
965
|
# get the "zoom level" so we can preserve that
|
|
@@ -1040,22 +1050,42 @@ def create_label_img(labels: np.array, label_display_options: np.array) -> np.ar
|
|
|
1040
1050
|
return label_img
|
|
1041
1051
|
|
|
1042
1052
|
|
|
1053
|
+
def create_confidence_img(confidence_scores: np.array) -> np.array:
|
|
1054
|
+
"""Create an image to display confidence scores
|
|
1055
|
+
|
|
1056
|
+
:param confidence_scores: confidence scores
|
|
1057
|
+
:return: confidence score image
|
|
1058
|
+
"""
|
|
1059
|
+
if confidence_scores is None:
|
|
1060
|
+
return None
|
|
1061
|
+
|
|
1062
|
+
confidence_img = np.ones([1, len(confidence_scores), 3])
|
|
1063
|
+
for i, c in enumerate(confidence_scores):
|
|
1064
|
+
confidence_img[0, i, 1:] = c
|
|
1065
|
+
return confidence_img
|
|
1066
|
+
|
|
1067
|
+
|
|
1043
1068
|
def create_upper_emg_signal(
|
|
1044
|
-
emg: np.array,
|
|
1069
|
+
emg: np.array,
|
|
1070
|
+
sampling_rate: int | float,
|
|
1071
|
+
epoch_length: int | float,
|
|
1072
|
+
emg_filter: EMGFilter,
|
|
1045
1073
|
) -> np.array:
|
|
1046
1074
|
"""Calculate RMS of EMG for each epoch and apply a ceiling
|
|
1047
1075
|
|
|
1048
1076
|
:param emg: EMG signal
|
|
1049
1077
|
:param sampling_rate: sampling rate, in Hz
|
|
1050
1078
|
:param epoch_length: epoch length, in seconds
|
|
1079
|
+
:param emg_filter: EMG filter parameters
|
|
1051
1080
|
:return: processed EMG signal
|
|
1052
1081
|
"""
|
|
1053
1082
|
emg_rms = get_emg_power(
|
|
1054
1083
|
emg,
|
|
1055
1084
|
sampling_rate,
|
|
1056
1085
|
epoch_length,
|
|
1086
|
+
emg_filter,
|
|
1057
1087
|
)
|
|
1058
|
-
return np.clip(emg_rms,
|
|
1088
|
+
return np.clip(emg_rms, np.min(emg_rms), np.mean(emg_rms) + np.std(emg_rms) * 2.5)
|
|
1059
1089
|
|
|
1060
1090
|
|
|
1061
1091
|
def transform_eeg_emg(eeg: np.array, emg: np.array) -> (np.array, np.array):
|
accusleepy/gui/mplwidget.py
CHANGED
|
@@ -66,6 +66,8 @@ class MplWidget(QtWidgets.QWidget):
|
|
|
66
66
|
self,
|
|
67
67
|
n_epochs: int,
|
|
68
68
|
label_img: np.array,
|
|
69
|
+
confidence_scores: np.array,
|
|
70
|
+
confidence_img: np.array,
|
|
69
71
|
spec: np.array,
|
|
70
72
|
f: np.array,
|
|
71
73
|
emg: np.array,
|
|
@@ -78,6 +80,8 @@ class MplWidget(QtWidgets.QWidget):
|
|
|
78
80
|
|
|
79
81
|
:param n_epochs: number of epochs in the recording
|
|
80
82
|
:param label_img: brain state labels, as an image
|
|
83
|
+
:param confidence_scores: confidence scores
|
|
84
|
+
:param confidence_img: confidence scores, as an image
|
|
81
85
|
:param spec: EEG spectrogram image
|
|
82
86
|
:param f: EEG spectrogram frequency axis
|
|
83
87
|
:param emg: EMG RMS per epoch
|
|
@@ -90,39 +94,60 @@ class MplWidget(QtWidgets.QWidget):
|
|
|
90
94
|
self.upper_marker = list()
|
|
91
95
|
|
|
92
96
|
# subplot layout
|
|
93
|
-
height_ratios = [8, 2, 12, 13]
|
|
94
|
-
gs1 = GridSpec(
|
|
95
|
-
gs2 = GridSpec(
|
|
97
|
+
height_ratios = [2, 8, 2, 12, 13]
|
|
98
|
+
gs1 = GridSpec(5, 1, hspace=0.02, height_ratios=height_ratios)
|
|
99
|
+
gs2 = GridSpec(5, 1, hspace=0.4, height_ratios=height_ratios)
|
|
96
100
|
axes = list()
|
|
97
101
|
axes.append(self.canvas.figure.add_subplot(gs1[0]))
|
|
98
102
|
axes.append(self.canvas.figure.add_subplot(gs1[1]))
|
|
99
103
|
axes.append(self.canvas.figure.add_subplot(gs1[2]))
|
|
100
|
-
axes.append(self.canvas.figure.add_subplot(
|
|
104
|
+
axes.append(self.canvas.figure.add_subplot(gs1[3]))
|
|
105
|
+
axes.append(self.canvas.figure.add_subplot(gs2[4]))
|
|
101
106
|
|
|
102
107
|
# subplots have different axes limits
|
|
103
|
-
for i in [0, 1,
|
|
108
|
+
for i in [0, 1, 2, 4]:
|
|
104
109
|
axes[i].set_xlim((-0.5, n_epochs - 0.5))
|
|
105
|
-
axes[
|
|
110
|
+
axes[3].set_xlim(0, n_epochs)
|
|
111
|
+
|
|
112
|
+
# confidence score subplot
|
|
113
|
+
if confidence_scores is None:
|
|
114
|
+
axes[0].set_visible(False)
|
|
115
|
+
else:
|
|
116
|
+
axes[0].set_ylim([-0.5, 0.5])
|
|
117
|
+
axes[0].set_xticks([])
|
|
118
|
+
axes[0].set_yticks([0])
|
|
119
|
+
axes[0].set_yticklabels(["Conf."])
|
|
120
|
+
axes[0].tick_params(axis="y", color="white")
|
|
121
|
+
axes[0].imshow(
|
|
122
|
+
confidence_img, aspect="auto", origin="lower", interpolation="None"
|
|
123
|
+
)
|
|
124
|
+
confidence_x = (
|
|
125
|
+
np.repeat(list(range(len(confidence_scores) + 1)), 2)[1:-1] - 0.5
|
|
126
|
+
)
|
|
127
|
+
confidence_y = np.repeat(confidence_scores, 2) - 0.5
|
|
128
|
+
axes[0].plot(confidence_x, confidence_y, "k", linewidth=0.5)
|
|
129
|
+
for side in ["left", "right", "bottom", "top"]:
|
|
130
|
+
axes[0].spines[side].set_visible(False)
|
|
106
131
|
|
|
107
132
|
# brain state subplot
|
|
108
|
-
axes[
|
|
133
|
+
axes[1].set_ylim(
|
|
109
134
|
[-0.5, np.max(label_display_options) - np.min(label_display_options) + 0.5]
|
|
110
135
|
)
|
|
111
|
-
axes[
|
|
112
|
-
axes[
|
|
136
|
+
axes[1].set_xticks([])
|
|
137
|
+
axes[1].set_yticks(
|
|
113
138
|
label_display_options - np.min(label_display_options),
|
|
114
139
|
)
|
|
115
|
-
axes[
|
|
116
|
-
ax2 = axes[
|
|
140
|
+
axes[1].set_yticklabels([b.name for b in brain_state_set.brain_states])
|
|
141
|
+
ax2 = axes[1].secondary_yaxis("right")
|
|
117
142
|
ax2.set_yticks(
|
|
118
143
|
label_display_options - np.min(label_display_options),
|
|
119
144
|
)
|
|
120
145
|
ax2.set_yticklabels([b.digit for b in brain_state_set.brain_states])
|
|
121
|
-
self.label_img_ref = axes[
|
|
146
|
+
self.label_img_ref = axes[1].imshow(
|
|
122
147
|
label_img, aspect="auto", origin="lower", interpolation="None"
|
|
123
148
|
)
|
|
124
149
|
# add patch to dim the display when creating an ROI
|
|
125
|
-
self.editing_patch = axes[
|
|
150
|
+
self.editing_patch = axes[1].add_patch(
|
|
126
151
|
Rectangle(
|
|
127
152
|
xy=(-0.5, -0.5),
|
|
128
153
|
width=n_epochs,
|
|
@@ -137,7 +162,7 @@ class MplWidget(QtWidgets.QWidget):
|
|
|
137
162
|
)
|
|
138
163
|
# add the ROI selection widget, but disable it until it's needed
|
|
139
164
|
self.roi = RectangleSelector(
|
|
140
|
-
ax=axes[
|
|
165
|
+
ax=axes[1],
|
|
141
166
|
onselect=roi_function,
|
|
142
167
|
interactive=False,
|
|
143
168
|
button=MouseButton(1),
|
|
@@ -145,34 +170,34 @@ class MplWidget(QtWidgets.QWidget):
|
|
|
145
170
|
self.roi.set_active(False)
|
|
146
171
|
# keep a reference to the ROI patch so we can change its color later
|
|
147
172
|
# index 0 is the "editing_patch" created earlier
|
|
148
|
-
self.roi_patch = [c for c in axes[
|
|
173
|
+
self.roi_patch = [c for c in axes[1].get_children() if type(c) is Rectangle][1]
|
|
149
174
|
|
|
150
175
|
# epoch marker subplot
|
|
151
|
-
axes[
|
|
152
|
-
axes[
|
|
176
|
+
axes[2].set_ylim((0, 1))
|
|
177
|
+
axes[2].axis("off")
|
|
153
178
|
self.upper_marker.append(
|
|
154
|
-
axes[
|
|
179
|
+
axes[2].plot([-0.5, epochs_to_show - 0.5], [0.5, 0.5], "r")[0]
|
|
155
180
|
)
|
|
156
|
-
self.upper_marker.append(axes[
|
|
181
|
+
self.upper_marker.append(axes[2].plot([0], [0.5], "rD")[0])
|
|
157
182
|
|
|
158
183
|
# EEG spectrogram subplot
|
|
159
184
|
# select subset of frequencies to show
|
|
160
185
|
f = f[f <= SPEC_UPPER_F]
|
|
161
186
|
spec = spec[0 : len(f), :]
|
|
162
|
-
axes[
|
|
163
|
-
axes[
|
|
187
|
+
axes[3].set_ylabel("EEG", rotation="horizontal", ha="right")
|
|
188
|
+
axes[3].set_yticks(
|
|
164
189
|
np.linspace(
|
|
165
190
|
0,
|
|
166
191
|
len(f),
|
|
167
192
|
1 + round(SPEC_UPPER_F / SPEC_Y_TICK_INTERVAL),
|
|
168
193
|
),
|
|
169
194
|
)
|
|
170
|
-
axes[
|
|
195
|
+
axes[3].set_yticklabels(
|
|
171
196
|
np.arange(0, SPEC_UPPER_F + SPEC_Y_TICK_INTERVAL, SPEC_Y_TICK_INTERVAL)
|
|
172
197
|
)
|
|
173
|
-
axes[
|
|
174
|
-
axes[
|
|
175
|
-
self.spec_ref = axes[
|
|
198
|
+
axes[3].tick_params(axis="both", which="major", labelsize=8)
|
|
199
|
+
axes[3].xaxis.set_major_formatter(mticker.FuncFormatter(self.time_formatter))
|
|
200
|
+
self.spec_ref = axes[3].imshow(
|
|
176
201
|
spec,
|
|
177
202
|
vmin=np.percentile(spec, 2),
|
|
178
203
|
vmax=np.percentile(spec, 98),
|
|
@@ -188,10 +213,10 @@ class MplWidget(QtWidgets.QWidget):
|
|
|
188
213
|
)
|
|
189
214
|
|
|
190
215
|
# EMG subplot
|
|
191
|
-
axes[
|
|
192
|
-
axes[
|
|
193
|
-
axes[
|
|
194
|
-
axes[
|
|
216
|
+
axes[4].set_xticks([])
|
|
217
|
+
axes[4].set_yticks([])
|
|
218
|
+
axes[4].set_ylabel("EMG", rotation="horizontal", ha="right")
|
|
219
|
+
axes[4].plot(
|
|
195
220
|
emg,
|
|
196
221
|
"k",
|
|
197
222
|
linewidth=0.5,
|