celldetective 1.5.0b7__py3-none-any.whl → 1.5.0b8__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.
- celldetective/_version.py +1 -1
- celldetective/event_detection_models.py +2463 -0
- celldetective/gui/base/channel_norm_generator.py +19 -3
- celldetective/gui/base/figure_canvas.py +1 -1
- celldetective/gui/base_annotator.py +2 -5
- celldetective/gui/event_annotator.py +248 -138
- celldetective/gui/pair_event_annotator.py +146 -20
- celldetective/gui/process_block.py +2 -2
- celldetective/gui/seg_model_loader.py +4 -4
- celldetective/gui/settings/_settings_event_model_training.py +32 -14
- celldetective/gui/settings/_settings_segmentation_model_training.py +5 -5
- celldetective/gui/settings/_settings_signal_annotator.py +0 -19
- celldetective/gui/viewers/base_viewer.py +17 -20
- celldetective/processes/train_signal_model.py +1 -1
- celldetective/scripts/train_signal_model.py +1 -1
- celldetective/signals.py +4 -2426
- celldetective/utils/event_detection/__init__.py +1 -1
- {celldetective-1.5.0b7.dist-info → celldetective-1.5.0b8.dist-info}/METADATA +1 -1
- {celldetective-1.5.0b7.dist-info → celldetective-1.5.0b8.dist-info}/RECORD +24 -23
- tests/test_signals.py +4 -4
- {celldetective-1.5.0b7.dist-info → celldetective-1.5.0b8.dist-info}/WHEEL +0 -0
- {celldetective-1.5.0b7.dist-info → celldetective-1.5.0b8.dist-info}/entry_points.txt +0 -0
- {celldetective-1.5.0b7.dist-info → celldetective-1.5.0b8.dist-info}/licenses/LICENSE +0 -0
- {celldetective-1.5.0b7.dist-info → celldetective-1.5.0b8.dist-info}/top_level.txt +0 -0
|
@@ -21,7 +21,7 @@ from celldetective.gui.base.components import (
|
|
|
21
21
|
CelldetectiveWidget,
|
|
22
22
|
)
|
|
23
23
|
from celldetective.gui.base.utils import center_window
|
|
24
|
-
from superqt import QLabeledDoubleRangeSlider, QSearchableComboBox
|
|
24
|
+
from superqt import QLabeledDoubleRangeSlider, QSearchableComboBox, QLabeledSlider
|
|
25
25
|
from celldetective import (
|
|
26
26
|
get_software_location,
|
|
27
27
|
)
|
|
@@ -466,11 +466,26 @@ class PairEventAnnotator(CelldetectiveMainWindow):
|
|
|
466
466
|
# Animation
|
|
467
467
|
animation_buttons_box = QHBoxLayout()
|
|
468
468
|
|
|
469
|
+
self.speed_slider = QLabeledSlider(Qt.Horizontal)
|
|
470
|
+
self.speed_slider.setRange(1, 60)
|
|
471
|
+
# Convert initial interval (ms) to FPS
|
|
472
|
+
initial_fps = int(1000 / max(1, self.anim_interval))
|
|
473
|
+
self.speed_slider.setValue(initial_fps)
|
|
474
|
+
self.speed_slider.valueChanged.connect(self.update_speed)
|
|
475
|
+
self.speed_slider.setFixedWidth(200)
|
|
476
|
+
self.speed_slider.setToolTip("Adjust animation framerate (FPS)")
|
|
477
|
+
|
|
478
|
+
speed_layout = QHBoxLayout()
|
|
479
|
+
speed_layout.addWidget(QLabel("Framerate: "))
|
|
480
|
+
speed_layout.addWidget(self.speed_slider)
|
|
481
|
+
|
|
469
482
|
animation_buttons_box.addWidget(self.frame_lbl, 20, alignment=Qt.AlignLeft)
|
|
483
|
+
animation_buttons_box.addLayout(speed_layout, 20)
|
|
470
484
|
|
|
471
485
|
self.first_frame_btn = QPushButton()
|
|
472
486
|
self.first_frame_btn.clicked.connect(self.set_first_frame)
|
|
473
|
-
self.
|
|
487
|
+
self.first_short = QShortcut(QKeySequence("f"), self)
|
|
488
|
+
self.first_short.activated.connect(self.set_first_frame)
|
|
474
489
|
self.first_frame_btn.setIcon(icon(MDI6.page_first, color="black"))
|
|
475
490
|
self.first_frame_btn.setStyleSheet(self.button_select_all)
|
|
476
491
|
self.first_frame_btn.setFixedSize(QSize(60, 60))
|
|
@@ -478,7 +493,8 @@ class PairEventAnnotator(CelldetectiveMainWindow):
|
|
|
478
493
|
|
|
479
494
|
self.last_frame_btn = QPushButton()
|
|
480
495
|
self.last_frame_btn.clicked.connect(self.set_last_frame)
|
|
481
|
-
self.
|
|
496
|
+
self.last_short = QShortcut(QKeySequence("l"), self)
|
|
497
|
+
self.last_short.activated.connect(self.set_last_frame)
|
|
482
498
|
self.last_frame_btn.setIcon(icon(MDI6.page_last, color="black"))
|
|
483
499
|
self.last_frame_btn.setStyleSheet(self.button_select_all)
|
|
484
500
|
self.last_frame_btn.setFixedSize(QSize(60, 60))
|
|
@@ -499,11 +515,32 @@ class PairEventAnnotator(CelldetectiveMainWindow):
|
|
|
499
515
|
self.start_btn.setIconSize(QSize(30, 30))
|
|
500
516
|
self.start_btn.hide()
|
|
501
517
|
|
|
518
|
+
self.toggle_short = QShortcut(Qt.Key_Space, self)
|
|
519
|
+
self.toggle_short.activated.connect(self.toggle_animation)
|
|
520
|
+
|
|
521
|
+
self.prev_frame_btn = QPushButton()
|
|
522
|
+
self.prev_frame_btn.clicked.connect(self.prev_frame)
|
|
523
|
+
self.prev_frame_btn.setIcon(icon(MDI6.chevron_left, color="black"))
|
|
524
|
+
self.prev_frame_btn.setStyleSheet(self.button_select_all)
|
|
525
|
+
self.prev_frame_btn.setFixedSize(QSize(60, 60))
|
|
526
|
+
self.prev_frame_btn.setIconSize(QSize(30, 30))
|
|
527
|
+
self.prev_frame_btn.setEnabled(False)
|
|
528
|
+
|
|
529
|
+
self.next_frame_btn = QPushButton()
|
|
530
|
+
self.next_frame_btn.clicked.connect(self.next_frame)
|
|
531
|
+
self.next_frame_btn.setIcon(icon(MDI6.chevron_right, color="black"))
|
|
532
|
+
self.next_frame_btn.setStyleSheet(self.button_select_all)
|
|
533
|
+
self.next_frame_btn.setFixedSize(QSize(60, 60))
|
|
534
|
+
self.next_frame_btn.setIconSize(QSize(30, 30))
|
|
535
|
+
self.next_frame_btn.setEnabled(False)
|
|
536
|
+
|
|
502
537
|
animation_buttons_box.addWidget(
|
|
503
538
|
self.first_frame_btn, 5, alignment=Qt.AlignRight
|
|
504
539
|
)
|
|
540
|
+
animation_buttons_box.addWidget(self.prev_frame_btn, 5, alignment=Qt.AlignRight)
|
|
505
541
|
animation_buttons_box.addWidget(self.stop_btn, 5, alignment=Qt.AlignRight)
|
|
506
542
|
animation_buttons_box.addWidget(self.start_btn, 5, alignment=Qt.AlignRight)
|
|
543
|
+
animation_buttons_box.addWidget(self.next_frame_btn, 5, alignment=Qt.AlignRight)
|
|
507
544
|
animation_buttons_box.addWidget(self.last_frame_btn, 5, alignment=Qt.AlignRight)
|
|
508
545
|
|
|
509
546
|
self.right_panel.addLayout(animation_buttons_box, 5)
|
|
@@ -1968,10 +2005,7 @@ class PairEventAnnotator(CelldetectiveMainWindow):
|
|
|
1968
2005
|
else:
|
|
1969
2006
|
self.fraction = 0.25
|
|
1970
2007
|
|
|
1971
|
-
|
|
1972
|
-
self.anim_interval = int(instructions["interval"])
|
|
1973
|
-
else:
|
|
1974
|
-
self.anim_interval = 1
|
|
2008
|
+
self.anim_interval = 33
|
|
1975
2009
|
|
|
1976
2010
|
if "log" in instructions:
|
|
1977
2011
|
self.log_option = instructions["log"]
|
|
@@ -1983,7 +2017,7 @@ class PairEventAnnotator(CelldetectiveMainWindow):
|
|
|
1983
2017
|
self.percentile_mode = True
|
|
1984
2018
|
self.target_channels = [[self.channel_names[0], 0.01, 99.99]]
|
|
1985
2019
|
self.fraction = 0.25
|
|
1986
|
-
self.anim_interval =
|
|
2020
|
+
self.anim_interval = 33
|
|
1987
2021
|
|
|
1988
2022
|
def prepare_stack(self):
|
|
1989
2023
|
|
|
@@ -2104,6 +2138,49 @@ class PairEventAnnotator(CelldetectiveMainWindow):
|
|
|
2104
2138
|
idx = self.relative_class_choice_cb.findText(self.relative_class)
|
|
2105
2139
|
self.relative_class_choice_cb.setCurrentIndex(idx)
|
|
2106
2140
|
|
|
2141
|
+
def update_speed(self):
|
|
2142
|
+
fps = self.speed_slider.value()
|
|
2143
|
+
# Convert FPS to interval in ms
|
|
2144
|
+
# FPS = 1000 / interval_ms => interval_ms = 1000 / FPS
|
|
2145
|
+
val = int(1000 / max(1, fps))
|
|
2146
|
+
|
|
2147
|
+
self.anim_interval = val
|
|
2148
|
+
print(
|
|
2149
|
+
f"DEBUG: Speed slider moved. FPS: {fps} -> Interval: {val} ms. Recreating animation object."
|
|
2150
|
+
)
|
|
2151
|
+
|
|
2152
|
+
# Check if animation is allowed to run (Pause button is visible means we are Playing)
|
|
2153
|
+
should_play = self.stop_btn.isVisible()
|
|
2154
|
+
|
|
2155
|
+
if hasattr(self, "anim") and self.anim:
|
|
2156
|
+
try:
|
|
2157
|
+
self.anim.event_source.stop()
|
|
2158
|
+
except Exception as e:
|
|
2159
|
+
print(f"DEBUG: Error stopping animation: {e}")
|
|
2160
|
+
|
|
2161
|
+
# Recreate animation with new interval
|
|
2162
|
+
try:
|
|
2163
|
+
# We must disconnect the old pick event to avoid accumulating connections
|
|
2164
|
+
# although mpl_connect returns a cid, we didn't store it properly before.
|
|
2165
|
+
# However, the canvas clears usually handle this if we cleared axes, but we aren't clearing axes here.
|
|
2166
|
+
# Ideally we should clean up, but for now let's focus on the animation object replacement.
|
|
2167
|
+
|
|
2168
|
+
self.anim = FuncAnimation(
|
|
2169
|
+
self.fig,
|
|
2170
|
+
self.draw_frame,
|
|
2171
|
+
frames=self.animation_generator,
|
|
2172
|
+
interval=self.anim_interval,
|
|
2173
|
+
blit=True,
|
|
2174
|
+
cache_frame_data=False,
|
|
2175
|
+
)
|
|
2176
|
+
|
|
2177
|
+
# If we were NOT playing (i.e. Paused), pause the new animation immediately
|
|
2178
|
+
if not should_play:
|
|
2179
|
+
self.anim.event_source.stop()
|
|
2180
|
+
|
|
2181
|
+
except Exception as e:
|
|
2182
|
+
print(f"DEBUG: Error recreating animation: {e}")
|
|
2183
|
+
|
|
2107
2184
|
def closeEvent(self, event):
|
|
2108
2185
|
|
|
2109
2186
|
self.stop()
|
|
@@ -2119,6 +2196,18 @@ class PairEventAnnotator(CelldetectiveMainWindow):
|
|
|
2119
2196
|
|
|
2120
2197
|
gc.collect()
|
|
2121
2198
|
|
|
2199
|
+
def animation_generator(self):
|
|
2200
|
+
"""
|
|
2201
|
+
Generator yielding frame indices for the animation,
|
|
2202
|
+
starting from the current self.framedata.
|
|
2203
|
+
"""
|
|
2204
|
+
i = self.framedata
|
|
2205
|
+
while True:
|
|
2206
|
+
yield i
|
|
2207
|
+
i += 1
|
|
2208
|
+
if i >= self.len_movie:
|
|
2209
|
+
i = 0
|
|
2210
|
+
|
|
2122
2211
|
def looped_animation(self):
|
|
2123
2212
|
"""
|
|
2124
2213
|
Load an image.
|
|
@@ -2195,9 +2284,10 @@ class PairEventAnnotator(CelldetectiveMainWindow):
|
|
|
2195
2284
|
self.anim = FuncAnimation(
|
|
2196
2285
|
self.fig,
|
|
2197
2286
|
self.draw_frame,
|
|
2198
|
-
frames=self.
|
|
2287
|
+
frames=self.animation_generator,
|
|
2199
2288
|
interval=self.anim_interval, # in ms
|
|
2200
2289
|
blit=True,
|
|
2290
|
+
cache_frame_data=False,
|
|
2201
2291
|
)
|
|
2202
2292
|
|
|
2203
2293
|
self.fig.canvas.mpl_connect("pick_event", self.on_scatter_pick)
|
|
@@ -2855,27 +2945,63 @@ class PairEventAnnotator(CelldetectiveMainWindow):
|
|
|
2855
2945
|
self.stop_btn.hide()
|
|
2856
2946
|
self.start_btn.show()
|
|
2857
2947
|
self.anim.pause()
|
|
2948
|
+
self.prev_frame_btn.setEnabled(True)
|
|
2949
|
+
self.next_frame_btn.setEnabled(True)
|
|
2858
2950
|
self.stop_btn.clicked.connect(self.start)
|
|
2859
2951
|
|
|
2860
2952
|
def start(self):
|
|
2861
2953
|
"""
|
|
2862
|
-
Starts interactive animation.
|
|
2863
|
-
handler, calls show to start the event loop.
|
|
2954
|
+
Starts interactive animation.
|
|
2864
2955
|
"""
|
|
2865
|
-
self.start_btn.setShortcut(QKeySequence(""))
|
|
2866
|
-
|
|
2867
|
-
self.last_frame_btn.setEnabled(True)
|
|
2868
|
-
self.last_frame_btn.clicked.connect(self.set_last_frame)
|
|
2869
|
-
|
|
2870
|
-
self.first_frame_btn.setEnabled(True)
|
|
2871
|
-
self.first_frame_btn.clicked.connect(self.set_first_frame)
|
|
2872
|
-
|
|
2873
2956
|
self.start_btn.hide()
|
|
2874
2957
|
self.stop_btn.show()
|
|
2875
2958
|
|
|
2876
|
-
self.
|
|
2959
|
+
self.prev_frame_btn.setEnabled(False)
|
|
2960
|
+
self.next_frame_btn.setEnabled(False)
|
|
2961
|
+
|
|
2962
|
+
self.anim.resume()
|
|
2877
2963
|
self.stop_btn.clicked.connect(self.stop)
|
|
2878
2964
|
|
|
2965
|
+
def toggle_animation(self):
|
|
2966
|
+
if self.stop_btn.isVisible():
|
|
2967
|
+
self.stop()
|
|
2968
|
+
else:
|
|
2969
|
+
self.start()
|
|
2970
|
+
|
|
2971
|
+
def next_frame(self):
|
|
2972
|
+
self.framedata += 1
|
|
2973
|
+
if self.framedata >= self.len_movie:
|
|
2974
|
+
self.framedata = 0
|
|
2975
|
+
self.draw_frame(self.framedata)
|
|
2976
|
+
self.fcanvas.canvas.draw()
|
|
2977
|
+
|
|
2978
|
+
def prev_frame(self):
|
|
2979
|
+
self.framedata -= 1
|
|
2980
|
+
if self.framedata < 0:
|
|
2981
|
+
self.framedata = self.len_movie - 1
|
|
2982
|
+
self.draw_frame(self.framedata)
|
|
2983
|
+
self.fcanvas.canvas.draw()
|
|
2984
|
+
|
|
2985
|
+
def set_first_frame(self):
|
|
2986
|
+
self.stop()
|
|
2987
|
+
self.framedata = 0
|
|
2988
|
+
self.draw_frame(self.framedata)
|
|
2989
|
+
self.fcanvas.canvas.draw()
|
|
2990
|
+
|
|
2991
|
+
def set_last_frame(self):
|
|
2992
|
+
self.stop()
|
|
2993
|
+
self.framedata = len(self.stack) - 1
|
|
2994
|
+
while len(np.where(self.stack[self.framedata].flatten() == 0)[0]) > 0.99 * len(
|
|
2995
|
+
self.stack[self.framedata].flatten()
|
|
2996
|
+
):
|
|
2997
|
+
self.framedata -= 1
|
|
2998
|
+
if self.framedata < 0:
|
|
2999
|
+
self.framedata = 0
|
|
3000
|
+
break
|
|
3001
|
+
|
|
3002
|
+
self.draw_frame(self.framedata)
|
|
3003
|
+
self.fcanvas.canvas.draw()
|
|
3004
|
+
|
|
2879
3005
|
def give_reference_cell_information(self):
|
|
2880
3006
|
|
|
2881
3007
|
df_reference = self.dataframes[self.reference_population]
|
|
@@ -1361,12 +1361,12 @@ class ProcessPanel(QFrame, Styles):
|
|
|
1361
1361
|
msgBox = QMessageBox()
|
|
1362
1362
|
msgBox.setIcon(QMessageBox.Question)
|
|
1363
1363
|
msgBox.setText(
|
|
1364
|
-
"A measurement table already exists. Previously annotated data for
|
|
1364
|
+
"A measurement table already exists. Previously annotated data for this position will be lost. Do you want to proceed?"
|
|
1365
1365
|
)
|
|
1366
1366
|
msgBox.setWindowTitle("Info")
|
|
1367
1367
|
msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
|
|
1368
1368
|
if msgBox.exec() == QMessageBox.No:
|
|
1369
|
-
|
|
1369
|
+
return None
|
|
1370
1370
|
|
|
1371
1371
|
if run_tracking:
|
|
1372
1372
|
track_args = {"mode": self.mode, "n_threads": self.n_threads}
|
|
@@ -129,14 +129,14 @@ class SegmentationModelLoader(CelldetectiveWidget):
|
|
|
129
129
|
def unlock_upload(self):
|
|
130
130
|
if self.stardist_button.isChecked():
|
|
131
131
|
if np.any(
|
|
132
|
-
[c.
|
|
132
|
+
[c.currentData() != "--" for c in self.channel_layout.channel_cbs]
|
|
133
133
|
):
|
|
134
134
|
self.upload_button.setEnabled(True)
|
|
135
135
|
else:
|
|
136
136
|
self.upload_button.setEnabled(False)
|
|
137
137
|
elif self.cellpose_button.isChecked():
|
|
138
138
|
if np.any(
|
|
139
|
-
[c.
|
|
139
|
+
[c.currentData() != "--" for c in self.channel_layout.channel_cbs]
|
|
140
140
|
):
|
|
141
141
|
self.upload_button.setEnabled(True)
|
|
142
142
|
else:
|
|
@@ -373,7 +373,7 @@ class SegmentationModelLoader(CelldetectiveWidget):
|
|
|
373
373
|
|
|
374
374
|
channels = []
|
|
375
375
|
for i in range(len(self.channel_layout.channel_cbs)):
|
|
376
|
-
channels.append(self.channel_layout.channel_cbs[i].
|
|
376
|
+
channels.append(self.channel_layout.channel_cbs[i].currentData())
|
|
377
377
|
|
|
378
378
|
if self.file_label.text() == "No file chosen":
|
|
379
379
|
generic_message("Please select a model first...")
|
|
@@ -450,7 +450,7 @@ class SegmentationModelLoader(CelldetectiveWidget):
|
|
|
450
450
|
|
|
451
451
|
channels = []
|
|
452
452
|
for i in range(len(self.channel_layout.channel_cbs)):
|
|
453
|
-
channels.append(self.channel_layout.channel_cbs[i].
|
|
453
|
+
channels.append(self.channel_layout.channel_cbs[i].currentData())
|
|
454
454
|
|
|
455
455
|
slots_to_keep = np.where(np.array(channels) != "--")[0]
|
|
456
456
|
while "--" in channels:
|
|
@@ -417,8 +417,7 @@ class SettingsEventDetectionModelTraining(CelldetectiveSettingsPanel):
|
|
|
417
417
|
self.signals = ["--"] + num_cols_pairs + num_cols_reference + num_cols_neighbor
|
|
418
418
|
|
|
419
419
|
for cb in self.ch_norm.channel_cbs:
|
|
420
|
-
cb.
|
|
421
|
-
cb.addItems(self.signals)
|
|
420
|
+
self.ch_norm.add_items_truncated(cb, self.signals)
|
|
422
421
|
|
|
423
422
|
def fill_available_neighborhoods(self):
|
|
424
423
|
|
|
@@ -541,15 +540,36 @@ class SettingsEventDetectionModelTraining(CelldetectiveSettingsPanel):
|
|
|
541
540
|
self.model_length_slider.setValue(int(signal_length))
|
|
542
541
|
self.model_length_slider.setEnabled(False)
|
|
543
542
|
|
|
544
|
-
|
|
545
|
-
|
|
543
|
+
try:
|
|
544
|
+
norm_perc = data["normalization_percentile"]
|
|
545
|
+
if isinstance(norm_perc, bool):
|
|
546
|
+
norm_perc = [norm_perc] * len(channels)
|
|
547
|
+
norm_val = data["normalization_values"]
|
|
548
|
+
if len(norm_val) == 2 and isinstance(norm_val[0], float):
|
|
549
|
+
norm_val = [norm_val] * len(channels)
|
|
550
|
+
norm_clip = data["normalization_clip"]
|
|
551
|
+
if isinstance(norm_clip, bool):
|
|
552
|
+
norm_clip = [norm_clip] * len(channels)
|
|
553
|
+
except Exception:
|
|
554
|
+
norm_perc = [True] * len(channels)
|
|
555
|
+
norm_val = [[0.1, 99.99]] * len(channels)
|
|
556
|
+
norm_clip = [False] * len(channels)
|
|
557
|
+
|
|
558
|
+
for k, (c, cb) in enumerate(zip(channels, self.ch_norm.channel_cbs)):
|
|
559
|
+
index = cb.findData(c)
|
|
546
560
|
cb.setCurrentIndex(index)
|
|
547
561
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
self.ch_norm.
|
|
551
|
-
|
|
552
|
-
|
|
562
|
+
# Set normalization mode
|
|
563
|
+
if self.ch_norm.normalization_mode[k] != norm_perc[k]:
|
|
564
|
+
self.ch_norm.switch_normalization_mode(k)
|
|
565
|
+
|
|
566
|
+
# Set clipping mode
|
|
567
|
+
if self.ch_norm.clip_option[k] != norm_clip[k]:
|
|
568
|
+
self.ch_norm.switch_clipping_mode(k)
|
|
569
|
+
|
|
570
|
+
# Set normalization values
|
|
571
|
+
self.ch_norm.normalization_min_value_le[k].setText(str(norm_val[k][0]))
|
|
572
|
+
self.ch_norm.normalization_max_value_le[k].setText(str(norm_val[k][1]))
|
|
553
573
|
|
|
554
574
|
def adjust_scroll_area(self):
|
|
555
575
|
"""
|
|
@@ -581,7 +601,7 @@ class SettingsEventDetectionModelTraining(CelldetectiveSettingsPanel):
|
|
|
581
601
|
|
|
582
602
|
channels = []
|
|
583
603
|
for i in range(len(self.ch_norm.channel_cbs)):
|
|
584
|
-
channels.append(self.ch_norm.channel_cbs[i].
|
|
604
|
+
channels.append(self.ch_norm.channel_cbs[i].currentData())
|
|
585
605
|
|
|
586
606
|
slots_to_keep = np.where(np.array(channels) != "--")[0]
|
|
587
607
|
while "--" in channels:
|
|
@@ -750,10 +770,8 @@ class SettingsEventDetectionModelTraining(CelldetectiveSettingsPanel):
|
|
|
750
770
|
|
|
751
771
|
# Assuming signal/event models directory. Verifying path would be better but this fits pattern.
|
|
752
772
|
# If exact attribute unknown, use os.path.dirname logic or config methods.
|
|
753
|
-
# Safe bet: self.
|
|
754
|
-
model_path = os.path.join(
|
|
755
|
-
self.parent_window.signal_models_dir, self.modelname_le.text()
|
|
756
|
-
)
|
|
773
|
+
# Safe bet: self.signal_models_dir based on init.
|
|
774
|
+
model_path = os.path.join(self.signal_models_dir, self.modelname_le.text())
|
|
757
775
|
if os.path.exists(model_path):
|
|
758
776
|
time.sleep(0.5)
|
|
759
777
|
shutil.rmtree(model_path)
|
|
@@ -355,7 +355,7 @@ class SettingsSegmentationModelTraining(CelldetectiveSettingsPanel):
|
|
|
355
355
|
not current_name in models
|
|
356
356
|
and not self.spatial_calib_le.text() == ""
|
|
357
357
|
and not np.all(
|
|
358
|
-
[cb.
|
|
358
|
+
[cb.currentData() == "--" for cb in self.ch_norm.channel_cbs]
|
|
359
359
|
)
|
|
360
360
|
):
|
|
361
361
|
self.submit_btn.setEnabled(True)
|
|
@@ -370,7 +370,7 @@ class SettingsSegmentationModelTraining(CelldetectiveSettingsPanel):
|
|
|
370
370
|
self.submit_warning.setText(
|
|
371
371
|
"Please provide a valid spatial calibration..."
|
|
372
372
|
)
|
|
373
|
-
elif np.all([cb.
|
|
373
|
+
elif np.all([cb.currentData() == "--" for cb in self.ch_norm.channel_cbs]):
|
|
374
374
|
self.submit_warning.setText("Please provide valid channels...")
|
|
375
375
|
|
|
376
376
|
def rescale_slider(self):
|
|
@@ -459,7 +459,7 @@ class SettingsSegmentationModelTraining(CelldetectiveSettingsPanel):
|
|
|
459
459
|
|
|
460
460
|
for k in range(len(self.diamWidget.cellpose_channel_cb)):
|
|
461
461
|
ch = self.diamWidget.cellpose_channel_cb[k].currentText()
|
|
462
|
-
idx = self.ch_norm.channel_cbs[k].
|
|
462
|
+
idx = self.ch_norm.channel_cbs[k].findData(ch)
|
|
463
463
|
self.ch_norm.channel_cbs[k].setCurrentIndex(idx)
|
|
464
464
|
|
|
465
465
|
self.diamWidget.close()
|
|
@@ -561,7 +561,7 @@ class SettingsSegmentationModelTraining(CelldetectiveSettingsPanel):
|
|
|
561
561
|
self.cellpose_model.setChecked(True)
|
|
562
562
|
|
|
563
563
|
for c, cb in zip(channels, self.ch_norm.channel_cbs):
|
|
564
|
-
index = cb.
|
|
564
|
+
index = cb.findData(c)
|
|
565
565
|
cb.setCurrentIndex(index)
|
|
566
566
|
|
|
567
567
|
for i in range(len(channels)):
|
|
@@ -622,7 +622,7 @@ class SettingsSegmentationModelTraining(CelldetectiveSettingsPanel):
|
|
|
622
622
|
|
|
623
623
|
channels = []
|
|
624
624
|
for i in range(len(self.ch_norm.channel_cbs)):
|
|
625
|
-
channels.append(self.ch_norm.channel_cbs[i].
|
|
625
|
+
channels.append(self.ch_norm.channel_cbs[i].currentData())
|
|
626
626
|
|
|
627
627
|
slots_to_keep = np.where(np.array(channels) != "--")[0]
|
|
628
628
|
while "--" in channels:
|
|
@@ -100,11 +100,6 @@ class SettingsSignalAnnotator(CelldetectiveSettingsPanel):
|
|
|
100
100
|
hbox_frac.addWidget(self.fraction_slider, 80)
|
|
101
101
|
sub_layout.addLayout(hbox_frac)
|
|
102
102
|
|
|
103
|
-
hbox_interval = QHBoxLayout()
|
|
104
|
-
hbox_interval.addWidget(self._interval_lbl, 20)
|
|
105
|
-
hbox_interval.addWidget(self.interval_slider, 80)
|
|
106
|
-
sub_layout.addLayout(hbox_interval)
|
|
107
|
-
|
|
108
103
|
self._layout.addLayout(sub_layout)
|
|
109
104
|
|
|
110
105
|
self._layout.addWidget(self.submit_btn)
|
|
@@ -118,7 +113,6 @@ class SettingsSignalAnnotator(CelldetectiveSettingsPanel):
|
|
|
118
113
|
|
|
119
114
|
self._modality_lbl = QLabel("Modality: ")
|
|
120
115
|
self._fraction_lbl = QLabel("fraction: ")
|
|
121
|
-
self._interval_lbl = QLabel("interval [ms]: ")
|
|
122
116
|
|
|
123
117
|
self.gs_btn = QRadioButton("grayscale")
|
|
124
118
|
self.gs_btn.setChecked(True)
|
|
@@ -173,14 +167,6 @@ class SettingsSignalAnnotator(CelldetectiveSettingsPanel):
|
|
|
173
167
|
self.fraction_slider.setRange(0.1, 1)
|
|
174
168
|
self.fraction_slider.setValue(0.25)
|
|
175
169
|
|
|
176
|
-
self.interval_slider = QLabeledSlider()
|
|
177
|
-
self.interval_slider.setSingleStep(1)
|
|
178
|
-
self.interval_slider.setTickInterval(1)
|
|
179
|
-
self.interval_slider.setSingleStep(1)
|
|
180
|
-
self.interval_slider.setOrientation(Qt.Horizontal)
|
|
181
|
-
self.interval_slider.setRange(1, 1000)
|
|
182
|
-
self.interval_slider.setValue(1)
|
|
183
|
-
|
|
184
170
|
def enable_channels(self):
|
|
185
171
|
"""
|
|
186
172
|
Enable three channels when RGB mode is checked.
|
|
@@ -255,7 +241,6 @@ class SettingsSignalAnnotator(CelldetectiveSettingsPanel):
|
|
|
255
241
|
"rgb_mode": self.rgb_btn.isChecked(),
|
|
256
242
|
"percentile_mode": self.percentile_mode,
|
|
257
243
|
"fraction": float(self.fraction_slider.value()),
|
|
258
|
-
"interval": int(self.interval_slider.value()),
|
|
259
244
|
"log": self.log_option,
|
|
260
245
|
}
|
|
261
246
|
max_i = 3 if self.rgb_btn.isChecked() else 1
|
|
@@ -322,10 +307,6 @@ class SettingsSignalAnnotator(CelldetectiveSettingsPanel):
|
|
|
322
307
|
fraction = instructions["fraction"]
|
|
323
308
|
self.fraction_slider.setValue(fraction)
|
|
324
309
|
|
|
325
|
-
if "interval" in instructions:
|
|
326
|
-
interval = instructions["interval"]
|
|
327
|
-
self.interval_slider.setValue(interval)
|
|
328
|
-
|
|
329
310
|
if "log" in instructions:
|
|
330
311
|
self.log_option = not instructions["log"]
|
|
331
312
|
self.switch_to_log()
|
|
@@ -6,6 +6,7 @@ from PyQt5.QtWidgets import QHBoxLayout, QAction, QLabel, QComboBox
|
|
|
6
6
|
from fonticon_mdi6 import MDI6
|
|
7
7
|
from superqt import QLabeledDoubleRangeSlider, QLabeledSlider
|
|
8
8
|
from superqt.fonticon import icon
|
|
9
|
+
import matplotlib.gridspec as gridspec
|
|
9
10
|
|
|
10
11
|
from celldetective.gui.base.components import CelldetectiveWidget
|
|
11
12
|
from celldetective.gui.base.utils import center_window
|
|
@@ -102,8 +103,7 @@ class StackLoader(QThread):
|
|
|
102
103
|
self.mutex.unlock()
|
|
103
104
|
|
|
104
105
|
except Exception as e:
|
|
105
|
-
|
|
106
|
-
# logger.error(f"Error loading frame {frame_to_load}: {e}")
|
|
106
|
+
logger.debug(f"Error loading frame {frame_to_load}: {e}")
|
|
107
107
|
# Prepare to wait to avoid spin loop on error
|
|
108
108
|
self.msleep(100)
|
|
109
109
|
|
|
@@ -169,10 +169,14 @@ class StackVisualizer(CelldetectiveWidget):
|
|
|
169
169
|
window_title="View",
|
|
170
170
|
PxToUm=None,
|
|
171
171
|
background_color="transparent",
|
|
172
|
-
imshow_kwargs=
|
|
172
|
+
imshow_kwargs=None,
|
|
173
173
|
):
|
|
174
174
|
super().__init__()
|
|
175
175
|
|
|
176
|
+
# Default mutable argument handling
|
|
177
|
+
if imshow_kwargs is None:
|
|
178
|
+
imshow_kwargs = {}
|
|
179
|
+
|
|
176
180
|
# self.setWindowTitle(window_title)
|
|
177
181
|
self.window_title = window_title
|
|
178
182
|
|
|
@@ -265,7 +269,6 @@ class StackVisualizer(CelldetectiveWidget):
|
|
|
265
269
|
self.canvas.toolbar.insertAction(insert_before, self.lock_y_action)
|
|
266
270
|
else:
|
|
267
271
|
if len(actions) > 5:
|
|
268
|
-
self.canvas.toolbar.insertAction(actions[5], self.line_action)
|
|
269
272
|
self.canvas.toolbar.insertAction(actions[5], self.line_action)
|
|
270
273
|
self.canvas.toolbar.insertAction(actions[5], self.lock_y_action)
|
|
271
274
|
else:
|
|
@@ -313,8 +316,6 @@ class StackVisualizer(CelldetectiveWidget):
|
|
|
313
316
|
# Use GridSpec for robust layout
|
|
314
317
|
# 2 rows: Main Image (top, ~75%), Profile (bottom, ~25%)
|
|
315
318
|
# Add margins to ensure axis labels and text are visible
|
|
316
|
-
import matplotlib.gridspec as gridspec
|
|
317
|
-
|
|
318
319
|
gs = gridspec.GridSpec(
|
|
319
320
|
2,
|
|
320
321
|
1,
|
|
@@ -406,10 +407,6 @@ class StackVisualizer(CelldetectiveWidget):
|
|
|
406
407
|
self.ax_profile = None
|
|
407
408
|
|
|
408
409
|
# Restore original layout
|
|
409
|
-
# if hasattr(self, "ax_original_pos"):
|
|
410
|
-
# standard 1x1 GridSpec or manual restore
|
|
411
|
-
import matplotlib.gridspec as gridspec
|
|
412
|
-
|
|
413
410
|
gs = gridspec.GridSpec(1, 1)
|
|
414
411
|
self.ax.set_subplotspec(gs[0])
|
|
415
412
|
# self.ax.set_position(gs[0].get_position(self.fig))
|
|
@@ -505,9 +502,8 @@ class StackVisualizer(CelldetectiveWidget):
|
|
|
505
502
|
profile = np.zeros_like(profile)
|
|
506
503
|
profile[:] = np.nan
|
|
507
504
|
|
|
508
|
-
# Distance in
|
|
505
|
+
# Distance in pixels
|
|
509
506
|
dist_axis = np.arange(num_points)
|
|
510
|
-
x_label = "Distance (px)"
|
|
511
507
|
|
|
512
508
|
# Only show pixel length, rounded to integer
|
|
513
509
|
title_str = f"{round(length_px,2)} [px]"
|
|
@@ -525,11 +521,8 @@ class StackVisualizer(CelldetectiveWidget):
|
|
|
525
521
|
if hasattr(self, "profile_line") and self.profile_line:
|
|
526
522
|
try:
|
|
527
523
|
self.profile_line.remove()
|
|
528
|
-
except:
|
|
529
|
-
pass
|
|
530
|
-
|
|
531
|
-
# Distance in microns if available
|
|
532
|
-
dist_axis = np.arange(num_points)
|
|
524
|
+
except ValueError:
|
|
525
|
+
pass # Already removed
|
|
533
526
|
|
|
534
527
|
(self.profile_line,) = self.ax_profile.plot(
|
|
535
528
|
dist_axis, profile, color="black", linestyle="-"
|
|
@@ -850,12 +843,16 @@ class StackVisualizer(CelldetectiveWidget):
|
|
|
850
843
|
|
|
851
844
|
if curr_min < self._min:
|
|
852
845
|
self._min = curr_min
|
|
853
|
-
|
|
846
|
+
rescale_contrast = True
|
|
854
847
|
if curr_max > self._max:
|
|
855
848
|
self._max = curr_max
|
|
856
849
|
rescale_contrast = True
|
|
857
850
|
|
|
858
|
-
if
|
|
851
|
+
if (
|
|
852
|
+
rescale_contrast
|
|
853
|
+
and self.create_contrast_slider
|
|
854
|
+
and hasattr(self, "contrast_slider")
|
|
855
|
+
):
|
|
859
856
|
self.contrast_slider.setRange(self._min, self._max)
|
|
860
857
|
self.canvas.canvas.draw_idle()
|
|
861
858
|
self.update_profile()
|
|
@@ -888,5 +885,5 @@ class StackVisualizer(CelldetectiveWidget):
|
|
|
888
885
|
try:
|
|
889
886
|
if hasattr(self, "loader_thread") and self.loader_thread:
|
|
890
887
|
self.loader_thread.stop()
|
|
891
|
-
except:
|
|
888
|
+
except Exception:
|
|
892
889
|
pass
|
|
@@ -7,7 +7,7 @@ import numpy as np
|
|
|
7
7
|
from art import tprint
|
|
8
8
|
from tensorflow.python.keras.callbacks import Callback
|
|
9
9
|
|
|
10
|
-
from celldetective.
|
|
10
|
+
from celldetective.event_detection_models import SignalDetectionModel
|
|
11
11
|
from celldetective.log_manager import get_logger
|
|
12
12
|
from celldetective.utils.model_loaders import locate_signal_model
|
|
13
13
|
|
|
@@ -8,7 +8,7 @@ import json
|
|
|
8
8
|
from glob import glob
|
|
9
9
|
import numpy as np
|
|
10
10
|
from art import tprint
|
|
11
|
-
from celldetective.
|
|
11
|
+
from celldetective.event_detection_models import SignalDetectionModel
|
|
12
12
|
from celldetective.utils.model_loaders import locate_signal_model
|
|
13
13
|
|
|
14
14
|
tprint("Train")
|