accusleepy 0.8.0__py3-none-any.whl → 0.9.2__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/bouts.py +46 -38
- accusleepy/brain_state_set.py +6 -4
- accusleepy/classification.py +14 -50
- accusleepy/constants.py +3 -0
- accusleepy/fileio.py +58 -27
- accusleepy/gui/dialogs.py +40 -0
- accusleepy/gui/images/primary_window.png +0 -0
- accusleepy/gui/main.py +212 -1026
- accusleepy/gui/manual_scoring.py +5 -13
- accusleepy/gui/primary_window.py +7 -9
- accusleepy/gui/primary_window.ui +6 -8
- accusleepy/gui/recording_manager.py +110 -0
- accusleepy/gui/settings_widget.py +409 -0
- accusleepy/gui/text/main_guide.md +1 -1
- accusleepy/models.py +1 -1
- accusleepy/services.py +581 -0
- accusleepy/signal_processing.py +110 -38
- accusleepy/temperature_scaling.py +14 -8
- accusleepy/validation.py +67 -2
- {accusleepy-0.8.0.dist-info → accusleepy-0.9.2.dist-info}/METADATA +3 -5
- {accusleepy-0.8.0.dist-info → accusleepy-0.9.2.dist-info}/RECORD +22 -18
- {accusleepy-0.8.0.dist-info → accusleepy-0.9.2.dist-info}/WHEEL +1 -1
accusleepy/signal_processing.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import os
|
|
2
3
|
import warnings
|
|
3
4
|
|
|
@@ -16,6 +17,7 @@ from accusleepy.constants import (
|
|
|
16
17
|
EMG_COPIES,
|
|
17
18
|
FILENAME_COL,
|
|
18
19
|
LABEL_COL,
|
|
20
|
+
MIN_EPOCHS_PER_STATE,
|
|
19
21
|
MIN_WINDOW_LEN,
|
|
20
22
|
UPPER_FREQ,
|
|
21
23
|
SPECTROGRAM_UPPER_FREQ,
|
|
@@ -25,10 +27,15 @@ from accusleepy.multitaper import spectrogram
|
|
|
25
27
|
|
|
26
28
|
# note: scipy is lazily imported
|
|
27
29
|
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
28
32
|
|
|
29
33
|
def resample(
|
|
30
|
-
eeg: np.
|
|
31
|
-
|
|
34
|
+
eeg: np.ndarray,
|
|
35
|
+
emg: np.ndarray,
|
|
36
|
+
sampling_rate: int | float,
|
|
37
|
+
epoch_length: int | float,
|
|
38
|
+
) -> tuple[np.ndarray, np.ndarray, float]:
|
|
32
39
|
"""Resample recording so that epochs contain equal numbers of samples
|
|
33
40
|
|
|
34
41
|
If the number of samples per epoch is not an integer, epoch-level calculations
|
|
@@ -62,8 +69,11 @@ def resample(
|
|
|
62
69
|
|
|
63
70
|
|
|
64
71
|
def standardize_signal_length(
|
|
65
|
-
eeg: np.
|
|
66
|
-
|
|
72
|
+
eeg: np.ndarray,
|
|
73
|
+
emg: np.ndarray,
|
|
74
|
+
sampling_rate: int | float,
|
|
75
|
+
epoch_length: int | float,
|
|
76
|
+
) -> tuple[np.ndarray, np.ndarray]:
|
|
67
77
|
"""Truncate or pad EEG/EMG signals to have an integer number of epochs
|
|
68
78
|
|
|
69
79
|
:param eeg: EEG signal
|
|
@@ -93,8 +103,11 @@ def standardize_signal_length(
|
|
|
93
103
|
|
|
94
104
|
|
|
95
105
|
def resample_and_standardize(
|
|
96
|
-
eeg: np.
|
|
97
|
-
|
|
106
|
+
eeg: np.ndarray,
|
|
107
|
+
emg: np.ndarray,
|
|
108
|
+
sampling_rate: int | float,
|
|
109
|
+
epoch_length: int | float,
|
|
110
|
+
) -> tuple[np.ndarray, np.ndarray, float]:
|
|
98
111
|
"""Preprocess EEG and EMG signals
|
|
99
112
|
|
|
100
113
|
Adjust the length and sampling rate of the EEG and EMG signals so that
|
|
@@ -117,12 +130,12 @@ def resample_and_standardize(
|
|
|
117
130
|
|
|
118
131
|
|
|
119
132
|
def create_spectrogram(
|
|
120
|
-
eeg: np.
|
|
133
|
+
eeg: np.ndarray,
|
|
121
134
|
sampling_rate: int | float,
|
|
122
135
|
epoch_length: int | float,
|
|
123
|
-
time_bandwidth=2,
|
|
124
|
-
n_tapers=3,
|
|
125
|
-
) ->
|
|
136
|
+
time_bandwidth: int = 2,
|
|
137
|
+
n_tapers: int = 3,
|
|
138
|
+
) -> tuple[np.ndarray, np.ndarray]:
|
|
126
139
|
"""Create an EEG spectrogram image
|
|
127
140
|
|
|
128
141
|
:param eeg: EEG signal
|
|
@@ -168,11 +181,11 @@ def create_spectrogram(
|
|
|
168
181
|
|
|
169
182
|
|
|
170
183
|
def get_emg_power(
|
|
171
|
-
emg: np.
|
|
184
|
+
emg: np.ndarray,
|
|
172
185
|
sampling_rate: int | float,
|
|
173
186
|
epoch_length: int | float,
|
|
174
187
|
emg_filter: EMGFilter,
|
|
175
|
-
) -> np.
|
|
188
|
+
) -> np.ndarray:
|
|
176
189
|
"""Calculate EMG power for each epoch
|
|
177
190
|
|
|
178
191
|
This applies a 20-50 Hz bandpass filter to the EMG, calculates the RMS
|
|
@@ -202,17 +215,18 @@ def get_emg_power(
|
|
|
202
215
|
[round(len(emg) / samples_per_epoch), samples_per_epoch],
|
|
203
216
|
)
|
|
204
217
|
rms = np.sqrt(np.mean(np.power(reshaped, 2), axis=1))
|
|
205
|
-
|
|
206
|
-
|
|
218
|
+
log_rms = np.log(rms)
|
|
219
|
+
log_rms[np.isinf(log_rms)] = 0
|
|
220
|
+
return log_rms
|
|
207
221
|
|
|
208
222
|
|
|
209
223
|
def create_eeg_emg_image(
|
|
210
|
-
eeg: np.
|
|
211
|
-
emg: np.
|
|
224
|
+
eeg: np.ndarray,
|
|
225
|
+
emg: np.ndarray,
|
|
212
226
|
sampling_rate: int | float,
|
|
213
227
|
epoch_length: int | float,
|
|
214
228
|
emg_filter: EMGFilter,
|
|
215
|
-
) -> np.
|
|
229
|
+
) -> np.ndarray:
|
|
216
230
|
"""Stack EEG spectrogram and EMG power into an image
|
|
217
231
|
|
|
218
232
|
This assumes that each epoch contains an integer number of samples and
|
|
@@ -247,8 +261,8 @@ def create_eeg_emg_image(
|
|
|
247
261
|
|
|
248
262
|
|
|
249
263
|
def get_mixture_values(
|
|
250
|
-
img: np.
|
|
251
|
-
) ->
|
|
264
|
+
img: np.ndarray, labels: np.ndarray, brain_state_set: BrainStateSet
|
|
265
|
+
) -> tuple[np.ndarray, np.ndarray]:
|
|
252
266
|
"""Compute weighted feature means and SDs for mixture z-scoring
|
|
253
267
|
|
|
254
268
|
The outputs of this function can be used to standardize features
|
|
@@ -289,12 +303,12 @@ def get_mixture_values(
|
|
|
289
303
|
|
|
290
304
|
|
|
291
305
|
def mixture_z_score_img(
|
|
292
|
-
img: np.
|
|
306
|
+
img: np.ndarray,
|
|
293
307
|
brain_state_set: BrainStateSet,
|
|
294
|
-
labels: np.
|
|
295
|
-
mixture_means: np.
|
|
296
|
-
mixture_sds: np.
|
|
297
|
-
) -> np.
|
|
308
|
+
labels: np.ndarray | None = None,
|
|
309
|
+
mixture_means: np.ndarray | None = None,
|
|
310
|
+
mixture_sds: np.ndarray | None = None,
|
|
311
|
+
) -> tuple[np.ndarray, bool]:
|
|
298
312
|
"""Perform mixture z-scoring on a combined EEG+EMG image
|
|
299
313
|
|
|
300
314
|
If brain state labels are provided, they will be used to calculate
|
|
@@ -307,10 +321,10 @@ def mixture_z_score_img(
|
|
|
307
321
|
:param labels: labels, in "class" format
|
|
308
322
|
:param mixture_means: mixture means
|
|
309
323
|
:param mixture_sds: mixture standard deviations
|
|
310
|
-
:return:
|
|
324
|
+
:return: tuple of (z-scored image, whether zero-variance features were detected)
|
|
311
325
|
"""
|
|
312
326
|
if labels is None and (mixture_means is None or mixture_sds is None):
|
|
313
|
-
raise
|
|
327
|
+
raise ValueError("must provide either labels or mixture means+SDs")
|
|
314
328
|
if labels is not None and ((mixture_means is not None) ^ (mixture_sds is not None)):
|
|
315
329
|
warnings.warn("labels were given, mixture means / SDs will be ignored")
|
|
316
330
|
|
|
@@ -319,14 +333,27 @@ def mixture_z_score_img(
|
|
|
319
333
|
img=img, labels=labels, brain_state_set=brain_state_set
|
|
320
334
|
)
|
|
321
335
|
|
|
336
|
+
# replace zero SDs with epsilon to avoid division by zero
|
|
337
|
+
# This can occur when a feature has no variance (e.g., no EMG signal)
|
|
338
|
+
zero_sd_mask = mixture_sds == 0
|
|
339
|
+
had_zero_variance = np.any(zero_sd_mask)
|
|
340
|
+
if had_zero_variance:
|
|
341
|
+
n_zero = np.sum(zero_sd_mask)
|
|
342
|
+
logger.warning(
|
|
343
|
+
"%s feature(s) have zero variance and will be mapped to neutral values",
|
|
344
|
+
n_zero,
|
|
345
|
+
)
|
|
346
|
+
mixture_sds = mixture_sds.copy()
|
|
347
|
+
mixture_sds[zero_sd_mask] = 1e-10
|
|
348
|
+
|
|
322
349
|
img = ((img.T - mixture_means) / mixture_sds).T
|
|
323
350
|
img = (img + ABS_MAX_Z_SCORE) / (2 * ABS_MAX_Z_SCORE)
|
|
324
351
|
img = np.clip(img, 0, 1)
|
|
325
352
|
|
|
326
|
-
return img
|
|
353
|
+
return img, had_zero_variance
|
|
327
354
|
|
|
328
355
|
|
|
329
|
-
def format_img(img: np.
|
|
356
|
+
def format_img(img: np.ndarray, epochs_per_img: int, add_padding: bool) -> np.ndarray:
|
|
330
357
|
"""Adjust the format of an EEG+EMG image
|
|
331
358
|
|
|
332
359
|
This function converts the values in a combined EEG+EMG image to uint8.
|
|
@@ -369,9 +396,18 @@ def create_training_images(
|
|
|
369
396
|
model_type: str,
|
|
370
397
|
calibration_fraction: float,
|
|
371
398
|
emg_filter: EMGFilter,
|
|
372
|
-
) -> list[int]:
|
|
373
|
-
"""Create training dataset
|
|
374
|
-
|
|
399
|
+
) -> tuple[list[int], np.ndarray, bool]:
|
|
400
|
+
"""Create training dataset and calculate class balance
|
|
401
|
+
|
|
402
|
+
This function creates images that can be used to train the
|
|
403
|
+
SSANN model, as well as files that describe the training data
|
|
404
|
+
(optionally split into training and calibration sets).
|
|
405
|
+
It returns a list of recordings that could not be processed,
|
|
406
|
+
the class balance of the usable training data, and a flag if any
|
|
407
|
+
recordings had features with 0 variance.
|
|
408
|
+
|
|
409
|
+
For each epoch, the model expects an image containing the
|
|
410
|
+
EEG spectrogram and EMG power for several surrounding epochs.
|
|
375
411
|
By default, the current epoch is located in the central column
|
|
376
412
|
of pixels in each image. For real-time scoring applications,
|
|
377
413
|
the current epoch is at the right edge of each image.
|
|
@@ -384,8 +420,7 @@ def create_training_images(
|
|
|
384
420
|
:param model_type: default or real-time
|
|
385
421
|
:param calibration_fraction: fraction of training data to use for calibration
|
|
386
422
|
:param emg_filter: EMG filter parameters
|
|
387
|
-
:return:
|
|
388
|
-
be used to create training images.
|
|
423
|
+
:return: tuple of (failed recording names, training class balance, had zero-variance)
|
|
389
424
|
"""
|
|
390
425
|
# recordings that had to be skipped
|
|
391
426
|
failed_recordings = list()
|
|
@@ -393,9 +428,36 @@ def create_training_images(
|
|
|
393
428
|
filenames = list()
|
|
394
429
|
# all valid labels from all valid recordings
|
|
395
430
|
all_labels = list()
|
|
431
|
+
# track if any recording had zero-variance features
|
|
432
|
+
any_zero_variance = False
|
|
396
433
|
# try to load each recording and create training images
|
|
397
434
|
for i in trange(len(recordings)):
|
|
398
435
|
recording = recordings[i]
|
|
436
|
+
try:
|
|
437
|
+
labels, _ = load_labels(recording.label_file)
|
|
438
|
+
except Exception:
|
|
439
|
+
logger.exception("Could not load labels for recording %s", recording.name)
|
|
440
|
+
failed_recordings.append(recording.name)
|
|
441
|
+
continue
|
|
442
|
+
|
|
443
|
+
# Check that each scored brain state has sufficient observations
|
|
444
|
+
# Ideally, we could use mixture means/SDs from another recording...
|
|
445
|
+
insufficient_labels = False
|
|
446
|
+
for brain_state in brain_state_set.brain_states:
|
|
447
|
+
if brain_state.is_scored:
|
|
448
|
+
count = np.sum(labels == brain_state.digit)
|
|
449
|
+
if count < MIN_EPOCHS_PER_STATE:
|
|
450
|
+
logger.warning(
|
|
451
|
+
"Recording %s can't be used: insufficient labels for class '%s'",
|
|
452
|
+
recording.name,
|
|
453
|
+
brain_state.name,
|
|
454
|
+
)
|
|
455
|
+
failed_recordings.append(recording.name)
|
|
456
|
+
insufficient_labels = True
|
|
457
|
+
break
|
|
458
|
+
if insufficient_labels:
|
|
459
|
+
continue
|
|
460
|
+
|
|
399
461
|
try:
|
|
400
462
|
eeg, emg = load_recording(recording.recording_file)
|
|
401
463
|
sampling_rate = recording.sampling_rate
|
|
@@ -406,14 +468,15 @@ def create_training_images(
|
|
|
406
468
|
epoch_length=epoch_length,
|
|
407
469
|
)
|
|
408
470
|
|
|
409
|
-
labels, _ = load_labels(recording.label_file)
|
|
410
471
|
labels = brain_state_set.convert_digit_to_class(labels)
|
|
411
472
|
img = create_eeg_emg_image(
|
|
412
473
|
eeg, emg, sampling_rate, epoch_length, emg_filter
|
|
413
474
|
)
|
|
414
|
-
img = mixture_z_score_img(
|
|
475
|
+
img, had_zero_variance = mixture_z_score_img(
|
|
415
476
|
img=img, brain_state_set=brain_state_set, labels=labels
|
|
416
477
|
)
|
|
478
|
+
if had_zero_variance:
|
|
479
|
+
any_zero_variance = True
|
|
417
480
|
img = format_img(img=img, epochs_per_img=epochs_per_img, add_padding=True)
|
|
418
481
|
|
|
419
482
|
# the model type determines which epochs are used in each image
|
|
@@ -442,8 +505,10 @@ def create_training_images(
|
|
|
442
505
|
all_labels.append(labels[j])
|
|
443
506
|
Image.fromarray(im).save(os.path.join(output_path, filename))
|
|
444
507
|
|
|
445
|
-
except Exception
|
|
446
|
-
|
|
508
|
+
except Exception:
|
|
509
|
+
logger.exception(
|
|
510
|
+
"Failed to create training images for recording %s", recording.name
|
|
511
|
+
)
|
|
447
512
|
failed_recordings.append(recording.name)
|
|
448
513
|
|
|
449
514
|
annotations = pd.DataFrame({FILENAME_COL: filenames, LABEL_COL: all_labels})
|
|
@@ -460,11 +525,18 @@ def create_training_images(
|
|
|
460
525
|
os.path.join(output_path, CALIBRATION_ANNOTATION_FILENAME),
|
|
461
526
|
index=False,
|
|
462
527
|
)
|
|
528
|
+
training_labels = training_set[LABEL_COL].values
|
|
463
529
|
else:
|
|
464
530
|
# annotation file contains info on all training images
|
|
465
531
|
annotations.to_csv(
|
|
466
532
|
os.path.join(output_path, ANNOTATIONS_FILENAME),
|
|
467
533
|
index=False,
|
|
468
534
|
)
|
|
535
|
+
training_labels = np.array(all_labels)
|
|
536
|
+
|
|
537
|
+
# compute class balance from training set
|
|
538
|
+
class_counts = np.bincount(training_labels, minlength=brain_state_set.n_classes)
|
|
539
|
+
training_class_balance = class_counts / class_counts.sum()
|
|
540
|
+
logger.info("Training set class balance: %s", training_class_balance)
|
|
469
541
|
|
|
470
|
-
return failed_recordings
|
|
542
|
+
return failed_recordings, training_class_balance, any_zero_variance
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
1
3
|
import numpy as np
|
|
2
4
|
import torch
|
|
3
5
|
from torch import nn, optim
|
|
4
6
|
from torch.nn import functional as F
|
|
5
7
|
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
6
10
|
|
|
7
11
|
class ModelWithTemperature(nn.Module):
|
|
8
12
|
"""
|
|
@@ -72,9 +76,10 @@ class ModelWithTemperature(nn.Module):
|
|
|
72
76
|
# Calculate NLL and ECE before temperature scaling
|
|
73
77
|
before_temperature_nll = nll_criterion(logits, labels).item()
|
|
74
78
|
before_temperature_ece = ece_criterion(logits, labels).item()
|
|
75
|
-
|
|
76
|
-
"Before temperature - NLL: %.3f, ECE: %.3f"
|
|
77
|
-
|
|
79
|
+
logger.info(
|
|
80
|
+
"Before temperature - NLL: %.3f, ECE: %.3f",
|
|
81
|
+
before_temperature_nll,
|
|
82
|
+
before_temperature_ece,
|
|
78
83
|
)
|
|
79
84
|
|
|
80
85
|
# Next: optimize the temperature w.r.t. NLL
|
|
@@ -96,16 +101,17 @@ class ModelWithTemperature(nn.Module):
|
|
|
96
101
|
after_temperature_ece = ece_criterion(
|
|
97
102
|
self.temperature_scale(logits), labels
|
|
98
103
|
).item()
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
"After temperature - NLL: %.3f, ECE: %.3f"
|
|
102
|
-
|
|
104
|
+
logger.info("Optimal temperature: %.3f", self.temperature.item())
|
|
105
|
+
logger.info(
|
|
106
|
+
"After temperature - NLL: %.3f, ECE: %.3f",
|
|
107
|
+
after_temperature_nll,
|
|
108
|
+
after_temperature_ece,
|
|
103
109
|
)
|
|
104
110
|
|
|
105
111
|
val_acc = round(
|
|
106
112
|
100 * np.mean(labels.cpu().numpy() == predictions.cpu().numpy()), 2
|
|
107
113
|
)
|
|
108
|
-
|
|
114
|
+
logger.info("Validation accuracy: %s%%", val_acc)
|
|
109
115
|
|
|
110
116
|
return self
|
|
111
117
|
|
accusleepy/validation.py
CHANGED
|
@@ -7,8 +7,8 @@ LABEL_LENGTH_ERROR = "label file length does not match recording length"
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def check_label_validity(
|
|
10
|
-
labels: np.
|
|
11
|
-
confidence_scores: np.
|
|
10
|
+
labels: np.ndarray,
|
|
11
|
+
confidence_scores: np.ndarray | None,
|
|
12
12
|
samples_in_recording: int,
|
|
13
13
|
sampling_rate: int | float,
|
|
14
14
|
epoch_length: int | float,
|
|
@@ -46,6 +46,71 @@ def check_label_validity(
|
|
|
46
46
|
return None
|
|
47
47
|
|
|
48
48
|
|
|
49
|
+
def validate_and_correct_labels(
|
|
50
|
+
labels: np.ndarray,
|
|
51
|
+
confidence_scores: np.ndarray | None,
|
|
52
|
+
samples_in_recording: int,
|
|
53
|
+
sampling_rate: int | float,
|
|
54
|
+
epoch_length: int | float,
|
|
55
|
+
brain_state_set: BrainStateSet,
|
|
56
|
+
) -> tuple[np.ndarray | None, np.ndarray | None, str | None]:
|
|
57
|
+
"""Validate labels and attempt to correct minor length mismatches.
|
|
58
|
+
|
|
59
|
+
If the label array is off by exactly one epoch, it will be padded or
|
|
60
|
+
truncated and a warning will be returned.
|
|
61
|
+
|
|
62
|
+
:param labels: brain state labels
|
|
63
|
+
:param confidence_scores: optional confidence scores
|
|
64
|
+
:param samples_in_recording: number of samples in the recording
|
|
65
|
+
:param sampling_rate: sampling rate in Hz
|
|
66
|
+
:param epoch_length: epoch length in seconds
|
|
67
|
+
:param brain_state_set: set of brain state options
|
|
68
|
+
:return: (labels, confidence_scores, message) - if labels is None,
|
|
69
|
+
message is an error. Otherwise, any message is a warning
|
|
70
|
+
"""
|
|
71
|
+
label_error = check_label_validity(
|
|
72
|
+
labels=labels,
|
|
73
|
+
confidence_scores=confidence_scores,
|
|
74
|
+
samples_in_recording=samples_in_recording,
|
|
75
|
+
sampling_rate=sampling_rate,
|
|
76
|
+
epoch_length=epoch_length,
|
|
77
|
+
brain_state_set=brain_state_set,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if not label_error:
|
|
81
|
+
return labels, confidence_scores, None
|
|
82
|
+
|
|
83
|
+
# If length is off by one, try to correct it
|
|
84
|
+
if label_error == LABEL_LENGTH_ERROR:
|
|
85
|
+
samples_per_epoch = round(sampling_rate * epoch_length)
|
|
86
|
+
epochs_in_recording = round(samples_in_recording / samples_per_epoch)
|
|
87
|
+
|
|
88
|
+
if epochs_in_recording - labels.size == 1:
|
|
89
|
+
# Pad with one undefined label
|
|
90
|
+
labels = np.concatenate((labels, np.array([UNDEFINED_LABEL])))
|
|
91
|
+
if confidence_scores is not None:
|
|
92
|
+
confidence_scores = np.concatenate((confidence_scores, np.array([0])))
|
|
93
|
+
return (
|
|
94
|
+
labels,
|
|
95
|
+
confidence_scores,
|
|
96
|
+
"An undefined epoch was added to the label file to correct its length.",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if labels.size - epochs_in_recording == 1:
|
|
100
|
+
# Truncate by one label
|
|
101
|
+
labels = labels[:-1]
|
|
102
|
+
if confidence_scores is not None:
|
|
103
|
+
confidence_scores = confidence_scores[:-1]
|
|
104
|
+
return (
|
|
105
|
+
labels,
|
|
106
|
+
confidence_scores,
|
|
107
|
+
"The last epoch was removed from the label file to correct its length.",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# error could not be fixed
|
|
111
|
+
return None, None, label_error
|
|
112
|
+
|
|
113
|
+
|
|
49
114
|
def check_config_consistency(
|
|
50
115
|
current_brain_states: dict,
|
|
51
116
|
model_brain_states: dict,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: accusleepy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.2
|
|
4
4
|
Summary: Python implementation of AccuSleep
|
|
5
5
|
License: GPL-3.0-only
|
|
6
6
|
Author: Zeke Barger
|
|
@@ -80,6 +80,7 @@ please consult the [developer guide](accusleepy/gui/text/dev_guide.md).
|
|
|
80
80
|
|
|
81
81
|
## Changelog
|
|
82
82
|
|
|
83
|
+
- 0.8.1-0.9.2: Improved error handling and code quality
|
|
83
84
|
- 0.8.0: More configurable settings, visual improvements
|
|
84
85
|
- 0.7.1-0.7.3: Bugfixes, code cleanup
|
|
85
86
|
- 0.7.0: More settings can be configured in the UI
|
|
@@ -87,10 +88,7 @@ please consult the [developer guide](accusleepy/gui/text/dev_guide.md).
|
|
|
87
88
|
since the new calibration feature will make the confidence scores more accurate.
|
|
88
89
|
- 0.5.0: Performance improvements
|
|
89
90
|
- 0.4.5: Added support for python 3.13, **removed support for python 3.10.**
|
|
90
|
-
- 0.4.4:
|
|
91
|
-
- 0.4.3: Improved unit tests and user manuals
|
|
92
|
-
- 0.4.0: Improved visuals and user manuals
|
|
93
|
-
- 0.1.0-0.3.1: Early development versions
|
|
91
|
+
- 0.1.0-0.4.4: Early development versions
|
|
94
92
|
|
|
95
93
|
## Screenshots
|
|
96
94
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
accusleepy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
accusleepy/__main__.py,sha256=dKzl2N2Hg9lD264CWYNxThRyDKzWwyMwHRXmJxOmMis,104
|
|
3
|
-
accusleepy/bouts.py,sha256=
|
|
4
|
-
accusleepy/brain_state_set.py,sha256=
|
|
5
|
-
accusleepy/classification.py,sha256=
|
|
3
|
+
accusleepy/bouts.py,sha256=GVYOSdsQjHzYirRRSFlie8w5iJq74umyGNMRVsQlpK0,6045
|
|
4
|
+
accusleepy/brain_state_set.py,sha256=i9RUwSMKG21AjromjOJYpsHkip3elTFhHYntMguBF6Q,3273
|
|
5
|
+
accusleepy/classification.py,sha256=UJ48QamTvqwqBwu-r4weSno-9TQyPFCk62nbUc5_iKA,8672
|
|
6
6
|
accusleepy/config.json,sha256=VmUFsiGD1ymEyjdzqeM5nTp8jWvDI-DIxLy1_92nueo,875
|
|
7
|
-
accusleepy/constants.py,sha256=
|
|
8
|
-
accusleepy/fileio.py,sha256=
|
|
7
|
+
accusleepy/constants.py,sha256=y3tvYxJy3M1zycOPVfXLpvhLVdyd7s0dSlokgKG7mUE,3127
|
|
8
|
+
accusleepy/fileio.py,sha256=A-ttN-N3wsEHrU89NvUYb5osuMK1HMmPecCDgtPTiqo,9076
|
|
9
9
|
accusleepy/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
accusleepy/gui/dialogs.py,sha256=DqkarJKgRovTuDDJhqPaw_o8ZfopuR7R6M8C49QWZu0,1230
|
|
10
11
|
accusleepy/gui/icons/brightness_down.png,sha256=PLT1fb83RHIhSRuU7MMMx0G7oJAY7o9wUcnqM8veZfM,12432
|
|
11
12
|
accusleepy/gui/icons/brightness_up.png,sha256=64GnUqgPvN5xZ6Um3wOzwqvUmdAWYZT6eFmWpBsHyks,12989
|
|
12
13
|
accusleepy/gui/icons/double_down_arrow.png,sha256=fGiPIP7_RJ3UAonNhORFVX0emXEmtzRlHI3Tfjai064,4964
|
|
@@ -18,26 +19,29 @@ accusleepy/gui/icons/save.png,sha256=J3EA8iU1BqLYRSsrq_OdoZlqrv2yfL7oV54DklTy_DI
|
|
|
18
19
|
accusleepy/gui/icons/up_arrow.png,sha256=V9yF9t1WgjPaUu-mF1YGe_DfaRHg2dUpR_sUVVcvVvY,3329
|
|
19
20
|
accusleepy/gui/icons/zoom_in.png,sha256=MFWnKZp7Rvh4bLPq4Cqo4sB_jQYedUUtT8-ZO8tNYyc,13589
|
|
20
21
|
accusleepy/gui/icons/zoom_out.png,sha256=IB8Jecb3i0U4qjWRR46ridjLpvLCSe7PozBaLqQqYSw,13055
|
|
21
|
-
accusleepy/gui/images/primary_window.png,sha256=
|
|
22
|
+
accusleepy/gui/images/primary_window.png,sha256=hO28hOOoQmBljED1UDQES1ZxaAq8431scOzHemKUpCA,168073
|
|
22
23
|
accusleepy/gui/images/viewer_window.png,sha256=b_B7m9WSLMAOzNjctq76SyekO1WfC6qYZVNnYfhjPe8,977197
|
|
23
24
|
accusleepy/gui/images/viewer_window_annotated.png,sha256=uMNUmsZIdzDlQpyoiS3lJGoWlg_T325Oj5hDZhM3Y14,146817
|
|
24
|
-
accusleepy/gui/main.py,sha256=
|
|
25
|
-
accusleepy/gui/manual_scoring.py,sha256=
|
|
25
|
+
accusleepy/gui/main.py,sha256=jtbeF1IXwCBDzpLTc_fFamkfDuNDc9ElotSLYPedWew,25094
|
|
26
|
+
accusleepy/gui/manual_scoring.py,sha256=9Lom83nPqSr_nbudgPQ92rEOUBt-s9dh2ExcL9-dHRo,40698
|
|
26
27
|
accusleepy/gui/mplwidget.py,sha256=rJSTtWmLjHn8r3c9Kb23Rc4XzXl3i9B-JrjNjjlNnmQ,13492
|
|
27
|
-
accusleepy/gui/primary_window.py,sha256
|
|
28
|
-
accusleepy/gui/primary_window.ui,sha256=
|
|
28
|
+
accusleepy/gui/primary_window.py,sha256=-CbQ_dn3svE7J1VCGjTFDBCC4Q1DcV7Q7kz3UX3Kwhw,141613
|
|
29
|
+
accusleepy/gui/primary_window.ui,sha256=WmeY7L9VyuiNRx3kSM56kPv5t6RDBVXlte8kT5wH-2s,208461
|
|
30
|
+
accusleepy/gui/recording_manager.py,sha256=0lYrGu-7gX0xpLxBBcn4-TcuSy8CsmiPBJ3l_fZa158,3916
|
|
29
31
|
accusleepy/gui/resources.qrc,sha256=wqPendnTLAuKfVI6v2lKHiRqAWM0oaz2ZuF5cucJdS4,803
|
|
30
32
|
accusleepy/gui/resources_rc.py,sha256=Z2e34h30U4snJjnYdZVV9B6yjATKxxfvgTRt5uXtQdo,329727
|
|
33
|
+
accusleepy/gui/settings_widget.py,sha256=OG9DojTnbHVqJjE0Nk-OVU5c4UlHv7vA94H_dFJB-Jc,16824
|
|
31
34
|
accusleepy/gui/text/dev_guide.md,sha256=PgOXfGvN17fCtnsfGvPhrhK4FUWFGP_TsHA6t9skP3U,2060
|
|
32
|
-
accusleepy/gui/text/main_guide.md,sha256=
|
|
35
|
+
accusleepy/gui/text/main_guide.md,sha256=QO0isKO-fqI-m062NY7k56fmBMNAV_ZFFNvE6ugCMi8,8289
|
|
33
36
|
accusleepy/gui/text/manual_scoring_guide.md,sha256=ow_RMSjFy05NupEDSCuJtu-V65-BPnIkrZqtssFoZCQ,999
|
|
34
37
|
accusleepy/gui/viewer_window.py,sha256=jysFw7C_Tr7mtK1XNWhIpHblBvatwduE3RF2GP4lrro,24479
|
|
35
38
|
accusleepy/gui/viewer_window.ui,sha256=a89iVLk1sJg9N6ZvWAV6YNPStb2Tm4-rs-W7TmIDkb4,31658
|
|
36
|
-
accusleepy/models.py,sha256=
|
|
39
|
+
accusleepy/models.py,sha256=MHMsC9sYfR3fTOQLh4COyjCZo9q2vaX1FmpTHaaVtM4,3516
|
|
37
40
|
accusleepy/multitaper.py,sha256=D5-iglwkFBRciL5tKSNcunMtcq0rM3zHwRHUVPgem1U,25679
|
|
38
|
-
accusleepy/
|
|
39
|
-
accusleepy/
|
|
40
|
-
accusleepy/
|
|
41
|
-
accusleepy
|
|
42
|
-
accusleepy-0.
|
|
43
|
-
accusleepy-0.
|
|
41
|
+
accusleepy/services.py,sha256=4xl9uWDaJXIpJTYH08mSgipkkEYM9fDLIcMj7-k9qf0,20228
|
|
42
|
+
accusleepy/signal_processing.py,sha256=k3a2IKchok3-38P7T0XHNpGTiWKBqWtQm7SHwFEkTNA,19973
|
|
43
|
+
accusleepy/temperature_scaling.py,sha256=RXsbDLT5JDrjPbz5ZdFXKj6NxpQCHpq5BzcLBzsUJKc,5848
|
|
44
|
+
accusleepy/validation.py,sha256=h3iHVW71AaUC4lp1KMQXo5j81bS-IuYbTZWRSIkmHWE,6933
|
|
45
|
+
accusleepy-0.9.2.dist-info/METADATA,sha256=50NVD9h3oIMy-R9WLVypyNO-KiGxHF8eeUzjvgWtUJ0,4733
|
|
46
|
+
accusleepy-0.9.2.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
|
|
47
|
+
accusleepy-0.9.2.dist-info/RECORD,,
|