accusleepy 0.7.0__py3-none-any.whl → 0.7.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/constants.py +18 -2
- accusleepy/gui/images/primary_window.png +0 -0
- accusleepy/gui/main.py +12 -144
- accusleepy/gui/manual_scoring.py +33 -43
- accusleepy/gui/primary_window.py +33 -10
- accusleepy/gui/primary_window.ui +19 -4
- accusleepy/models.py +1 -12
- accusleepy/signal_processing.py +2 -6
- accusleepy/validation.py +128 -0
- {accusleepy-0.7.0.dist-info → accusleepy-0.7.2.dist-info}/METADATA +6 -4
- {accusleepy-0.7.0.dist-info → accusleepy-0.7.2.dist-info}/RECORD +12 -11
- {accusleepy-0.7.0.dist-info → accusleepy-0.7.2.dist-info}/WHEEL +0 -0
accusleepy/constants.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
1
3
|
# probably don't change these unless you really need to
|
|
2
4
|
UNDEFINED_LABEL = -1 # can't be the same as a brain state's digit, must be an integer
|
|
3
5
|
# calibration file columns
|
|
@@ -9,9 +11,16 @@ EMG_COL = "emg"
|
|
|
9
11
|
# label file columns
|
|
10
12
|
BRAIN_STATE_COL = "brain_state"
|
|
11
13
|
CONFIDENCE_SCORE_COL = "confidence_score"
|
|
14
|
+
# max number of messages to store in main window message box
|
|
15
|
+
MESSAGE_BOX_MAX_DEPTH = 200
|
|
16
|
+
# clip mixture z-scores above and below this level
|
|
17
|
+
# in the matlab implementation, 4.5 was used
|
|
18
|
+
ABS_MAX_Z_SCORE = 3.5
|
|
19
|
+
# upper frequency limit when generating EEG spectrograms
|
|
20
|
+
SPECTROGRAM_UPPER_FREQ = 64
|
|
12
21
|
|
|
13
22
|
|
|
14
|
-
#
|
|
23
|
+
# very unlikely you will want to change values from here onwards
|
|
15
24
|
# config file location
|
|
16
25
|
CONFIG_FILE = "config.json"
|
|
17
26
|
# number of times to include the EMG power in a training image
|
|
@@ -20,8 +29,15 @@ EMG_COPIES = 9
|
|
|
20
29
|
MIN_WINDOW_LEN = 5
|
|
21
30
|
# frequency above which to downsample EEG spectrograms
|
|
22
31
|
DOWNSAMPLING_START_FREQ = 20
|
|
23
|
-
#
|
|
32
|
+
# highest EEG frequency used as model input
|
|
24
33
|
UPPER_FREQ = 50
|
|
34
|
+
# height in pixels of each training image
|
|
35
|
+
IMAGE_HEIGHT = (
|
|
36
|
+
len(np.arange(0, DOWNSAMPLING_START_FREQ, 1 / MIN_WINDOW_LEN))
|
|
37
|
+
+ len(np.arange(DOWNSAMPLING_START_FREQ, UPPER_FREQ, 2 / MIN_WINDOW_LEN))
|
|
38
|
+
+ EMG_COPIES
|
|
39
|
+
)
|
|
40
|
+
|
|
25
41
|
# classification model types
|
|
26
42
|
DEFAULT_MODEL_TYPE = "default" # current epoch is centered
|
|
27
43
|
REAL_TIME_MODEL_TYPE = "real-time" # current epoch on the right
|
|
Binary file
|
accusleepy/gui/main.py
CHANGED
|
@@ -47,6 +47,7 @@ from accusleepy.constants import (
|
|
|
47
47
|
DEFAULT_MOMENTUM,
|
|
48
48
|
DEFAULT_TRAINING_EPOCHS,
|
|
49
49
|
LABEL_FILE_TYPE,
|
|
50
|
+
MESSAGE_BOX_MAX_DEPTH,
|
|
50
51
|
MODEL_FILE_TYPE,
|
|
51
52
|
REAL_TIME_MODEL_TYPE,
|
|
52
53
|
RECORDING_FILE_TYPES,
|
|
@@ -72,14 +73,15 @@ from accusleepy.signal_processing import (
|
|
|
72
73
|
create_training_images,
|
|
73
74
|
resample_and_standardize,
|
|
74
75
|
)
|
|
76
|
+
from accusleepy.validation import (
|
|
77
|
+
check_label_validity,
|
|
78
|
+
LABEL_LENGTH_ERROR,
|
|
79
|
+
check_config_consistency,
|
|
80
|
+
)
|
|
75
81
|
|
|
76
82
|
# note: functions using torch or scipy are lazily imported
|
|
77
83
|
|
|
78
|
-
#
|
|
79
|
-
MESSAGE_BOX_MAX_DEPTH = 200
|
|
80
|
-
LABEL_LENGTH_ERROR = "label file length does not match recording length"
|
|
81
|
-
# relative path to config guide txt file
|
|
82
|
-
CONFIG_GUIDE_FILE = os.path.normpath(r"text/config_guide.txt")
|
|
84
|
+
# relative path to user manual
|
|
83
85
|
MAIN_GUIDE_FILE = os.path.normpath(r"text/main_guide.md")
|
|
84
86
|
|
|
85
87
|
|
|
@@ -219,10 +221,9 @@ class AccuSleepWindow(QMainWindow):
|
|
|
219
221
|
|
|
220
222
|
:param default_selected: whether default option is selected
|
|
221
223
|
"""
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
self.model_type = REAL_TIME_MODEL_TYPE
|
|
224
|
+
self.model_type = (
|
|
225
|
+
DEFAULT_MODEL_TYPE if default_selected else REAL_TIME_MODEL_TYPE
|
|
226
|
+
)
|
|
226
227
|
|
|
227
228
|
def export_recording_list(self) -> None:
|
|
228
229
|
"""Save current list of recordings to file"""
|
|
@@ -1279,11 +1280,11 @@ class AccuSleepWindow(QMainWindow):
|
|
|
1279
1280
|
state.enabled_widget.stateChanged.connect(
|
|
1280
1281
|
partial(self.set_brain_state_enabled, digit)
|
|
1281
1282
|
)
|
|
1282
|
-
state.name_widget.editingFinished.connect(self.
|
|
1283
|
+
state.name_widget.editingFinished.connect(self.check_config_validity)
|
|
1283
1284
|
state.is_scored_widget.stateChanged.connect(
|
|
1284
1285
|
partial(self.is_scored_changed, digit)
|
|
1285
1286
|
)
|
|
1286
|
-
state.frequency_widget.valueChanged.connect(self.
|
|
1287
|
+
state.frequency_widget.valueChanged.connect(self.check_config_validity)
|
|
1287
1288
|
|
|
1288
1289
|
def set_brain_state_enabled(self, digit, e) -> None:
|
|
1289
1290
|
"""Called when user clicks "enabled" checkbox
|
|
@@ -1309,17 +1310,6 @@ class AccuSleepWindow(QMainWindow):
|
|
|
1309
1310
|
# check that configuration is valid
|
|
1310
1311
|
_ = self.check_config_validity()
|
|
1311
1312
|
|
|
1312
|
-
def finished_editing_state_name(self) -> None:
|
|
1313
|
-
"""Called when user finishes editing a brain state's name"""
|
|
1314
|
-
_ = self.check_config_validity()
|
|
1315
|
-
|
|
1316
|
-
def state_frequency_changed(self, new_value) -> None:
|
|
1317
|
-
"""Called when user edits a brain state's frequency
|
|
1318
|
-
|
|
1319
|
-
:param new_value: unused
|
|
1320
|
-
"""
|
|
1321
|
-
_ = self.check_config_validity()
|
|
1322
|
-
|
|
1323
1313
|
def is_scored_changed(self, digit, e) -> None:
|
|
1324
1314
|
"""Called when user sets whether a state is scored
|
|
1325
1315
|
|
|
@@ -1464,128 +1454,6 @@ class AccuSleepWindow(QMainWindow):
|
|
|
1464
1454
|
self.ui.training_epochs_spinbox.setValue(DEFAULT_TRAINING_EPOCHS)
|
|
1465
1455
|
|
|
1466
1456
|
|
|
1467
|
-
def check_label_validity(
|
|
1468
|
-
labels: np.array,
|
|
1469
|
-
confidence_scores: np.array,
|
|
1470
|
-
samples_in_recording: int,
|
|
1471
|
-
sampling_rate: int | float,
|
|
1472
|
-
epoch_length: int | float,
|
|
1473
|
-
brain_state_set: BrainStateSet,
|
|
1474
|
-
) -> str | None:
|
|
1475
|
-
"""Check whether a set of brain state labels is valid
|
|
1476
|
-
|
|
1477
|
-
This returns an error message if a problem is found with the
|
|
1478
|
-
brain state labels.
|
|
1479
|
-
|
|
1480
|
-
:param labels: brain state labels
|
|
1481
|
-
:param confidence_scores: confidence scores
|
|
1482
|
-
:param samples_in_recording: number of samples in the recording
|
|
1483
|
-
:param sampling_rate: sampling rate, in Hz
|
|
1484
|
-
:param epoch_length: epoch length, in seconds
|
|
1485
|
-
:param brain_state_set: BrainStateMapper object
|
|
1486
|
-
:return: error message
|
|
1487
|
-
"""
|
|
1488
|
-
# check that number of labels is correct
|
|
1489
|
-
samples_per_epoch = round(sampling_rate * epoch_length)
|
|
1490
|
-
epochs_in_recording = round(samples_in_recording / samples_per_epoch)
|
|
1491
|
-
if epochs_in_recording != labels.size:
|
|
1492
|
-
return LABEL_LENGTH_ERROR
|
|
1493
|
-
|
|
1494
|
-
# check that entries are valid
|
|
1495
|
-
if not set(labels.tolist()).issubset(
|
|
1496
|
-
set([b.digit for b in brain_state_set.brain_states] + [UNDEFINED_LABEL])
|
|
1497
|
-
):
|
|
1498
|
-
return "label file contains invalid entries"
|
|
1499
|
-
|
|
1500
|
-
if confidence_scores is not None:
|
|
1501
|
-
if np.min(confidence_scores) < 0 or np.max(confidence_scores) > 1:
|
|
1502
|
-
return "label file contains invalid confidence scores"
|
|
1503
|
-
|
|
1504
|
-
return None
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
def check_config_consistency(
|
|
1508
|
-
current_brain_states: dict,
|
|
1509
|
-
model_brain_states: dict,
|
|
1510
|
-
current_epoch_length: int | float,
|
|
1511
|
-
model_epoch_length: int | float,
|
|
1512
|
-
) -> list[str]:
|
|
1513
|
-
"""Compare current brain state config to the model's config
|
|
1514
|
-
|
|
1515
|
-
This only displays warnings - the user should decide whether to proceed
|
|
1516
|
-
|
|
1517
|
-
:param current_brain_states: current brain state config
|
|
1518
|
-
:param model_brain_states: brain state config when the model was created
|
|
1519
|
-
:param current_epoch_length: current epoch length setting
|
|
1520
|
-
:param model_epoch_length: epoch length used when the model was created
|
|
1521
|
-
"""
|
|
1522
|
-
output = list()
|
|
1523
|
-
|
|
1524
|
-
# make lists of names and digits for scored brain states
|
|
1525
|
-
current_scored_states = {
|
|
1526
|
-
f: [b[f] for b in current_brain_states if b["is_scored"]]
|
|
1527
|
-
for f in ["name", "digit"]
|
|
1528
|
-
}
|
|
1529
|
-
model_scored_states = {
|
|
1530
|
-
f: [b[f] for b in model_brain_states if b["is_scored"]]
|
|
1531
|
-
for f in ["name", "digit"]
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
|
-
# generate message comparing the brain state configs
|
|
1535
|
-
config_comparisons = list()
|
|
1536
|
-
for config, config_name in zip(
|
|
1537
|
-
[current_scored_states, model_scored_states], ["current", "model's"]
|
|
1538
|
-
):
|
|
1539
|
-
config_comparisons.append(
|
|
1540
|
-
f"Scored brain states in {config_name} configuration: "
|
|
1541
|
-
f"""{
|
|
1542
|
-
", ".join(
|
|
1543
|
-
[
|
|
1544
|
-
f"{x}: {y}"
|
|
1545
|
-
for x, y in zip(
|
|
1546
|
-
config["digit"],
|
|
1547
|
-
config["name"],
|
|
1548
|
-
)
|
|
1549
|
-
]
|
|
1550
|
-
)
|
|
1551
|
-
}"""
|
|
1552
|
-
)
|
|
1553
|
-
|
|
1554
|
-
# check if the number of scored states is different
|
|
1555
|
-
len_diff = len(current_scored_states["name"]) - len(model_scored_states["name"])
|
|
1556
|
-
if len_diff != 0:
|
|
1557
|
-
output.append(
|
|
1558
|
-
(
|
|
1559
|
-
"WARNING: current brain state configuration has "
|
|
1560
|
-
f"{'fewer' if len_diff < 0 else 'more'} "
|
|
1561
|
-
"scored brain states than the model's configuration."
|
|
1562
|
-
)
|
|
1563
|
-
)
|
|
1564
|
-
output = output + config_comparisons
|
|
1565
|
-
else:
|
|
1566
|
-
# the length is the same, but names might be different
|
|
1567
|
-
if current_scored_states["name"] != model_scored_states["name"]:
|
|
1568
|
-
output.append(
|
|
1569
|
-
(
|
|
1570
|
-
"WARNING: current brain state configuration appears "
|
|
1571
|
-
"to contain different brain states than "
|
|
1572
|
-
"the model's configuration."
|
|
1573
|
-
)
|
|
1574
|
-
)
|
|
1575
|
-
output = output + config_comparisons
|
|
1576
|
-
|
|
1577
|
-
if current_epoch_length != model_epoch_length:
|
|
1578
|
-
output.append(
|
|
1579
|
-
(
|
|
1580
|
-
"Warning: the epoch length used when training this model "
|
|
1581
|
-
f"({model_epoch_length} seconds) "
|
|
1582
|
-
"does not match the current epoch length setting."
|
|
1583
|
-
)
|
|
1584
|
-
)
|
|
1585
|
-
|
|
1586
|
-
return output
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
1457
|
def run_primary_window() -> None:
|
|
1590
1458
|
app = QApplication(sys.argv)
|
|
1591
1459
|
AccuSleepWindow()
|
accusleepy/gui/manual_scoring.py
CHANGED
|
@@ -79,6 +79,12 @@ UNDEFINED_STATE = "undefined"
|
|
|
79
79
|
SCROLL_BOUNDARY = 0.35
|
|
80
80
|
# max number of sequential undo actions allowed
|
|
81
81
|
UNDO_LIMIT = 1000
|
|
82
|
+
# brightness scaling factors for the spectrogram
|
|
83
|
+
BRIGHTER_SCALE_FACTOR = 0.96
|
|
84
|
+
DIMMER_SCALE_FACTOR = 1.07
|
|
85
|
+
# zoom factor for upper plots
|
|
86
|
+
ZOOM_IN_FACTOR = 0.45
|
|
87
|
+
ZOOM_OUT_FACTOR = 1.017
|
|
82
88
|
|
|
83
89
|
|
|
84
90
|
@dataclass
|
|
@@ -232,23 +238,6 @@ class ManualScoringWindow(QDialog):
|
|
|
232
238
|
keypress_zoom_out_x = QShortcut(QKeySequence(Qt.Key.Key_Minus), self)
|
|
233
239
|
keypress_zoom_out_x.activated.connect(partial(self.zoom_x, ZOOM_OUT))
|
|
234
240
|
|
|
235
|
-
keypress_modify_label = list()
|
|
236
|
-
for brain_state in self.brain_state_set.brain_states:
|
|
237
|
-
keypress_modify_label.append(
|
|
238
|
-
QShortcut(
|
|
239
|
-
QKeySequence(Qt.Key[f"Key_{brain_state.digit}"]),
|
|
240
|
-
self,
|
|
241
|
-
)
|
|
242
|
-
)
|
|
243
|
-
keypress_modify_label[-1].activated.connect(
|
|
244
|
-
partial(self.modify_current_epoch_label, brain_state.digit)
|
|
245
|
-
)
|
|
246
|
-
|
|
247
|
-
keypress_delete_label = QShortcut(QKeySequence(Qt.Key.Key_Backspace), self)
|
|
248
|
-
keypress_delete_label.activated.connect(
|
|
249
|
-
partial(self.modify_current_epoch_label, UNDEFINED_LABEL)
|
|
250
|
-
)
|
|
251
|
-
|
|
252
241
|
keypress_quit = QShortcut(
|
|
253
242
|
QKeySequence(QKeyCombination(Qt.Modifier.CTRL, Qt.Key.Key_W)),
|
|
254
243
|
self,
|
|
@@ -261,36 +250,37 @@ class ManualScoringWindow(QDialog):
|
|
|
261
250
|
)
|
|
262
251
|
keypress_save.activated.connect(self.save)
|
|
263
252
|
|
|
253
|
+
keypress_modify_label = list()
|
|
264
254
|
keypress_roi = list()
|
|
265
|
-
|
|
255
|
+
digit_key_label_pairs = [
|
|
256
|
+
(Qt.Key[f"Key_{brain_state.digit}"], brain_state.digit)
|
|
257
|
+
for brain_state in self.brain_state_set.brain_states
|
|
258
|
+
] + [(Qt.Key.Key_Backspace, UNDEFINED_LABEL)]
|
|
259
|
+
|
|
260
|
+
for digit_key, digit_label in digit_key_label_pairs:
|
|
261
|
+
keypress_modify_label.append(
|
|
262
|
+
QShortcut(
|
|
263
|
+
QKeySequence(digit_key),
|
|
264
|
+
self,
|
|
265
|
+
)
|
|
266
|
+
)
|
|
267
|
+
keypress_modify_label[-1].activated.connect(
|
|
268
|
+
partial(self.modify_current_epoch_label, digit_label)
|
|
269
|
+
)
|
|
266
270
|
keypress_roi.append(
|
|
267
271
|
QShortcut(
|
|
268
272
|
QKeySequence(
|
|
269
273
|
QKeyCombination(
|
|
270
274
|
Qt.Modifier.SHIFT,
|
|
271
|
-
|
|
275
|
+
digit_key,
|
|
272
276
|
)
|
|
273
277
|
),
|
|
274
278
|
self,
|
|
275
279
|
)
|
|
276
280
|
)
|
|
277
281
|
keypress_roi[-1].activated.connect(
|
|
278
|
-
partial(self.enter_label_roi_mode,
|
|
279
|
-
)
|
|
280
|
-
keypress_roi.append(
|
|
281
|
-
QShortcut(
|
|
282
|
-
QKeySequence(
|
|
283
|
-
QKeyCombination(
|
|
284
|
-
Qt.Modifier.SHIFT,
|
|
285
|
-
Qt.Key.Key_Backspace,
|
|
286
|
-
)
|
|
287
|
-
),
|
|
288
|
-
self,
|
|
282
|
+
partial(self.enter_label_roi_mode, digit_label)
|
|
289
283
|
)
|
|
290
|
-
)
|
|
291
|
-
keypress_roi[-1].activated.connect(
|
|
292
|
-
partial(self.enter_label_roi_mode, UNDEFINED_LABEL)
|
|
293
|
-
)
|
|
294
284
|
|
|
295
285
|
keypress_esc = QShortcut(QKeySequence(Qt.Key.Key_Escape), self)
|
|
296
286
|
keypress_esc.activated.connect(self.exit_label_roi_mode)
|
|
@@ -626,9 +616,9 @@ class ManualScoringWindow(QDialog):
|
|
|
626
616
|
"""
|
|
627
617
|
vmin, vmax = self.ui.upperfigure.spec_ref.get_clim()
|
|
628
618
|
if direction == BRIGHTER:
|
|
629
|
-
self.ui.upperfigure.spec_ref.set(clim=(vmin, vmax *
|
|
619
|
+
self.ui.upperfigure.spec_ref.set(clim=(vmin, vmax * BRIGHTER_SCALE_FACTOR))
|
|
630
620
|
else:
|
|
631
|
-
self.ui.upperfigure.spec_ref.set(clim=(vmin, vmax *
|
|
621
|
+
self.ui.upperfigure.spec_ref.set(clim=(vmin, vmax * DIMMER_SCALE_FACTOR))
|
|
632
622
|
self.ui.upperfigure.canvas.draw()
|
|
633
623
|
|
|
634
624
|
def update_epochs_shown(self, direction: str) -> None:
|
|
@@ -725,31 +715,29 @@ class ManualScoringWindow(QDialog):
|
|
|
725
715
|
|
|
726
716
|
:param direction: in, out, or reset
|
|
727
717
|
"""
|
|
728
|
-
zoom_in_factor = 0.45
|
|
729
|
-
zoom_out_factor = 1.017
|
|
730
718
|
epochs_shown = self.upper_right_epoch - self.upper_left_epoch + 1
|
|
731
719
|
if direction == ZOOM_IN:
|
|
732
720
|
self.upper_left_epoch = max(
|
|
733
721
|
[
|
|
734
722
|
self.upper_left_epoch,
|
|
735
|
-
round(self.epoch -
|
|
723
|
+
round(self.epoch - ZOOM_IN_FACTOR * epochs_shown),
|
|
736
724
|
]
|
|
737
725
|
)
|
|
738
726
|
|
|
739
727
|
self.upper_right_epoch = min(
|
|
740
728
|
[
|
|
741
729
|
self.upper_right_epoch,
|
|
742
|
-
round(self.epoch +
|
|
730
|
+
round(self.epoch + ZOOM_IN_FACTOR * epochs_shown),
|
|
743
731
|
]
|
|
744
732
|
)
|
|
745
733
|
|
|
746
734
|
elif direction == ZOOM_OUT:
|
|
747
735
|
self.upper_left_epoch = max(
|
|
748
|
-
[0, round(self.epoch -
|
|
736
|
+
[0, round(self.epoch - ZOOM_OUT_FACTOR * epochs_shown)]
|
|
749
737
|
)
|
|
750
738
|
|
|
751
739
|
self.upper_right_epoch = min(
|
|
752
|
-
[self.n_epochs - 1, round(self.epoch +
|
|
740
|
+
[self.n_epochs - 1, round(self.epoch + ZOOM_OUT_FACTOR * epochs_shown)]
|
|
753
741
|
)
|
|
754
742
|
|
|
755
743
|
else: # reset
|
|
@@ -1085,7 +1073,9 @@ def create_upper_emg_signal(
|
|
|
1085
1073
|
epoch_length,
|
|
1086
1074
|
emg_filter,
|
|
1087
1075
|
)
|
|
1088
|
-
return np.clip(
|
|
1076
|
+
return np.clip(
|
|
1077
|
+
emg_rms, np.percentile(emg_rms, 0.1), np.mean(emg_rms) + np.std(emg_rms) * 2.5
|
|
1078
|
+
)
|
|
1089
1079
|
|
|
1090
1080
|
|
|
1091
1081
|
def transform_eeg_emg(eeg: np.array, emg: np.array) -> (np.array, np.array):
|
accusleepy/gui/primary_window.py
CHANGED
|
@@ -8,8 +8,20 @@
|
|
|
8
8
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
|
9
9
|
################################################################################
|
|
10
10
|
|
|
11
|
-
from PySide6.QtCore import
|
|
12
|
-
|
|
11
|
+
from PySide6.QtCore import (
|
|
12
|
+
QCoreApplication,
|
|
13
|
+
QMetaObject,
|
|
14
|
+
QRect,
|
|
15
|
+
QSize,
|
|
16
|
+
Qt,
|
|
17
|
+
)
|
|
18
|
+
from PySide6.QtGui import (
|
|
19
|
+
QBrush,
|
|
20
|
+
QColor,
|
|
21
|
+
QFont,
|
|
22
|
+
QIcon,
|
|
23
|
+
QPalette,
|
|
24
|
+
)
|
|
13
25
|
from PySide6.QtWidgets import (
|
|
14
26
|
QCheckBox,
|
|
15
27
|
QComboBox,
|
|
@@ -2785,7 +2797,10 @@ class Ui_PrimaryWindow(object):
|
|
|
2785
2797
|
QCoreApplication.translate("PrimaryWindow", "MainWindow", None)
|
|
2786
2798
|
)
|
|
2787
2799
|
self.epochlengthlabel.setText(
|
|
2788
|
-
QCoreApplication.translate("PrimaryWindow", "Epoch length
|
|
2800
|
+
QCoreApplication.translate("PrimaryWindow", "Epoch length:", None)
|
|
2801
|
+
)
|
|
2802
|
+
self.epoch_length_input.setSuffix(
|
|
2803
|
+
QCoreApplication.translate("PrimaryWindow", " sec", None)
|
|
2789
2804
|
)
|
|
2790
2805
|
self.recordinglistgroupbox.setTitle(
|
|
2791
2806
|
QCoreApplication.translate("PrimaryWindow", "Recording list", None)
|
|
@@ -2840,7 +2855,10 @@ class Ui_PrimaryWindow(object):
|
|
|
2840
2855
|
)
|
|
2841
2856
|
)
|
|
2842
2857
|
self.samplingratelabel.setText(
|
|
2843
|
-
QCoreApplication.translate("PrimaryWindow", "Sampling rate
|
|
2858
|
+
QCoreApplication.translate("PrimaryWindow", "Sampling rate:", None)
|
|
2859
|
+
)
|
|
2860
|
+
self.sampling_rate_input.setSuffix(
|
|
2861
|
+
QCoreApplication.translate("PrimaryWindow", " Hz", None)
|
|
2844
2862
|
)
|
|
2845
2863
|
# if QT_CONFIG(tooltip)
|
|
2846
2864
|
self.recording_file_button.setToolTip(
|
|
@@ -2948,11 +2966,12 @@ class Ui_PrimaryWindow(object):
|
|
|
2948
2966
|
QCoreApplication.translate("PrimaryWindow", "Save confidence scores", None)
|
|
2949
2967
|
)
|
|
2950
2968
|
self.boutlengthlabel.setText(
|
|
2951
|
-
QCoreApplication.translate(
|
|
2952
|
-
"PrimaryWindow", "Minimum bout length (sec):", None
|
|
2953
|
-
)
|
|
2969
|
+
QCoreApplication.translate("PrimaryWindow", "Minimum bout length:", None)
|
|
2954
2970
|
)
|
|
2955
2971
|
self.bout_length_input.setPrefix("")
|
|
2972
|
+
self.bout_length_input.setSuffix(
|
|
2973
|
+
QCoreApplication.translate("PrimaryWindow", " sec", None)
|
|
2974
|
+
)
|
|
2956
2975
|
self.lower_tab_widget.setTabText(
|
|
2957
2976
|
self.lower_tab_widget.indexOf(self.classification_tab),
|
|
2958
2977
|
QCoreApplication.translate("PrimaryWindow", "Classification", None),
|
|
@@ -3096,6 +3115,9 @@ class Ui_PrimaryWindow(object):
|
|
|
3096
3115
|
self.label_17.setText(
|
|
3097
3116
|
QCoreApplication.translate("PrimaryWindow", "Epoch length:", None)
|
|
3098
3117
|
)
|
|
3118
|
+
self.default_epoch_input.setSuffix(
|
|
3119
|
+
QCoreApplication.translate("PrimaryWindow", " sec", None)
|
|
3120
|
+
)
|
|
3099
3121
|
self.overwrite_default_checkbox.setText(
|
|
3100
3122
|
QCoreApplication.translate(
|
|
3101
3123
|
"PrimaryWindow", "Only overwrite undefined epochs", None
|
|
@@ -3105,9 +3127,10 @@ class Ui_PrimaryWindow(object):
|
|
|
3105
3127
|
QCoreApplication.translate("PrimaryWindow", "Save confidence scores", None)
|
|
3106
3128
|
)
|
|
3107
3129
|
self.label_19.setText(
|
|
3108
|
-
QCoreApplication.translate(
|
|
3109
|
-
|
|
3110
|
-
|
|
3130
|
+
QCoreApplication.translate("PrimaryWindow", "Minimum bout length:", None)
|
|
3131
|
+
)
|
|
3132
|
+
self.default_min_bout_length_spinbox.setSuffix(
|
|
3133
|
+
QCoreApplication.translate("PrimaryWindow", " sec", None)
|
|
3111
3134
|
)
|
|
3112
3135
|
self.ui_default_description_label.setText(
|
|
3113
3136
|
QCoreApplication.translate(
|
accusleepy/gui/primary_window.ui
CHANGED
|
@@ -152,7 +152,7 @@
|
|
|
152
152
|
</sizepolicy>
|
|
153
153
|
</property>
|
|
154
154
|
<property name="text">
|
|
155
|
-
<string>Epoch length
|
|
155
|
+
<string>Epoch length:</string>
|
|
156
156
|
</property>
|
|
157
157
|
</widget>
|
|
158
158
|
</item>
|
|
@@ -167,6 +167,9 @@
|
|
|
167
167
|
<property name="alignment">
|
|
168
168
|
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
|
|
169
169
|
</property>
|
|
170
|
+
<property name="suffix">
|
|
171
|
+
<string> sec</string>
|
|
172
|
+
</property>
|
|
170
173
|
<property name="maximum">
|
|
171
174
|
<double>100000.000000000000000</double>
|
|
172
175
|
</property>
|
|
@@ -529,7 +532,7 @@ color: rgb(244, 195, 68);</string>
|
|
|
529
532
|
</sizepolicy>
|
|
530
533
|
</property>
|
|
531
534
|
<property name="text">
|
|
532
|
-
<string>Sampling rate
|
|
535
|
+
<string>Sampling rate:</string>
|
|
533
536
|
</property>
|
|
534
537
|
</widget>
|
|
535
538
|
</item>
|
|
@@ -544,6 +547,9 @@ color: rgb(244, 195, 68);</string>
|
|
|
544
547
|
<property name="alignment">
|
|
545
548
|
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
|
|
546
549
|
</property>
|
|
550
|
+
<property name="suffix">
|
|
551
|
+
<string> Hz</string>
|
|
552
|
+
</property>
|
|
547
553
|
<property name="minimum">
|
|
548
554
|
<double>0.000000000000000</double>
|
|
549
555
|
</property>
|
|
@@ -1148,7 +1154,7 @@ color: rgb(244, 195, 68);</string>
|
|
|
1148
1154
|
<string notr="true">background-color: transparent;</string>
|
|
1149
1155
|
</property>
|
|
1150
1156
|
<property name="text">
|
|
1151
|
-
<string>Minimum bout length
|
|
1157
|
+
<string>Minimum bout length:</string>
|
|
1152
1158
|
</property>
|
|
1153
1159
|
</widget>
|
|
1154
1160
|
</item>
|
|
@@ -1166,6 +1172,9 @@ color: rgb(244, 195, 68);</string>
|
|
|
1166
1172
|
<property name="prefix">
|
|
1167
1173
|
<string/>
|
|
1168
1174
|
</property>
|
|
1175
|
+
<property name="suffix">
|
|
1176
|
+
<string> sec</string>
|
|
1177
|
+
</property>
|
|
1169
1178
|
<property name="decimals">
|
|
1170
1179
|
<number>2</number>
|
|
1171
1180
|
</property>
|
|
@@ -3811,6 +3820,9 @@ Each brain state has several attributes:
|
|
|
3811
3820
|
<verstretch>0</verstretch>
|
|
3812
3821
|
</sizepolicy>
|
|
3813
3822
|
</property>
|
|
3823
|
+
<property name="suffix">
|
|
3824
|
+
<string> sec</string>
|
|
3825
|
+
</property>
|
|
3814
3826
|
<property name="maximum">
|
|
3815
3827
|
<double>100000.000000000000000</double>
|
|
3816
3828
|
</property>
|
|
@@ -3920,7 +3932,7 @@ Each brain state has several attributes:
|
|
|
3920
3932
|
</sizepolicy>
|
|
3921
3933
|
</property>
|
|
3922
3934
|
<property name="text">
|
|
3923
|
-
<string>Minimum bout length
|
|
3935
|
+
<string>Minimum bout length:</string>
|
|
3924
3936
|
</property>
|
|
3925
3937
|
</widget>
|
|
3926
3938
|
</item>
|
|
@@ -3932,6 +3944,9 @@ Each brain state has several attributes:
|
|
|
3932
3944
|
<verstretch>0</verstretch>
|
|
3933
3945
|
</sizepolicy>
|
|
3934
3946
|
</property>
|
|
3947
|
+
<property name="suffix">
|
|
3948
|
+
<string> sec</string>
|
|
3949
|
+
</property>
|
|
3935
3950
|
<property name="maximum">
|
|
3936
3951
|
<double>1000.000000000000000</double>
|
|
3937
3952
|
</property>
|
accusleepy/models.py
CHANGED
|
@@ -1,24 +1,13 @@
|
|
|
1
|
-
import numpy as np
|
|
2
1
|
from torch import device, flatten, nn
|
|
3
2
|
from torch import load as torch_load
|
|
4
3
|
from torch import save as torch_save
|
|
5
4
|
|
|
6
5
|
from accusleepy.brain_state_set import BRAIN_STATES_KEY, BrainStateSet
|
|
7
6
|
from accusleepy.constants import (
|
|
8
|
-
|
|
9
|
-
EMG_COPIES,
|
|
10
|
-
MIN_WINDOW_LEN,
|
|
11
|
-
UPPER_FREQ,
|
|
7
|
+
IMAGE_HEIGHT,
|
|
12
8
|
)
|
|
13
9
|
from accusleepy.temperature_scaling import ModelWithTemperature
|
|
14
10
|
|
|
15
|
-
# height in pixels of each training image
|
|
16
|
-
IMAGE_HEIGHT = (
|
|
17
|
-
len(np.arange(0, DOWNSAMPLING_START_FREQ, 1 / MIN_WINDOW_LEN))
|
|
18
|
-
+ len(np.arange(DOWNSAMPLING_START_FREQ, UPPER_FREQ, 2 / MIN_WINDOW_LEN))
|
|
19
|
-
+ EMG_COPIES
|
|
20
|
-
)
|
|
21
|
-
|
|
22
11
|
|
|
23
12
|
class SSANN(nn.Module):
|
|
24
13
|
"""Small CNN for classifying images"""
|
accusleepy/signal_processing.py
CHANGED
|
@@ -8,6 +8,7 @@ from tqdm import trange
|
|
|
8
8
|
|
|
9
9
|
from accusleepy.brain_state_set import BrainStateSet
|
|
10
10
|
from accusleepy.constants import (
|
|
11
|
+
ABS_MAX_Z_SCORE,
|
|
11
12
|
ANNOTATIONS_FILENAME,
|
|
12
13
|
CALIBRATION_ANNOTATION_FILENAME,
|
|
13
14
|
DEFAULT_MODEL_TYPE,
|
|
@@ -17,18 +18,13 @@ from accusleepy.constants import (
|
|
|
17
18
|
LABEL_COL,
|
|
18
19
|
MIN_WINDOW_LEN,
|
|
19
20
|
UPPER_FREQ,
|
|
21
|
+
SPECTROGRAM_UPPER_FREQ,
|
|
20
22
|
)
|
|
21
23
|
from accusleepy.fileio import Recording, load_labels, load_recording, EMGFilter
|
|
22
24
|
from accusleepy.multitaper import spectrogram
|
|
23
25
|
|
|
24
26
|
# note: scipy is lazily imported
|
|
25
27
|
|
|
26
|
-
# clip mixture z-scores above and below this level
|
|
27
|
-
# in the matlab implementation, I used 4.5
|
|
28
|
-
ABS_MAX_Z_SCORE = 3.5
|
|
29
|
-
# upper frequency limit when generating EEG spectrograms
|
|
30
|
-
SPECTROGRAM_UPPER_FREQ = 64
|
|
31
|
-
|
|
32
28
|
|
|
33
29
|
def resample(
|
|
34
30
|
eeg: np.array, emg: np.array, sampling_rate: int | float, epoch_length: int | float
|
accusleepy/validation.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from accusleepy.brain_state_set import BrainStateSet
|
|
4
|
+
from accusleepy.constants import UNDEFINED_LABEL
|
|
5
|
+
|
|
6
|
+
LABEL_LENGTH_ERROR = "label file length does not match recording length"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def check_label_validity(
|
|
10
|
+
labels: np.array,
|
|
11
|
+
confidence_scores: np.array,
|
|
12
|
+
samples_in_recording: int,
|
|
13
|
+
sampling_rate: int | float,
|
|
14
|
+
epoch_length: int | float,
|
|
15
|
+
brain_state_set: BrainStateSet,
|
|
16
|
+
) -> str | None:
|
|
17
|
+
"""Check whether a set of brain state labels is valid
|
|
18
|
+
|
|
19
|
+
This returns an error message if a problem is found with the
|
|
20
|
+
brain state labels.
|
|
21
|
+
|
|
22
|
+
:param labels: brain state labels
|
|
23
|
+
:param confidence_scores: confidence scores
|
|
24
|
+
:param samples_in_recording: number of samples in the recording
|
|
25
|
+
:param sampling_rate: sampling rate, in Hz
|
|
26
|
+
:param epoch_length: epoch length, in seconds
|
|
27
|
+
:param brain_state_set: BrainStateMapper object
|
|
28
|
+
:return: error message
|
|
29
|
+
"""
|
|
30
|
+
# check that number of labels is correct
|
|
31
|
+
samples_per_epoch = round(sampling_rate * epoch_length)
|
|
32
|
+
epochs_in_recording = round(samples_in_recording / samples_per_epoch)
|
|
33
|
+
if epochs_in_recording != labels.size:
|
|
34
|
+
return LABEL_LENGTH_ERROR
|
|
35
|
+
|
|
36
|
+
# check that entries are valid
|
|
37
|
+
if not set(labels.tolist()).issubset(
|
|
38
|
+
set([b.digit for b in brain_state_set.brain_states] + [UNDEFINED_LABEL])
|
|
39
|
+
):
|
|
40
|
+
return "label file contains invalid entries"
|
|
41
|
+
|
|
42
|
+
if confidence_scores is not None:
|
|
43
|
+
if np.min(confidence_scores) < 0 or np.max(confidence_scores) > 1:
|
|
44
|
+
return "label file contains invalid confidence scores"
|
|
45
|
+
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def check_config_consistency(
|
|
50
|
+
current_brain_states: dict,
|
|
51
|
+
model_brain_states: dict,
|
|
52
|
+
current_epoch_length: int | float,
|
|
53
|
+
model_epoch_length: int | float,
|
|
54
|
+
) -> list[str]:
|
|
55
|
+
"""Compare current brain state config to the model's config
|
|
56
|
+
|
|
57
|
+
This only displays warnings - the user should decide whether to proceed
|
|
58
|
+
|
|
59
|
+
:param current_brain_states: current brain state config
|
|
60
|
+
:param model_brain_states: brain state config when the model was created
|
|
61
|
+
:param current_epoch_length: current epoch length setting
|
|
62
|
+
:param model_epoch_length: epoch length used when the model was created
|
|
63
|
+
"""
|
|
64
|
+
output = list()
|
|
65
|
+
|
|
66
|
+
# make lists of names and digits for scored brain states
|
|
67
|
+
current_scored_states = {
|
|
68
|
+
f: [b[f] for b in current_brain_states if b["is_scored"]]
|
|
69
|
+
for f in ["name", "digit"]
|
|
70
|
+
}
|
|
71
|
+
model_scored_states = {
|
|
72
|
+
f: [b[f] for b in model_brain_states if b["is_scored"]]
|
|
73
|
+
for f in ["name", "digit"]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# generate message comparing the brain state configs
|
|
77
|
+
config_comparisons = list()
|
|
78
|
+
for config, config_name in zip(
|
|
79
|
+
[current_scored_states, model_scored_states], ["current", "model's"]
|
|
80
|
+
):
|
|
81
|
+
config_comparisons.append(
|
|
82
|
+
f"Scored brain states in {config_name} configuration: "
|
|
83
|
+
f"""{
|
|
84
|
+
", ".join(
|
|
85
|
+
[
|
|
86
|
+
f"{x}: {y}"
|
|
87
|
+
for x, y in zip(
|
|
88
|
+
config["digit"],
|
|
89
|
+
config["name"],
|
|
90
|
+
)
|
|
91
|
+
]
|
|
92
|
+
)
|
|
93
|
+
}"""
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# check if the number of scored states is different
|
|
97
|
+
len_diff = len(current_scored_states["name"]) - len(model_scored_states["name"])
|
|
98
|
+
if len_diff != 0:
|
|
99
|
+
output.append(
|
|
100
|
+
(
|
|
101
|
+
"WARNING: current brain state configuration has "
|
|
102
|
+
f"{'fewer' if len_diff < 0 else 'more'} "
|
|
103
|
+
"scored brain states than the model's configuration."
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
output = output + config_comparisons
|
|
107
|
+
else:
|
|
108
|
+
# the length is the same, but names might be different
|
|
109
|
+
if current_scored_states["name"] != model_scored_states["name"]:
|
|
110
|
+
output.append(
|
|
111
|
+
(
|
|
112
|
+
"WARNING: current brain state configuration appears "
|
|
113
|
+
"to contain different brain states than "
|
|
114
|
+
"the model's configuration."
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
output = output + config_comparisons
|
|
118
|
+
|
|
119
|
+
if current_epoch_length != model_epoch_length:
|
|
120
|
+
output.append(
|
|
121
|
+
(
|
|
122
|
+
"Warning: the epoch length used when training this model "
|
|
123
|
+
f"({model_epoch_length} seconds) "
|
|
124
|
+
"does not match the current epoch length setting."
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return output
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: accusleepy
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Python implementation of AccuSleep
|
|
5
5
|
License: GPL-3.0-only
|
|
6
6
|
Author: Zeke Barger
|
|
@@ -18,11 +18,11 @@ Requires-Dist: numpy (>=2.2.4,<3.0.0)
|
|
|
18
18
|
Requires-Dist: pandas (>=2.2.3,<3.0.0)
|
|
19
19
|
Requires-Dist: pillow (>=11.1.0,<12.0.0)
|
|
20
20
|
Requires-Dist: pre-commit (>=4.2.0,<5.0.0)
|
|
21
|
-
Requires-Dist: pyside6 (
|
|
21
|
+
Requires-Dist: pyside6 (==6.9.0)
|
|
22
22
|
Requires-Dist: scipy (>=1.15.2,<2.0.0)
|
|
23
23
|
Requires-Dist: toml (>=0.10.2,<0.11.0)
|
|
24
|
-
Requires-Dist: torch (>=2.
|
|
25
|
-
Requires-Dist: torchvision (>=0.
|
|
24
|
+
Requires-Dist: torch (>=2.8.0,<3.0.0)
|
|
25
|
+
Requires-Dist: torchvision (>=0.23.0,<1.0.0)
|
|
26
26
|
Requires-Dist: tqdm (>=4.67.1,<5.0.0)
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
28
28
|
|
|
@@ -76,6 +76,8 @@ to the [config file](accusleepy/config.json).
|
|
|
76
76
|
|
|
77
77
|
## Changelog
|
|
78
78
|
|
|
79
|
+
- 0.7.2: Address issues with certain dependencies
|
|
80
|
+
- 0.7.1: Bugfixes, code cleanup
|
|
79
81
|
- 0.7.0: More settings can be configured in the UI
|
|
80
82
|
- 0.6.0: Confidence scores can now be displayed and saved. Retraining your models is recommended
|
|
81
83
|
since the new calibration feature will make the confidence scores more accurate.
|
|
@@ -4,7 +4,7 @@ accusleepy/bouts.py,sha256=F_y6DxnpKFfImYb7vCZluZ2eD5I_33gZXmRM8mvebsg,5679
|
|
|
4
4
|
accusleepy/brain_state_set.py,sha256=fRkrArHLIbEKimub804yt_mUXoyfsjJEfiJnTjeCMkY,3233
|
|
5
5
|
accusleepy/classification.py,sha256=mF35xMrD9QXGldSnl3vkdHbm7CAptPUNjHxUA_agOTA,9778
|
|
6
6
|
accusleepy/config.json,sha256=Ip0qTMAn2LZfof9GVA_azOvpXP0WKnqLCZeSaya1sss,819
|
|
7
|
-
accusleepy/constants.py,sha256=
|
|
7
|
+
accusleepy/constants.py,sha256=r53FWeMMefuKA-mR_b2E1KBeJbq2Ck9c5VHoJ1WGZjg,2815
|
|
8
8
|
accusleepy/fileio.py,sha256=woIF0zgJt6Lx6T9KBXAQ-AlbQAwOK1_RUmVF710nltI,7383
|
|
9
9
|
accusleepy/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
10
|
accusleepy/gui/icons/brightness_down.png,sha256=PLT1fb83RHIhSRuU7MMMx0G7oJAY7o9wUcnqM8veZfM,12432
|
|
@@ -18,24 +18,25 @@ accusleepy/gui/icons/save.png,sha256=J3EA8iU1BqLYRSsrq_OdoZlqrv2yfL7oV54DklTy_DI
|
|
|
18
18
|
accusleepy/gui/icons/up_arrow.png,sha256=V9yF9t1WgjPaUu-mF1YGe_DfaRHg2dUpR_sUVVcvVvY,3329
|
|
19
19
|
accusleepy/gui/icons/zoom_in.png,sha256=MFWnKZp7Rvh4bLPq4Cqo4sB_jQYedUUtT8-ZO8tNYyc,13589
|
|
20
20
|
accusleepy/gui/icons/zoom_out.png,sha256=IB8Jecb3i0U4qjWRR46ridjLpvLCSe7PozBaLqQqYSw,13055
|
|
21
|
-
accusleepy/gui/images/primary_window.png,sha256=
|
|
21
|
+
accusleepy/gui/images/primary_window.png,sha256=vYiDdf1mTx6E6SSOfkjy6UL5yusxCQNU3VBqguIjmuE,597348
|
|
22
22
|
accusleepy/gui/images/viewer_window.png,sha256=b_B7m9WSLMAOzNjctq76SyekO1WfC6qYZVNnYfhjPe8,977197
|
|
23
23
|
accusleepy/gui/images/viewer_window_annotated.png,sha256=uMNUmsZIdzDlQpyoiS3lJGoWlg_T325Oj5hDZhM3Y14,146817
|
|
24
|
-
accusleepy/gui/main.py,sha256=
|
|
25
|
-
accusleepy/gui/manual_scoring.py,sha256=
|
|
24
|
+
accusleepy/gui/main.py,sha256=sLHe1imVU6xz3f6sJ9u7sxrTny9X213ev29ldoxXpX0,57740
|
|
25
|
+
accusleepy/gui/manual_scoring.py,sha256=7jKdw-D9HdpYC2G_2RHt5UALLMjrIsP-hvLFkNM25Ek,40553
|
|
26
26
|
accusleepy/gui/mplwidget.py,sha256=rJSTtWmLjHn8r3c9Kb23Rc4XzXl3i9B-JrjNjjlNnmQ,13492
|
|
27
|
-
accusleepy/gui/primary_window.py,sha256=
|
|
28
|
-
accusleepy/gui/primary_window.ui,sha256=
|
|
27
|
+
accusleepy/gui/primary_window.py,sha256=Qc3WWiwJHbgmGDzGzNI3fBycBKtlpT5YdskwnHr6OWs,136070
|
|
28
|
+
accusleepy/gui/primary_window.ui,sha256=u7HQdeNKObKsUSOlk91gEMat-RrXITEsnBedZPd7y6Y,201950
|
|
29
29
|
accusleepy/gui/resources.qrc,sha256=wqPendnTLAuKfVI6v2lKHiRqAWM0oaz2ZuF5cucJdS4,803
|
|
30
30
|
accusleepy/gui/resources_rc.py,sha256=Z2e34h30U4snJjnYdZVV9B6yjATKxxfvgTRt5uXtQdo,329727
|
|
31
31
|
accusleepy/gui/text/main_guide.md,sha256=iZDRp5OWyQX9LV7CMeUFIYv2ryKlIcGALRLXjxR8HpI,8288
|
|
32
32
|
accusleepy/gui/text/manual_scoring_guide.md,sha256=ow_RMSjFy05NupEDSCuJtu-V65-BPnIkrZqtssFoZCQ,999
|
|
33
33
|
accusleepy/gui/viewer_window.py,sha256=O4ceqLMYdahxQ9s6DYhloUnNESim-cqIZxFeXEiRjog,24444
|
|
34
34
|
accusleepy/gui/viewer_window.ui,sha256=jsjydsSSyN49AwJw4nVS2mEJ2JBIUTXesAJsij1JNV0,31530
|
|
35
|
-
accusleepy/models.py,sha256=
|
|
35
|
+
accusleepy/models.py,sha256=kqkcQJoKi7gpnM8gZ7nZbWGvpv7ruNnFLaB7ED1X6Dc,3493
|
|
36
36
|
accusleepy/multitaper.py,sha256=D5-iglwkFBRciL5tKSNcunMtcq0rM3zHwRHUVPgem1U,25679
|
|
37
|
-
accusleepy/signal_processing.py,sha256=
|
|
37
|
+
accusleepy/signal_processing.py,sha256=47fEAx8Aqqkiqix1ai2YEK9Fhq6UHoQcwAcOi-a8ewo,16834
|
|
38
38
|
accusleepy/temperature_scaling.py,sha256=glvPcvxHpBdFjwjGfZdNku9L_BozycEmdqZhKKUCCNg,5749
|
|
39
|
-
accusleepy
|
|
40
|
-
accusleepy-0.7.
|
|
41
|
-
accusleepy-0.7.
|
|
39
|
+
accusleepy/validation.py,sha256=VpLWK-wD5tCU6lTBG3KYgTi3PWGuYh6NitMgMoMH8JM,4434
|
|
40
|
+
accusleepy-0.7.2.dist-info/METADATA,sha256=X3r6C8hE124cK1pmsWPGlzBRgmXEYGIyKoQieumi1R0,4610
|
|
41
|
+
accusleepy-0.7.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
42
|
+
accusleepy-0.7.2.dist-info/RECORD,,
|
|
File without changes
|