accusleepy 0.1.2__py3-none-any.whl → 0.3.1__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/config.json +3 -2
- accusleepy/constants.py +2 -0
- accusleepy/fileio.py +14 -6
- accusleepy/gui/main.py +24 -29
- accusleepy/gui/manual_scoring.py +2 -2
- accusleepy/gui/primary_window.py +73 -3
- accusleepy/gui/primary_window.ui +79 -1
- accusleepy/gui/text/config_guide.txt +2 -1
- accusleepy/gui/text/{main_guide.txt → main_guide_text.py} +27 -20
- {accusleepy-0.1.2.dist-info → accusleepy-0.3.1.dist-info}/METADATA +9 -5
- {accusleepy-0.1.2.dist-info → accusleepy-0.3.1.dist-info}/RECORD +12 -12
- {accusleepy-0.1.2.dist-info → accusleepy-0.3.1.dist-info}/WHEEL +0 -0
accusleepy/config.json
CHANGED
accusleepy/constants.py
CHANGED
accusleepy/fileio.py
CHANGED
|
@@ -11,6 +11,7 @@ from accusleepy.brain_state_set import BRAIN_STATES_KEY, BrainState, BrainStateS
|
|
|
11
11
|
from accusleepy.constants import (
|
|
12
12
|
BRAIN_STATE_COL,
|
|
13
13
|
CONFIG_FILE,
|
|
14
|
+
DEFAULT_EPOCH_LENGTH_KEY,
|
|
14
15
|
EEG_COL,
|
|
15
16
|
EMG_COL,
|
|
16
17
|
MIXTURE_MEAN_COL,
|
|
@@ -82,7 +83,9 @@ def load_model(filename: str) -> tuple[SSANN, int | float, int, str, dict]:
|
|
|
82
83
|
(default or real-time), set of brain state options
|
|
83
84
|
used when training the model
|
|
84
85
|
"""
|
|
85
|
-
state_dict = torch.load(
|
|
86
|
+
state_dict = torch.load(
|
|
87
|
+
filename, weights_only=True, map_location=torch.device("cpu")
|
|
88
|
+
)
|
|
86
89
|
epoch_length = state_dict.pop("epoch_length")
|
|
87
90
|
epochs_per_img = state_dict.pop("epochs_per_img")
|
|
88
91
|
model_type = state_dict.pop("model_type")
|
|
@@ -141,10 +144,10 @@ def save_labels(labels: np.array, filename: str) -> None:
|
|
|
141
144
|
pd.DataFrame({BRAIN_STATE_COL: labels}).to_csv(filename, index=False)
|
|
142
145
|
|
|
143
146
|
|
|
144
|
-
def load_config() -> BrainStateSet:
|
|
147
|
+
def load_config() -> tuple[BrainStateSet, int | float]:
|
|
145
148
|
"""Load configuration file with brain state options
|
|
146
149
|
|
|
147
|
-
:return: set of brain state options
|
|
150
|
+
:return: set of brain state options and default epoch length
|
|
148
151
|
"""
|
|
149
152
|
with open(
|
|
150
153
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), CONFIG_FILE), "r"
|
|
@@ -152,18 +155,23 @@ def load_config() -> BrainStateSet:
|
|
|
152
155
|
data = json.load(f)
|
|
153
156
|
return BrainStateSet(
|
|
154
157
|
[BrainState(**b) for b in data[BRAIN_STATES_KEY]], UNDEFINED_LABEL
|
|
155
|
-
)
|
|
158
|
+
), data[DEFAULT_EPOCH_LENGTH_KEY]
|
|
156
159
|
|
|
157
160
|
|
|
158
|
-
def save_config(
|
|
161
|
+
def save_config(
|
|
162
|
+
brain_state_set: BrainStateSet, default_epoch_length: int | float
|
|
163
|
+
) -> None:
|
|
159
164
|
"""Save configuration of brain state options to json file
|
|
160
165
|
|
|
161
166
|
:param brain_state_set: set of brain state options
|
|
167
|
+
:param default_epoch_length: epoch length to use when the GUI starts
|
|
162
168
|
"""
|
|
169
|
+
output_dict = brain_state_set.to_output_dict()
|
|
170
|
+
output_dict.update({DEFAULT_EPOCH_LENGTH_KEY: default_epoch_length})
|
|
163
171
|
with open(
|
|
164
172
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), CONFIG_FILE), "w"
|
|
165
173
|
) as f:
|
|
166
|
-
json.dump(
|
|
174
|
+
json.dump(output_dict, f, indent=4)
|
|
167
175
|
|
|
168
176
|
|
|
169
177
|
def load_recording_list(filename: str) -> list[Recording]:
|
accusleepy/gui/main.py
CHANGED
|
@@ -40,6 +40,7 @@ from accusleepy.fileio import (
|
|
|
40
40
|
save_model,
|
|
41
41
|
save_recording_list,
|
|
42
42
|
)
|
|
43
|
+
from accusleepy.gui.text.main_guide_text import MAIN_GUIDE_TEXT
|
|
43
44
|
from accusleepy.gui.manual_scoring import ManualScoringWindow
|
|
44
45
|
from accusleepy.gui.primary_window import Ui_PrimaryWindow
|
|
45
46
|
from accusleepy.signal_processing import (
|
|
@@ -52,8 +53,7 @@ from accusleepy.signal_processing import (
|
|
|
52
53
|
# max number of messages to display
|
|
53
54
|
MESSAGE_BOX_MAX_DEPTH = 50
|
|
54
55
|
LABEL_LENGTH_ERROR = "label file length does not match recording length"
|
|
55
|
-
# relative path to
|
|
56
|
-
USER_MANUAL_FILE = "text/main_guide.txt"
|
|
56
|
+
# relative path to config guide txt file
|
|
57
57
|
CONFIG_GUIDE_FILE = "text/config_guide.txt"
|
|
58
58
|
|
|
59
59
|
|
|
@@ -80,12 +80,12 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
80
80
|
self.setWindowTitle("AccuSleePy")
|
|
81
81
|
|
|
82
82
|
# fill in settings tab
|
|
83
|
-
self.brain_state_set = load_config()
|
|
83
|
+
self.brain_state_set, self.epoch_length = load_config()
|
|
84
84
|
self.settings_widgets = None
|
|
85
85
|
self.initialize_settings_tab()
|
|
86
86
|
|
|
87
87
|
# initialize info about the recordings, classification data / settings
|
|
88
|
-
self.epoch_length
|
|
88
|
+
self.ui.epoch_length_input.setValue(self.epoch_length)
|
|
89
89
|
self.model = None
|
|
90
90
|
self.only_overwrite_undefined = False
|
|
91
91
|
self.min_bout_length = 5
|
|
@@ -290,23 +290,28 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
290
290
|
if not model_filename:
|
|
291
291
|
self.show_message("Model training canceled, no filename given")
|
|
292
292
|
|
|
293
|
-
# create image folder
|
|
294
|
-
|
|
293
|
+
# create (probably temporary) image folder
|
|
294
|
+
temp_image_dir = os.path.join(
|
|
295
|
+
self.training_image_dir,
|
|
296
|
+
"images_" + datetime.datetime.now().strftime("%Y%m%d%H%M"),
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
if os.path.exists(temp_image_dir): # unlikely
|
|
295
300
|
self.show_message(
|
|
296
301
|
"Warning: training image folder exists, will be overwritten"
|
|
297
302
|
)
|
|
298
|
-
os.makedirs(
|
|
303
|
+
os.makedirs(temp_image_dir, exist_ok=True)
|
|
299
304
|
|
|
300
305
|
# create training images
|
|
301
306
|
self.show_message(
|
|
302
|
-
(f"Creating training images in {
|
|
307
|
+
(f"Creating training images in {temp_image_dir}, please wait...")
|
|
303
308
|
)
|
|
304
309
|
self.ui.message_area.repaint()
|
|
305
310
|
QtWidgets.QApplication.processEvents()
|
|
306
311
|
print("Creating training images")
|
|
307
312
|
failed_recordings = create_training_images(
|
|
308
313
|
recordings=self.recordings,
|
|
309
|
-
output_path=
|
|
314
|
+
output_path=temp_image_dir,
|
|
310
315
|
epoch_length=self.epoch_length,
|
|
311
316
|
epochs_per_img=self.training_epochs_per_img,
|
|
312
317
|
brain_state_set=self.brain_state_set,
|
|
@@ -330,10 +335,8 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
330
335
|
QtWidgets.QApplication.processEvents()
|
|
331
336
|
print("Training model")
|
|
332
337
|
model = train_model(
|
|
333
|
-
annotations_file=os.path.join(
|
|
334
|
-
|
|
335
|
-
),
|
|
336
|
-
img_dir=self.training_image_dir,
|
|
338
|
+
annotations_file=os.path.join(temp_image_dir, ANNOTATIONS_FILENAME),
|
|
339
|
+
img_dir=temp_image_dir,
|
|
337
340
|
mixture_weights=self.brain_state_set.mixture_weights,
|
|
338
341
|
n_classes=self.brain_state_set.n_classes,
|
|
339
342
|
)
|
|
@@ -350,20 +353,18 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
350
353
|
|
|
351
354
|
# optionally delete images
|
|
352
355
|
if self.delete_training_images:
|
|
353
|
-
shutil.rmtree(
|
|
356
|
+
shutil.rmtree(temp_image_dir)
|
|
354
357
|
|
|
355
358
|
self.show_message(f"Training complete, saved model to {model_filename}")
|
|
356
359
|
|
|
357
|
-
def set_training_folder(self):
|
|
360
|
+
def set_training_folder(self) -> None:
|
|
361
|
+
"""Select location in which to create a folder for training images"""
|
|
358
362
|
training_folder_parent = QtWidgets.QFileDialog.getExistingDirectory(
|
|
359
363
|
self, "Select directory for training images"
|
|
360
364
|
)
|
|
361
365
|
if training_folder_parent:
|
|
362
|
-
self.training_image_dir =
|
|
363
|
-
|
|
364
|
-
"images_" + datetime.datetime.now().strftime("%Y%m%d%H%M"),
|
|
365
|
-
)
|
|
366
|
-
self.ui.image_folder_label.setText(self.training_image_dir)
|
|
366
|
+
self.training_image_dir = training_folder_parent
|
|
367
|
+
self.ui.image_folder_label.setText(training_folder_parent)
|
|
367
368
|
|
|
368
369
|
def update_image_deletion(self) -> None:
|
|
369
370
|
"""Update choice of whether to delete images after training"""
|
|
@@ -1004,15 +1005,8 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
1004
1005
|
|
|
1005
1006
|
def show_user_manual(self) -> None:
|
|
1006
1007
|
"""Show a popup window with the user manual"""
|
|
1007
|
-
user_manual_file = open(
|
|
1008
|
-
os.path.join(os.path.dirname(os.path.abspath(__file__)), USER_MANUAL_FILE),
|
|
1009
|
-
"r",
|
|
1010
|
-
)
|
|
1011
|
-
user_manual_text = user_manual_file.read()
|
|
1012
|
-
user_manual_file.close()
|
|
1013
|
-
|
|
1014
1008
|
label_widget = QtWidgets.QLabel()
|
|
1015
|
-
label_widget.setText(
|
|
1009
|
+
label_widget.setText(MAIN_GUIDE_TEXT)
|
|
1016
1010
|
scroll_area = QtWidgets.QScrollArea()
|
|
1017
1011
|
scroll_area.setStyleSheet("background-color: white;")
|
|
1018
1012
|
scroll_area.setWidget(label_widget)
|
|
@@ -1110,6 +1104,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
1110
1104
|
}
|
|
1111
1105
|
|
|
1112
1106
|
# update widget state to display current config
|
|
1107
|
+
self.ui.default_epoch_input.setValue(self.epoch_length)
|
|
1113
1108
|
states = {b.digit: b for b in self.brain_state_set.brain_states}
|
|
1114
1109
|
for digit in range(10):
|
|
1115
1110
|
if digit in states.keys():
|
|
@@ -1247,7 +1242,7 @@ class AccuSleepWindow(QtWidgets.QMainWindow):
|
|
|
1247
1242
|
self.brain_state_set = BrainStateSet(brain_states, UNDEFINED_LABEL)
|
|
1248
1243
|
|
|
1249
1244
|
# save to file
|
|
1250
|
-
save_config(self.brain_state_set)
|
|
1245
|
+
save_config(self.brain_state_set, self.ui.default_epoch_input.value())
|
|
1251
1246
|
self.ui.save_config_status.setText("configuration saved")
|
|
1252
1247
|
|
|
1253
1248
|
|
accusleepy/gui/manual_scoring.py
CHANGED
|
@@ -111,7 +111,7 @@ class ManualScoringWindow(QtWidgets.QDialog):
|
|
|
111
111
|
self.setWindowTitle("AccuSleePy manual scoring window")
|
|
112
112
|
|
|
113
113
|
# load set of valid brain states
|
|
114
|
-
self.brain_state_set = load_config()
|
|
114
|
+
self.brain_state_set, _ = load_config()
|
|
115
115
|
|
|
116
116
|
# initial setting for number of epochs to show in the lower plot
|
|
117
117
|
self.epochs_to_show = 5
|
|
@@ -833,7 +833,7 @@ class ManualScoringWindow(QtWidgets.QDialog):
|
|
|
833
833
|
self.adjust_upper_figure_x_limits()
|
|
834
834
|
|
|
835
835
|
# update parts of lower plot
|
|
836
|
-
old_window_center = round(self.epochs_to_show / 2) + self.lower_left_epoch
|
|
836
|
+
old_window_center = round((self.epochs_to_show - 1) / 2) + self.lower_left_epoch
|
|
837
837
|
# change the window bounds if needed
|
|
838
838
|
if self.epoch < old_window_center and self.lower_left_epoch > 0:
|
|
839
839
|
self.lower_left_epoch -= 1
|
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
|
QDoubleSpinBox,
|
|
@@ -100,6 +112,11 @@ class Ui_PrimaryWindow(object):
|
|
|
100
112
|
self.epoch_length_input.sizePolicy().hasHeightForWidth()
|
|
101
113
|
)
|
|
102
114
|
self.epoch_length_input.setSizePolicy(sizePolicy1)
|
|
115
|
+
self.epoch_length_input.setAlignment(
|
|
116
|
+
Qt.AlignmentFlag.AlignLeading
|
|
117
|
+
| Qt.AlignmentFlag.AlignLeft
|
|
118
|
+
| Qt.AlignmentFlag.AlignVCenter
|
|
119
|
+
)
|
|
103
120
|
self.epoch_length_input.setMaximum(100000.000000000000000)
|
|
104
121
|
self.epoch_length_input.setSingleStep(0.500000000000000)
|
|
105
122
|
|
|
@@ -293,6 +310,11 @@ class Ui_PrimaryWindow(object):
|
|
|
293
310
|
self.sampling_rate_input.sizePolicy().hasHeightForWidth()
|
|
294
311
|
)
|
|
295
312
|
self.sampling_rate_input.setSizePolicy(sizePolicy1)
|
|
313
|
+
self.sampling_rate_input.setAlignment(
|
|
314
|
+
Qt.AlignmentFlag.AlignLeading
|
|
315
|
+
| Qt.AlignmentFlag.AlignLeft
|
|
316
|
+
| Qt.AlignmentFlag.AlignVCenter
|
|
317
|
+
)
|
|
296
318
|
self.sampling_rate_input.setMinimum(0.000000000000000)
|
|
297
319
|
self.sampling_rate_input.setMaximum(100000.000000000000000)
|
|
298
320
|
|
|
@@ -608,6 +630,11 @@ class Ui_PrimaryWindow(object):
|
|
|
608
630
|
self.bout_length_input.sizePolicy().hasHeightForWidth()
|
|
609
631
|
)
|
|
610
632
|
self.bout_length_input.setSizePolicy(sizePolicy1)
|
|
633
|
+
self.bout_length_input.setAlignment(
|
|
634
|
+
Qt.AlignmentFlag.AlignLeading
|
|
635
|
+
| Qt.AlignmentFlag.AlignLeft
|
|
636
|
+
| Qt.AlignmentFlag.AlignVCenter
|
|
637
|
+
)
|
|
611
638
|
self.bout_length_input.setDecimals(2)
|
|
612
639
|
self.bout_length_input.setMaximum(1000.000000000000000)
|
|
613
640
|
self.bout_length_input.setValue(5.000000000000000)
|
|
@@ -2044,6 +2071,44 @@ class Ui_PrimaryWindow(object):
|
|
|
2044
2071
|
|
|
2045
2072
|
self.verticalLayout_6.addLayout(self.horizontalLayout_4)
|
|
2046
2073
|
|
|
2074
|
+
self.default_epoch_layout = QHBoxLayout()
|
|
2075
|
+
self.default_epoch_layout.setObjectName("default_epoch_layout")
|
|
2076
|
+
self.horizontalSpacer_71 = QSpacerItem(
|
|
2077
|
+
10, 10, QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Minimum
|
|
2078
|
+
)
|
|
2079
|
+
|
|
2080
|
+
self.default_epoch_layout.addItem(self.horizontalSpacer_71)
|
|
2081
|
+
|
|
2082
|
+
self.horizontalLayout_60 = QHBoxLayout()
|
|
2083
|
+
self.horizontalLayout_60.setObjectName("horizontalLayout_60")
|
|
2084
|
+
self.label_17 = QLabel(self.settings_tab)
|
|
2085
|
+
self.label_17.setObjectName("label_17")
|
|
2086
|
+
sizePolicy1.setHeightForWidth(self.label_17.sizePolicy().hasHeightForWidth())
|
|
2087
|
+
self.label_17.setSizePolicy(sizePolicy1)
|
|
2088
|
+
|
|
2089
|
+
self.horizontalLayout_60.addWidget(self.label_17)
|
|
2090
|
+
|
|
2091
|
+
self.default_epoch_input = QDoubleSpinBox(self.settings_tab)
|
|
2092
|
+
self.default_epoch_input.setObjectName("default_epoch_input")
|
|
2093
|
+
sizePolicy1.setHeightForWidth(
|
|
2094
|
+
self.default_epoch_input.sizePolicy().hasHeightForWidth()
|
|
2095
|
+
)
|
|
2096
|
+
self.default_epoch_input.setSizePolicy(sizePolicy1)
|
|
2097
|
+
self.default_epoch_input.setMaximum(100000.000000000000000)
|
|
2098
|
+
self.default_epoch_input.setSingleStep(0.500000000000000)
|
|
2099
|
+
|
|
2100
|
+
self.horizontalLayout_60.addWidget(self.default_epoch_input)
|
|
2101
|
+
|
|
2102
|
+
self.default_epoch_layout.addLayout(self.horizontalLayout_60)
|
|
2103
|
+
|
|
2104
|
+
self.horizontalSpacer_70 = QSpacerItem(
|
|
2105
|
+
10, 10, QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Minimum
|
|
2106
|
+
)
|
|
2107
|
+
|
|
2108
|
+
self.default_epoch_layout.addItem(self.horizontalSpacer_70)
|
|
2109
|
+
|
|
2110
|
+
self.verticalLayout_6.addLayout(self.default_epoch_layout)
|
|
2111
|
+
|
|
2047
2112
|
self.horizontalLayout_18 = QHBoxLayout()
|
|
2048
2113
|
self.horizontalLayout_18.setSpacing(10)
|
|
2049
2114
|
self.horizontalLayout_18.setObjectName("horizontalLayout_18")
|
|
@@ -2079,6 +2144,7 @@ class Ui_PrimaryWindow(object):
|
|
|
2079
2144
|
self.verticalLayout_6.addLayout(self.horizontalLayout_18)
|
|
2080
2145
|
|
|
2081
2146
|
self.verticalLayout_6.setStretch(0, 2)
|
|
2147
|
+
self.verticalLayout_6.setStretch(1, 2)
|
|
2082
2148
|
self.verticalLayout_6.setStretch(2, 2)
|
|
2083
2149
|
self.verticalLayout_6.setStretch(3, 2)
|
|
2084
2150
|
self.verticalLayout_6.setStretch(4, 2)
|
|
@@ -2088,7 +2154,8 @@ class Ui_PrimaryWindow(object):
|
|
|
2088
2154
|
self.verticalLayout_6.setStretch(8, 2)
|
|
2089
2155
|
self.verticalLayout_6.setStretch(9, 2)
|
|
2090
2156
|
self.verticalLayout_6.setStretch(10, 2)
|
|
2091
|
-
self.verticalLayout_6.setStretch(11,
|
|
2157
|
+
self.verticalLayout_6.setStretch(11, 2)
|
|
2158
|
+
self.verticalLayout_6.setStretch(12, 3)
|
|
2092
2159
|
|
|
2093
2160
|
self.settings_tab_layout.addLayout(self.verticalLayout_6, 0, 0, 1, 1)
|
|
2094
2161
|
|
|
@@ -2352,6 +2419,9 @@ class Ui_PrimaryWindow(object):
|
|
|
2352
2419
|
self.label_3.setText(QCoreApplication.translate("PrimaryWindow", "0", None))
|
|
2353
2420
|
self.enable_state_0.setText("")
|
|
2354
2421
|
self.state_scored_0.setText("")
|
|
2422
|
+
self.label_17.setText(
|
|
2423
|
+
QCoreApplication.translate("PrimaryWindow", "Default epoch length:", None)
|
|
2424
|
+
)
|
|
2355
2425
|
self.save_config_button.setText(
|
|
2356
2426
|
QCoreApplication.translate("PrimaryWindow", "Save", None)
|
|
2357
2427
|
)
|
accusleepy/gui/primary_window.ui
CHANGED
|
@@ -164,6 +164,9 @@
|
|
|
164
164
|
<verstretch>0</verstretch>
|
|
165
165
|
</sizepolicy>
|
|
166
166
|
</property>
|
|
167
|
+
<property name="alignment">
|
|
168
|
+
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
|
|
169
|
+
</property>
|
|
167
170
|
<property name="maximum">
|
|
168
171
|
<double>100000.000000000000000</double>
|
|
169
172
|
</property>
|
|
@@ -512,6 +515,9 @@ color: rgb(244, 195, 68);</string>
|
|
|
512
515
|
<verstretch>0</verstretch>
|
|
513
516
|
</sizepolicy>
|
|
514
517
|
</property>
|
|
518
|
+
<property name="alignment">
|
|
519
|
+
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
|
|
520
|
+
</property>
|
|
515
521
|
<property name="minimum">
|
|
516
522
|
<double>0.000000000000000</double>
|
|
517
523
|
</property>
|
|
@@ -982,6 +988,9 @@ color: rgb(244, 195, 68);</string>
|
|
|
982
988
|
<verstretch>0</verstretch>
|
|
983
989
|
</sizepolicy>
|
|
984
990
|
</property>
|
|
991
|
+
<property name="alignment">
|
|
992
|
+
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
|
|
993
|
+
</property>
|
|
985
994
|
<property name="decimals">
|
|
986
995
|
<number>2</number>
|
|
987
996
|
</property>
|
|
@@ -1465,7 +1474,7 @@ color: rgb(244, 195, 68);</string>
|
|
|
1465
1474
|
</layout>
|
|
1466
1475
|
</item>
|
|
1467
1476
|
<item row="0" column="0">
|
|
1468
|
-
<layout class="QVBoxLayout" name="verticalLayout_6" stretch="2,
|
|
1477
|
+
<layout class="QVBoxLayout" name="verticalLayout_6" stretch="2,2,2,2,2,2,2,2,2,2,2,2,3">
|
|
1469
1478
|
<item>
|
|
1470
1479
|
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="3,3,4,3,4">
|
|
1471
1480
|
<property name="spacing">
|
|
@@ -3446,6 +3455,75 @@ color: rgb(244, 195, 68);</string>
|
|
|
3446
3455
|
</item>
|
|
3447
3456
|
</layout>
|
|
3448
3457
|
</item>
|
|
3458
|
+
<item>
|
|
3459
|
+
<layout class="QHBoxLayout" name="default_epoch_layout">
|
|
3460
|
+
<item>
|
|
3461
|
+
<spacer name="horizontalSpacer_71">
|
|
3462
|
+
<property name="orientation">
|
|
3463
|
+
<enum>Qt::Orientation::Horizontal</enum>
|
|
3464
|
+
</property>
|
|
3465
|
+
<property name="sizeType">
|
|
3466
|
+
<enum>QSizePolicy::Policy::Preferred</enum>
|
|
3467
|
+
</property>
|
|
3468
|
+
<property name="sizeHint" stdset="0">
|
|
3469
|
+
<size>
|
|
3470
|
+
<width>10</width>
|
|
3471
|
+
<height>10</height>
|
|
3472
|
+
</size>
|
|
3473
|
+
</property>
|
|
3474
|
+
</spacer>
|
|
3475
|
+
</item>
|
|
3476
|
+
<item>
|
|
3477
|
+
<layout class="QHBoxLayout" name="horizontalLayout_60">
|
|
3478
|
+
<item>
|
|
3479
|
+
<widget class="QLabel" name="label_17">
|
|
3480
|
+
<property name="sizePolicy">
|
|
3481
|
+
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
3482
|
+
<horstretch>0</horstretch>
|
|
3483
|
+
<verstretch>0</verstretch>
|
|
3484
|
+
</sizepolicy>
|
|
3485
|
+
</property>
|
|
3486
|
+
<property name="text">
|
|
3487
|
+
<string>Default epoch length:</string>
|
|
3488
|
+
</property>
|
|
3489
|
+
</widget>
|
|
3490
|
+
</item>
|
|
3491
|
+
<item>
|
|
3492
|
+
<widget class="QDoubleSpinBox" name="default_epoch_input">
|
|
3493
|
+
<property name="sizePolicy">
|
|
3494
|
+
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
3495
|
+
<horstretch>0</horstretch>
|
|
3496
|
+
<verstretch>0</verstretch>
|
|
3497
|
+
</sizepolicy>
|
|
3498
|
+
</property>
|
|
3499
|
+
<property name="maximum">
|
|
3500
|
+
<double>100000.000000000000000</double>
|
|
3501
|
+
</property>
|
|
3502
|
+
<property name="singleStep">
|
|
3503
|
+
<double>0.500000000000000</double>
|
|
3504
|
+
</property>
|
|
3505
|
+
</widget>
|
|
3506
|
+
</item>
|
|
3507
|
+
</layout>
|
|
3508
|
+
</item>
|
|
3509
|
+
<item>
|
|
3510
|
+
<spacer name="horizontalSpacer_70">
|
|
3511
|
+
<property name="orientation">
|
|
3512
|
+
<enum>Qt::Orientation::Horizontal</enum>
|
|
3513
|
+
</property>
|
|
3514
|
+
<property name="sizeType">
|
|
3515
|
+
<enum>QSizePolicy::Policy::Preferred</enum>
|
|
3516
|
+
</property>
|
|
3517
|
+
<property name="sizeHint" stdset="0">
|
|
3518
|
+
<size>
|
|
3519
|
+
<width>10</width>
|
|
3520
|
+
<height>10</height>
|
|
3521
|
+
</size>
|
|
3522
|
+
</property>
|
|
3523
|
+
</spacer>
|
|
3524
|
+
</item>
|
|
3525
|
+
</layout>
|
|
3526
|
+
</item>
|
|
3449
3527
|
<item>
|
|
3450
3528
|
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="6,1,7">
|
|
3451
3529
|
<property name="spacing">
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
from accusleepy.constants import BRAIN_STATE_COL, EEG_COL, EMG_COL, UNDEFINED_LABEL
|
|
2
|
+
|
|
3
|
+
MAIN_GUIDE_TEXT = f"""
|
|
1
4
|
Section 1: Overview of the GUI
|
|
2
5
|
Section 2: AccuSleePy file types
|
|
3
6
|
Section 3: Manually assigning brain state labels
|
|
@@ -41,17 +44,16 @@ To select a file in the primary interface, you can either use the
|
|
|
41
44
|
associated button, or drag/drop the file into the empty box adjacent
|
|
42
45
|
to the button.
|
|
43
46
|
Recording file: a .csv or .parquet file containing one column of EEG
|
|
44
|
-
data and one column of EMG data. The column names must
|
|
45
|
-
|
|
46
|
-
Label file: a .csv file with one column
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
and REM = 1, wake = 2, NREM = 3.
|
|
47
|
+
data and one column of EMG data. The column names must be
|
|
48
|
+
{EEG_COL} and {EMG_COL}.
|
|
49
|
+
Label file: a .csv file with one column titled {BRAIN_STATE_COL}
|
|
50
|
+
with entries that are either the undefined brain state, {UNDEFINED_LABEL},
|
|
51
|
+
or one of the digits in your brain state configuration.
|
|
52
|
+
By default, these are 1-3 where REM = 1, wake = 2, NREM = 3.
|
|
51
53
|
Calibration data file: required for automatic labeling. See Section 4
|
|
52
|
-
for details.
|
|
54
|
+
for details. These have .csv format.
|
|
53
55
|
Trained classification model: required for automatic labeling. See
|
|
54
|
-
Section 4 for details.
|
|
56
|
+
Section 4 for details. These have .pth format.
|
|
55
57
|
|
|
56
58
|
-----------------------------------------------------------------------
|
|
57
59
|
Section 3: Manually assigning brain state labels
|
|
@@ -75,6 +77,12 @@ Section 4: Automatically scoring recordings with a classification model
|
|
|
75
77
|
Automatic brain state scoring requires the inputs described in
|
|
76
78
|
Section 3, as well as calibration data files and a trained classifier.
|
|
77
79
|
If you already have all of these files, proceed to Section 4C.
|
|
80
|
+
Models trained on the AccuSleep dataset are provided at
|
|
81
|
+
https://osf.io/py5eb under /python_format/models/ for epoch lengths of
|
|
82
|
+
2.5, 4, 5, and 10 seconds. These models are the "default" type, in that
|
|
83
|
+
they use several epochs of data before and after any given epoch when
|
|
84
|
+
scoring that epoch. (The other model type, called "real-time", only
|
|
85
|
+
uses data from the current epoch and several preceding epochs.)
|
|
78
86
|
|
|
79
87
|
--- Section 4A: Creating calibration data files ---
|
|
80
88
|
Each recording must have a calibration file assigned to it.
|
|
@@ -97,12 +105,6 @@ To create a calibration data file:
|
|
|
97
105
|
selected recording.
|
|
98
106
|
|
|
99
107
|
--- Section 4B: Training your own classification model ---
|
|
100
|
-
Pre-trained classification models are provided with AccuSleePy for
|
|
101
|
-
epoch lengths of 2.5, 4, 5, and 10 seconds. These models are the
|
|
102
|
-
"default" type, in that they use several epochs of data before and
|
|
103
|
-
after any given epoch when scoring that epoch. (The other model type,
|
|
104
|
-
called "real-time", only uses data from the current epoch and several
|
|
105
|
-
preceding epochs.
|
|
106
108
|
To train a new model on your own data:
|
|
107
109
|
1. Add your scored recordings to the recording list. Make sure the
|
|
108
110
|
sampling rate, recording file, and label file are set for each
|
|
@@ -113,11 +115,15 @@ To train a new model on your own data:
|
|
|
113
115
|
type models, this must be an odd number. In general, about 30
|
|
114
116
|
seconds worth of data is enough.
|
|
115
117
|
4. Choose whether the images used to train the model should be
|
|
116
|
-
deleted once training is complete.
|
|
117
|
-
|
|
118
|
+
deleted once training is complete. (It's generally best to
|
|
119
|
+
leave this box checked.)
|
|
120
|
+
5. Choose whether to create a "default" or "real-time"-type model.
|
|
121
|
+
Note that scoring recordings in the GUI requires a default-type
|
|
122
|
+
model.
|
|
123
|
+
6. Select a directory where the training images will be saved. A
|
|
118
124
|
new directory with an automatically generated name will be
|
|
119
125
|
created inside the directory you choose.
|
|
120
|
-
|
|
126
|
+
7. Click the "Train classification model" button and enter a
|
|
121
127
|
filename for the trained model. Training can take some time.
|
|
122
128
|
|
|
123
129
|
--- Section 4C: Automatic labeling ---
|
|
@@ -127,8 +133,8 @@ Instructions for automatic labeling using this GUI are below.
|
|
|
127
133
|
for each recording. See section 4A for instructions on creating
|
|
128
134
|
calibration files.
|
|
129
135
|
3. Click 'Load classification model' to load the trained classification
|
|
130
|
-
model.
|
|
131
|
-
the same as the current epoch length.
|
|
136
|
+
model. It's important that the epoch length used when training this
|
|
137
|
+
model is the same as the current epoch length.
|
|
132
138
|
4. If you wish to preserve any existing labels in the label file, and
|
|
133
139
|
only overwrite undefined epochs, check the box labeled
|
|
134
140
|
'Only overwrite undefined epochs'.
|
|
@@ -140,3 +146,4 @@ Instructions for automatic labeling using this GUI are below.
|
|
|
140
146
|
recording list. Labels will be saved to the file specified by
|
|
141
147
|
the 'Select or create label file' field of each recording. You can
|
|
142
148
|
click 'Score manually' to visualize the results.
|
|
149
|
+
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: accusleepy
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Python implementation of AccuSleep
|
|
5
5
|
License: GPL-3.0-only
|
|
6
6
|
Author: Zeke Barger
|
|
@@ -30,16 +30,19 @@ Description-Content-Type: text/markdown
|
|
|
30
30
|
## Description
|
|
31
31
|
|
|
32
32
|
AccuSleePy is a python implementation of AccuSleep--a set of graphical user interfaces for scoring rodent
|
|
33
|
-
sleep using EEG and EMG recordings.
|
|
33
|
+
sleep using EEG and EMG recordings. It offers several improvements over the original MATLAB version
|
|
34
|
+
and is the only version that will be actively maintained.
|
|
35
|
+
|
|
36
|
+
If you use AccuSleep in your research, please cite our
|
|
34
37
|
[publication](https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0224642):
|
|
35
38
|
|
|
36
39
|
Barger, Z., Frye, C. G., Liu, D., Dan, Y., & Bouchard, K. E. (2019). Robust, automated sleep scoring by a compact neural network with distributional shift correction. *PLOS ONE, 14*(12), 1–18.
|
|
37
40
|
|
|
38
|
-
The data
|
|
41
|
+
The data and models associated with AccuSleep are available at https://osf.io/py5eb/
|
|
39
42
|
|
|
40
43
|
Please contact zekebarger (at) gmail (dot) com with any questions or comments about the software.
|
|
41
44
|
|
|
42
|
-
## Installation
|
|
45
|
+
## Installation
|
|
43
46
|
|
|
44
47
|
- (recommended) create a new virtual environment (using
|
|
45
48
|
[venv](https://docs.python.org/3/library/venv.html),
|
|
@@ -47,10 +50,11 @@ Please contact zekebarger (at) gmail (dot) com with any questions or comments ab
|
|
|
47
50
|
etc.) using python >=3.10,<3.13
|
|
48
51
|
- (optional) if you have a CUDA device and want to speed up model training, [install PyTorch](https://pytorch.org/)
|
|
49
52
|
- `pip install accusleepy`
|
|
53
|
+
- (optional) download a classification model from https://osf.io/py5eb/ under /python_format/models/
|
|
50
54
|
|
|
51
55
|
## Usage
|
|
52
56
|
|
|
53
|
-
`python -m accusleepy`
|
|
57
|
+
`python -m accusleepy` will open the primary interface.
|
|
54
58
|
|
|
55
59
|
## Acknowledgements
|
|
56
60
|
|
|
@@ -2,9 +2,9 @@ accusleepy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
2
2
|
accusleepy/__main__.py,sha256=dKzl2N2Hg9lD264CWYNxThRyDKzWwyMwHRXmJxOmMis,104
|
|
3
3
|
accusleepy/brain_state_set.py,sha256=fRkrArHLIbEKimub804yt_mUXoyfsjJEfiJnTjeCMkY,3233
|
|
4
4
|
accusleepy/classification.py,sha256=xrmPyMHlzYh0QfNCID1PRIYEIyNkWduOi7g1Bdb6xfg,8573
|
|
5
|
-
accusleepy/config.json,sha256=
|
|
6
|
-
accusleepy/constants.py,sha256=
|
|
7
|
-
accusleepy/fileio.py,sha256=
|
|
5
|
+
accusleepy/config.json,sha256=F76WRLarMEW38BBMPwFlQ_d7Dur-ptqYmW8BxqnQF4A,464
|
|
6
|
+
accusleepy/constants.py,sha256=PnsPANggyIfMfd6OCR-kNztFOTybUEhMnPeibu5_eEU,1280
|
|
7
|
+
accusleepy/fileio.py,sha256=S5pf_hE-btJPMbrTplKLaQTULSJQoOJ-56LBH79Uz3I,6383
|
|
8
8
|
accusleepy/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
accusleepy/gui/icons/brightness_down.png,sha256=PLT1fb83RHIhSRuU7MMMx0G7oJAY7o9wUcnqM8veZfM,12432
|
|
10
10
|
accusleepy/gui/icons/brightness_up.png,sha256=64GnUqgPvN5xZ6Um3wOzwqvUmdAWYZT6eFmWpBsHyks,12989
|
|
@@ -17,21 +17,21 @@ accusleepy/gui/icons/save.png,sha256=J3EA8iU1BqLYRSsrq_OdoZlqrv2yfL7oV54DklTy_DI
|
|
|
17
17
|
accusleepy/gui/icons/up_arrow.png,sha256=V9yF9t1WgjPaUu-mF1YGe_DfaRHg2dUpR_sUVVcvVvY,3329
|
|
18
18
|
accusleepy/gui/icons/zoom_in.png,sha256=MFWnKZp7Rvh4bLPq4Cqo4sB_jQYedUUtT8-ZO8tNYyc,13589
|
|
19
19
|
accusleepy/gui/icons/zoom_out.png,sha256=IB8Jecb3i0U4qjWRR46ridjLpvLCSe7PozBaLqQqYSw,13055
|
|
20
|
-
accusleepy/gui/main.py,sha256=
|
|
21
|
-
accusleepy/gui/manual_scoring.py,sha256=
|
|
20
|
+
accusleepy/gui/main.py,sha256=IGhSD9vSTO09H72Ou1icbujQfzCy1twTCdeR2Dv8I7s,53055
|
|
21
|
+
accusleepy/gui/manual_scoring.py,sha256=Jfu7uxXk6W4mmq81h4LnLSArP735l0XvbuZq9yYG650,40686
|
|
22
22
|
accusleepy/gui/mplwidget.py,sha256=f9O3u_96whQGUwpi3o_QGc7yjiETX5vE0oj3ePXTJWE,12279
|
|
23
|
-
accusleepy/gui/primary_window.py,sha256=
|
|
24
|
-
accusleepy/gui/primary_window.ui,sha256=
|
|
23
|
+
accusleepy/gui/primary_window.py,sha256=eK9yrU6PK5hX1RORjc7DvqCDuDt55iA1LylXp3oh0j8,101850
|
|
24
|
+
accusleepy/gui/primary_window.ui,sha256=NqCSnvlC1mKrWNn034u8avpMF7WudlR8G5jLjFNdTmk,144812
|
|
25
25
|
accusleepy/gui/resources.qrc,sha256=ByNEmJqr0YbKBqoZGvONZtjyNYr4ST4enO6TEdYSqWg,802
|
|
26
26
|
accusleepy/gui/resources_rc.py,sha256=Z2e34h30U4snJjnYdZVV9B6yjATKxxfvgTRt5uXtQdo,329727
|
|
27
|
-
accusleepy/gui/text/config_guide.txt,sha256=
|
|
28
|
-
accusleepy/gui/text/
|
|
27
|
+
accusleepy/gui/text/config_guide.txt,sha256=wz2QtRgd1eXUZMmtfAIVgqvDOtjkOnTOK-IGkLALg4U,1073
|
|
28
|
+
accusleepy/gui/text/main_guide_text.py,sha256=YP-2sexvU5VIeKc1FXHsG0cUCgGfcU4qLD59JRlOMMo,8038
|
|
29
29
|
accusleepy/gui/text/manual_scoring_guide.txt,sha256=onBnUZJyX18oN1CgjD2HSnlEQHsUscHpOYf11kTKZ4U,1460
|
|
30
30
|
accusleepy/gui/viewer_window.py,sha256=5PkbuYMXUegH1CExCoqSGDZ9GeJqCCUz0-3WWkM8Vfc,24049
|
|
31
31
|
accusleepy/gui/viewer_window.ui,sha256=D1LwUFR-kZ_GWGZFFtXvGJdFWghLrOWZTblQeLQt9kI,30525
|
|
32
32
|
accusleepy/models.py,sha256=Muapsw088AUHqRIbW97Rkbv0oiwCtQvO9tEoBCC-MYg,1476
|
|
33
33
|
accusleepy/multitaper.py,sha256=V6MJDk0OSWhg2MFhrnt9dvYrHiNsk2T7IxAA7paZVyE,25549
|
|
34
34
|
accusleepy/signal_processing.py,sha256=-aXnywfp1LBsk3DcbIMmZlgv3f8j6sZ6js0bizZId0o,21718
|
|
35
|
-
accusleepy-0.1.
|
|
36
|
-
accusleepy-0.1.
|
|
37
|
-
accusleepy-0.1.
|
|
35
|
+
accusleepy-0.3.1.dist-info/METADATA,sha256=xgwUR8pWzqQp3fi_g6UXCVbvxrL3CdVahoq476bIEaQ,2729
|
|
36
|
+
accusleepy-0.3.1.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
|
37
|
+
accusleepy-0.3.1.dist-info/RECORD,,
|
|
File without changes
|