celldetective 1.3.6.post2__py3-none-any.whl → 1.3.7__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/events.py +4 -0
- celldetective/gui/InitWindow.py +23 -9
- celldetective/gui/control_panel.py +19 -11
- celldetective/gui/generic_signal_plot.py +5 -0
- celldetective/gui/help/DL-segmentation-strategy.json +17 -17
- celldetective/gui/help/Threshold-vs-DL.json +11 -11
- celldetective/gui/help/cell-populations.json +5 -5
- celldetective/gui/help/exp-structure.json +15 -15
- celldetective/gui/help/feature-btrack.json +5 -5
- celldetective/gui/help/neighborhood.json +7 -7
- celldetective/gui/help/prefilter-for-segmentation.json +7 -7
- celldetective/gui/help/preprocessing.json +19 -19
- celldetective/gui/help/propagate-classification.json +7 -7
- celldetective/gui/plot_signals_ui.py +13 -9
- celldetective/gui/process_block.py +63 -14
- celldetective/gui/retrain_segmentation_model_options.py +21 -8
- celldetective/gui/retrain_signal_model_options.py +12 -2
- celldetective/gui/signal_annotator.py +9 -0
- celldetective/gui/signal_annotator2.py +8 -0
- celldetective/gui/styles.py +1 -0
- celldetective/gui/tableUI.py +1 -1
- celldetective/gui/workers.py +136 -0
- celldetective/io.py +53 -27
- celldetective/measure.py +112 -14
- celldetective/scripts/measure_cells.py +10 -35
- celldetective/scripts/segment_cells.py +15 -62
- celldetective/scripts/segment_cells_thresholds.py +1 -2
- celldetective/scripts/track_cells.py +16 -19
- celldetective/segmentation.py +16 -62
- celldetective/signals.py +11 -7
- celldetective/utils.py +587 -67
- {celldetective-1.3.6.post2.dist-info → celldetective-1.3.7.dist-info}/METADATA +1 -1
- {celldetective-1.3.6.post2.dist-info → celldetective-1.3.7.dist-info}/RECORD +38 -37
- {celldetective-1.3.6.post2.dist-info → celldetective-1.3.7.dist-info}/LICENSE +0 -0
- {celldetective-1.3.6.post2.dist-info → celldetective-1.3.7.dist-info}/WHEEL +0 -0
- {celldetective-1.3.6.post2.dist-info → celldetective-1.3.7.dist-info}/entry_points.txt +0 -0
- {celldetective-1.3.6.post2.dist-info → celldetective-1.3.7.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import QFrame, QGridLayout, QComboBox, QListWidget, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, \
|
|
1
|
+
from PyQt5.QtWidgets import QDialog, QFrame, QGridLayout, QComboBox, QListWidget, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, \
|
|
2
2
|
QMessageBox, QWidget
|
|
3
3
|
from PyQt5.QtCore import Qt, QSize
|
|
4
4
|
from superqt.fonticon import icon
|
|
@@ -10,7 +10,7 @@ from celldetective.gui.signal_annotator import MeasureAnnotator
|
|
|
10
10
|
from celldetective.gui.signal_annotator2 import SignalAnnotator2
|
|
11
11
|
from celldetective.io import get_segmentation_models_list, control_segmentation_napari, get_signal_models_list, \
|
|
12
12
|
control_tracks, load_experiment_tables, get_pair_signal_models_list
|
|
13
|
-
from celldetective.io import locate_segmentation_model, fix_missing_labels, auto_load_number_of_frames, load_frames, locate_signal_model
|
|
13
|
+
from celldetective.io import locate_segmentation_model, extract_position_name, fix_missing_labels, auto_load_number_of_frames, load_frames, locate_signal_model
|
|
14
14
|
from celldetective.gui import SegmentationModelLoader, ClassifierWidget, ConfigNeighborhoods, ConfigSegmentationModelTraining, ConfigTracking, SignalAnnotator, ConfigSignalModelTraining, ConfigMeasurements, ConfigSignalAnnotator, TableUI
|
|
15
15
|
from celldetective.gui.gui_utils import QHSeperationLine
|
|
16
16
|
from celldetective.relative_measurements import rel_measure_at_position
|
|
@@ -37,6 +37,14 @@ from celldetective.gui.layouts import CellposeParamsWidget, StarDistParamsWidget
|
|
|
37
37
|
from celldetective.gui import Styles
|
|
38
38
|
from celldetective.utils import get_software_location
|
|
39
39
|
|
|
40
|
+
from celldetective.gui.workers import ProgressWindow
|
|
41
|
+
from celldetective.gui.processes.segment_cells import SegmentCellThresholdProcess, SegmentCellDLProcess
|
|
42
|
+
from celldetective.gui.processes.track_cells import TrackingProcess
|
|
43
|
+
from celldetective.gui.processes.measure_cells import MeasurementProcess
|
|
44
|
+
|
|
45
|
+
import time
|
|
46
|
+
import asyncio
|
|
47
|
+
|
|
40
48
|
class ProcessPanel(QFrame, Styles):
|
|
41
49
|
def __init__(self, parent_window, mode):
|
|
42
50
|
|
|
@@ -52,6 +60,8 @@ class ProcessPanel(QFrame, Styles):
|
|
|
52
60
|
self.wells = np.array(self.parent_window.wells,dtype=str)
|
|
53
61
|
self.cellpose_calibrated = False
|
|
54
62
|
self.stardist_calibrated = False
|
|
63
|
+
self.use_gpu = self.parent_window.parent_window.use_gpu
|
|
64
|
+
self.n_threads = self.parent_window.parent_window.n_threads
|
|
55
65
|
|
|
56
66
|
self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
57
67
|
self.grid = QGridLayout(self)
|
|
@@ -264,7 +274,7 @@ class ProcessPanel(QFrame, Styles):
|
|
|
264
274
|
#self.to_disable.append(self.cell_models_list)
|
|
265
275
|
|
|
266
276
|
self.train_signal_model_btn = QPushButton("TRAIN")
|
|
267
|
-
self.train_signal_model_btn.setToolTip("
|
|
277
|
+
self.train_signal_model_btn.setToolTip("Train or retrain an event detection model\non newly annotated data.")
|
|
268
278
|
self.train_signal_model_btn.setIcon(icon(MDI6.redo_variant,color='black'))
|
|
269
279
|
self.train_signal_model_btn.setIconSize(QSize(20, 20))
|
|
270
280
|
self.train_signal_model_btn.setStyleSheet(self.button_style_sheet_3)
|
|
@@ -284,7 +294,7 @@ class ProcessPanel(QFrame, Styles):
|
|
|
284
294
|
self.signal_models_list.addItems(signal_models)
|
|
285
295
|
|
|
286
296
|
def generate_tracking_options(self):
|
|
287
|
-
|
|
297
|
+
|
|
288
298
|
grid_track = QHBoxLayout()
|
|
289
299
|
|
|
290
300
|
self.track_action = QCheckBox("TRACK")
|
|
@@ -363,7 +373,7 @@ class ProcessPanel(QFrame, Styles):
|
|
|
363
373
|
self.segment_action = QCheckBox("SEGMENT")
|
|
364
374
|
self.segment_action.setStyleSheet(self.menu_check_style)
|
|
365
375
|
self.segment_action.setIcon(icon(MDI6.bacteria, color='black'))
|
|
366
|
-
self.segment_action.setToolTip(f"Segment the {self.mode} cells on the images.")
|
|
376
|
+
self.segment_action.setToolTip(f"Segment the {self.mode[:-1]} cells on the images.")
|
|
367
377
|
self.segment_action.toggled.connect(self.enable_segmentation_model_list)
|
|
368
378
|
#self.to_disable.append(self.segment_action)
|
|
369
379
|
grid_segment.addWidget(self.segment_action, 90)
|
|
@@ -390,6 +400,7 @@ class ProcessPanel(QFrame, Styles):
|
|
|
390
400
|
model_zoo_layout = QHBoxLayout()
|
|
391
401
|
model_zoo_layout.addWidget(QLabel("Model zoo:"),90)
|
|
392
402
|
self.seg_model_list = QComboBox()
|
|
403
|
+
self.seg_model_list.currentIndexChanged.connect(self.reset_generalist_setup)
|
|
393
404
|
#self.to_disable.append(self.tc_seg_model_list)
|
|
394
405
|
self.seg_model_list.setGeometry(50, 50, 200, 30)
|
|
395
406
|
self.init_seg_model_list()
|
|
@@ -682,6 +693,10 @@ class ProcessPanel(QFrame, Styles):
|
|
|
682
693
|
self.ConfigSignalAnnotator = ConfigSignalAnnotator(self)
|
|
683
694
|
self.ConfigSignalAnnotator.show()
|
|
684
695
|
|
|
696
|
+
def reset_generalist_setup(self, index):
|
|
697
|
+
self.cellpose_calibrated = False
|
|
698
|
+
self.stardist_calibrated = False
|
|
699
|
+
|
|
685
700
|
def process_population(self):
|
|
686
701
|
|
|
687
702
|
# if self.parent_window.well_list.currentText().startswith('Multiple'):
|
|
@@ -750,18 +765,19 @@ class ProcessPanel(QFrame, Styles):
|
|
|
750
765
|
self.model_name = self.seg_models[self.seg_model_list.currentIndex()]
|
|
751
766
|
print(self.model_name, self.seg_model_list.currentIndex())
|
|
752
767
|
|
|
753
|
-
if self.model_name.startswith('CP') and self.model_name in self.seg_models_generic and not self.cellpose_calibrated:
|
|
768
|
+
if self.segment_action.isChecked() and self.model_name.startswith('CP') and self.model_name in self.seg_models_generic and not self.cellpose_calibrated:
|
|
754
769
|
|
|
755
770
|
self.diamWidget = CellposeParamsWidget(self, model_name=self.model_name)
|
|
756
771
|
self.diamWidget.show()
|
|
757
772
|
return None
|
|
758
773
|
|
|
759
|
-
if self.model_name.startswith('SD') and self.model_name in self.seg_models_generic and not self.stardist_calibrated:
|
|
774
|
+
if self.segment_action.isChecked() and self.model_name.startswith('SD') and self.model_name in self.seg_models_generic and not self.stardist_calibrated:
|
|
760
775
|
|
|
761
776
|
self.diamWidget = StarDistParamsWidget(self, model_name = self.model_name)
|
|
762
777
|
self.diamWidget.show()
|
|
763
778
|
return None
|
|
764
779
|
|
|
780
|
+
self.movie_prefix = self.parent_window.movie_prefix
|
|
765
781
|
|
|
766
782
|
for w_idx in self.well_index:
|
|
767
783
|
|
|
@@ -775,6 +791,7 @@ class ProcessPanel(QFrame, Styles):
|
|
|
775
791
|
|
|
776
792
|
self.pos = natsorted(glob(well+f"{os.path.split(well)[-1].replace('W','').replace(os.sep,'')}*/"))[pos_idx]
|
|
777
793
|
print(f"Position {self.pos}...\nLoading stack movie...")
|
|
794
|
+
self.pos_name = extract_position_name(self.pos)
|
|
778
795
|
|
|
779
796
|
if not os.path.exists(self.pos + 'output/'):
|
|
780
797
|
os.mkdir(self.pos + 'output/')
|
|
@@ -805,9 +822,27 @@ class ProcessPanel(QFrame, Styles):
|
|
|
805
822
|
return None
|
|
806
823
|
else:
|
|
807
824
|
print(f"Segmentation from threshold config: {self.threshold_config}")
|
|
808
|
-
|
|
825
|
+
process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads, "threshold_instructions": self.threshold_config, "use_gpu": self.use_gpu}
|
|
826
|
+
self.job = ProgressWindow(SegmentCellThresholdProcess, parent_window=self, title="Segment", process_args = process_args)
|
|
827
|
+
result = self.job.exec_()
|
|
828
|
+
if result == QDialog.Accepted:
|
|
829
|
+
pass
|
|
830
|
+
elif result == QDialog.Rejected:
|
|
831
|
+
return None
|
|
832
|
+
#segment_from_threshold_at_position(self.pos, self.mode, self.threshold_config, threads=self.parent_window.parent_window.n_threads)
|
|
809
833
|
else:
|
|
810
|
-
|
|
834
|
+
# model = locate_segmentation_model(self.model_name)
|
|
835
|
+
# if model is None:
|
|
836
|
+
# process = {"output_dir": self.output_dir, "file": self.model_name}
|
|
837
|
+
# self.download_model_job = ProgressWindow(DownloadProcess, parent_window=self, title="Download", process_args = args)
|
|
838
|
+
|
|
839
|
+
process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads, "model_name": self.model_name, "use_gpu": self.use_gpu}
|
|
840
|
+
self.job = ProgressWindow(SegmentCellDLProcess, parent_window=self, title="Segment", process_args = process_args)
|
|
841
|
+
result = self.job.exec_()
|
|
842
|
+
if result == QDialog.Accepted:
|
|
843
|
+
pass
|
|
844
|
+
elif result == QDialog.Rejected:
|
|
845
|
+
return None
|
|
811
846
|
|
|
812
847
|
if self.track_action.isChecked():
|
|
813
848
|
if os.path.exists(os.sep.join([self.pos, 'output', 'tables', f'trajectories_{self.mode}.csv'])) and not self.parent_window.position_list.isMultipleSelection():
|
|
@@ -819,10 +854,25 @@ class ProcessPanel(QFrame, Styles):
|
|
|
819
854
|
returnValue = msgBox.exec()
|
|
820
855
|
if returnValue == QMessageBox.No:
|
|
821
856
|
return None
|
|
822
|
-
|
|
857
|
+
|
|
858
|
+
process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads}
|
|
859
|
+
self.job = ProgressWindow(TrackingProcess, parent_window=self, title="Tracking", process_args=process_args)
|
|
860
|
+
result = self.job.exec_()
|
|
861
|
+
if result == QDialog.Accepted:
|
|
862
|
+
pass
|
|
863
|
+
elif result == QDialog.Rejected:
|
|
864
|
+
return None
|
|
865
|
+
#track_at_position(self.pos, self.mode, threads=self.parent_window.parent_window.n_threads)
|
|
823
866
|
|
|
824
867
|
if self.measure_action.isChecked():
|
|
825
|
-
|
|
868
|
+
process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads}
|
|
869
|
+
self.job = ProgressWindow(MeasurementProcess, parent_window=self, title="Measurement", process_args=process_args)
|
|
870
|
+
result = self.job.exec_()
|
|
871
|
+
if result == QDialog.Accepted:
|
|
872
|
+
pass
|
|
873
|
+
elif result == QDialog.Rejected:
|
|
874
|
+
return None
|
|
875
|
+
#measure_at_position(self.pos, self.mode, threads=self.parent_window.parent_window.n_threads)
|
|
826
876
|
|
|
827
877
|
table = os.sep.join([self.pos, 'output', 'tables', f'trajectories_{self.mode}.csv'])
|
|
828
878
|
if self.signal_analysis_action.isChecked() and os.path.exists(table):
|
|
@@ -845,12 +895,11 @@ class ProcessPanel(QFrame, Styles):
|
|
|
845
895
|
|
|
846
896
|
# self.stack = None
|
|
847
897
|
self.parent_window.update_position_options()
|
|
848
|
-
|
|
849
898
|
for action in [self.segment_action, self.track_action, self.measure_action, self.signal_analysis_action]:
|
|
850
899
|
if action.isChecked():
|
|
851
900
|
action.setChecked(False)
|
|
852
901
|
|
|
853
|
-
self.
|
|
902
|
+
self.reset_generalist_setup(0)
|
|
854
903
|
|
|
855
904
|
def open_napari_tracking(self):
|
|
856
905
|
print(f'View the tracks before post-processing for position {self.parent_window.pos} in napari...')
|
|
@@ -1207,7 +1256,7 @@ class NeighPanel(QFrame, Styles):
|
|
|
1207
1256
|
self.neigh_action.setChecked(False)
|
|
1208
1257
|
|
|
1209
1258
|
def open_classifier_ui_pairs(self):
|
|
1210
|
-
|
|
1259
|
+
|
|
1211
1260
|
self.mode = "pairs"
|
|
1212
1261
|
self.load_available_tables()
|
|
1213
1262
|
if self.df is None:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import QMainWindow, QApplication,QRadioButton, QMessageBox, QScrollArea, QComboBox, QFrame, QFileDialog, QGridLayout, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
|
|
1
|
+
from PyQt5.QtWidgets import QDialog, QMainWindow, QApplication,QRadioButton, QMessageBox, QScrollArea, QComboBox, QFrame, QFileDialog, QGridLayout, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
|
|
2
2
|
from PyQt5.QtCore import Qt, QSize
|
|
3
3
|
from PyQt5.QtGui import QDoubleValidator, QIntValidator, QIcon
|
|
4
4
|
from celldetective.gui.gui_utils import center_window
|
|
@@ -17,6 +17,8 @@ import os
|
|
|
17
17
|
from glob import glob
|
|
18
18
|
from datetime import datetime
|
|
19
19
|
from celldetective.gui import Styles
|
|
20
|
+
from celldetective.gui.processes.train_segmentation_model import TrainSegModelProcess
|
|
21
|
+
from celldetective.gui.workers import ProgressWindow
|
|
20
22
|
|
|
21
23
|
class ConfigSegmentationModelTraining(QMainWindow, Styles):
|
|
22
24
|
|
|
@@ -29,6 +31,7 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
|
|
|
29
31
|
|
|
30
32
|
super().__init__()
|
|
31
33
|
self.parent_window = parent_window
|
|
34
|
+
self.use_gpu = self.parent_window.use_gpu
|
|
32
35
|
self.setWindowTitle("Train segmentation model")
|
|
33
36
|
self.setWindowIcon(QIcon(os.sep.join(['celldetective','icons','mexican-hat.png'])))
|
|
34
37
|
self.mode = self.parent_window.mode
|
|
@@ -239,7 +242,7 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
|
|
|
239
242
|
self.augmentation_slider.setSingleStep(0.01)
|
|
240
243
|
self.augmentation_slider.setTickInterval(0.01)
|
|
241
244
|
self.augmentation_slider.setOrientation(1)
|
|
242
|
-
self.augmentation_slider.setRange(
|
|
245
|
+
self.augmentation_slider.setRange(0.01, 3)
|
|
243
246
|
self.augmentation_slider.setValue(2.0)
|
|
244
247
|
|
|
245
248
|
augmentation_hbox.addWidget(self.augmentation_slider, 70)
|
|
@@ -597,24 +600,34 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
|
|
|
597
600
|
epochs = self.epochs_slider.value()
|
|
598
601
|
spatial_calib = float(self.spatial_calib_le.text().replace(',','.'))
|
|
599
602
|
|
|
600
|
-
training_instructions = {'model_name': model_name,'model_type': model_type, 'pretrained': pretrained_model, 'spatial_calibration': spatial_calib, 'channel_option': channels, 'normalization_percentile': normalization_mode,
|
|
603
|
+
self.training_instructions = {'model_name': model_name,'model_type': model_type, 'pretrained': pretrained_model, 'spatial_calibration': spatial_calib, 'channel_option': channels, 'normalization_percentile': normalization_mode,
|
|
601
604
|
'normalization_clip': clip_values,'normalization_values': norm_values, 'ds': data_folders, 'augmentation_factor': aug_factor, 'validation_split': val_split,
|
|
602
605
|
'learning_rate': lr, 'batch_size': bs, 'epochs': epochs}
|
|
603
606
|
|
|
604
|
-
print(training_instructions)
|
|
605
607
|
|
|
606
608
|
model_folder = os.sep.join([self.software_models_dir,model_name, ''])
|
|
607
609
|
print(model_folder)
|
|
608
610
|
if not os.path.exists(model_folder):
|
|
609
611
|
os.mkdir(model_folder)
|
|
610
612
|
|
|
611
|
-
training_instructions.update({'target_directory': self.software_models_dir})
|
|
613
|
+
self.training_instructions.update({'target_directory': self.software_models_dir})
|
|
614
|
+
|
|
615
|
+
print(f"Set of instructions: {self.training_instructions}")
|
|
616
|
+
|
|
617
|
+
self.instructions = model_folder+"training_instructions.json"
|
|
612
618
|
|
|
613
|
-
print(f"Set of instructions: {training_instructions}")
|
|
614
619
|
with open(model_folder+"training_instructions.json", 'w') as f:
|
|
615
|
-
json.dump(training_instructions, f, indent=4)
|
|
620
|
+
json.dump(self.training_instructions, f, indent=4)
|
|
616
621
|
|
|
617
|
-
|
|
622
|
+
# process_args = {"instructions": self.instructions, "use_gpu": self.use_gpu}
|
|
623
|
+
# self.job = ProgressWindow(TrainSegModelProcess, parent_window=self, title="Training", position_info=False, process_args=process_args)
|
|
624
|
+
# result = self.job.exec_()
|
|
625
|
+
# if result == QDialog.Accepted:
|
|
626
|
+
# pass
|
|
627
|
+
# elif result == QDialog.Rejected:
|
|
628
|
+
# return None
|
|
629
|
+
|
|
630
|
+
train_segmentation_model(self.instructions, use_gpu=self.parent_window.parent_window.parent_window.use_gpu)
|
|
618
631
|
|
|
619
632
|
self.parent_window.init_seg_model_list()
|
|
620
633
|
idx = self.parent_window.seg_model_list.findText(model_name)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox, QScrollArea, QComboBox, QFrame, QCheckBox, QFileDialog, QGridLayout, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
|
|
1
|
+
from PyQt5.QtWidgets import QMainWindow, QApplication, QDialog, QMessageBox, QScrollArea, QComboBox, QFrame, QCheckBox, QFileDialog, QGridLayout, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
|
|
2
2
|
from PyQt5.QtCore import Qt, QSize
|
|
3
3
|
from PyQt5.QtGui import QDoubleValidator, QIntValidator, QIcon
|
|
4
4
|
from celldetective.gui.gui_utils import center_window
|
|
@@ -16,6 +16,8 @@ from glob import glob
|
|
|
16
16
|
from datetime import datetime
|
|
17
17
|
from celldetective.gui import Styles
|
|
18
18
|
from pandas.api.types import is_numeric_dtype
|
|
19
|
+
from celldetective.gui.processes.train_signal_model import TrainSignalModelProcess
|
|
20
|
+
from celldetective.gui.workers import ProgressWindow
|
|
19
21
|
|
|
20
22
|
class ConfigSignalModelTraining(QMainWindow, Styles):
|
|
21
23
|
|
|
@@ -584,6 +586,14 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
|
|
|
584
586
|
with open(model_folder+"training_instructions.json", 'w') as f:
|
|
585
587
|
json.dump(training_instructions, f, indent=4)
|
|
586
588
|
|
|
587
|
-
|
|
589
|
+
# self.instructions = model_folder+"training_instructions.json"
|
|
590
|
+
# process_args = {"instructions": self.instructions} # "use_gpu": self.use_gpu
|
|
591
|
+
# self.job = ProgressWindow(TrainSignalModelProcess, parent_window=self, title="Training", position_info=False, process_args=process_args)
|
|
592
|
+
# result = self.job.exec_()
|
|
593
|
+
# if result == QDialog.Accepted:
|
|
594
|
+
# pass
|
|
595
|
+
# elif result == QDialog.Rejected:
|
|
596
|
+
# return None
|
|
588
597
|
|
|
598
|
+
train_signal_model(model_folder+"training_instructions.json")
|
|
589
599
|
self.parent_window.refresh_signal_models()
|
|
@@ -83,6 +83,15 @@ class SignalAnnotator(QMainWindow, Styles):
|
|
|
83
83
|
|
|
84
84
|
self.populate_widget()
|
|
85
85
|
|
|
86
|
+
def resizeEvent(self, event):
|
|
87
|
+
|
|
88
|
+
super().resizeEvent(event)
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
self.cell_fig.tight_layout()
|
|
92
|
+
except:
|
|
93
|
+
pass
|
|
94
|
+
|
|
86
95
|
def populate_widget(self):
|
|
87
96
|
|
|
88
97
|
"""
|
|
@@ -134,6 +134,14 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
134
134
|
|
|
135
135
|
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
136
136
|
|
|
137
|
+
def resizeEvent(self, event):
|
|
138
|
+
|
|
139
|
+
super().resizeEvent(event)
|
|
140
|
+
try:
|
|
141
|
+
self.cell_fig.tight_layout()
|
|
142
|
+
except:
|
|
143
|
+
pass
|
|
144
|
+
|
|
137
145
|
def populate_widget(self):
|
|
138
146
|
|
|
139
147
|
"""
|
celldetective/gui/styles.py
CHANGED
celldetective/gui/tableUI.py
CHANGED
|
@@ -1337,7 +1337,7 @@ class TableUI(QMainWindow, Styles):
|
|
|
1337
1337
|
file_name += ".csv"
|
|
1338
1338
|
invalid_cols = [c for c in list(self.data.columns) if c.startswith('Unnamed')]
|
|
1339
1339
|
if len(invalid_cols)>0:
|
|
1340
|
-
self.data = self.data.drop(invalid_cols, axis=1)
|
|
1340
|
+
self.data = self.data.drop(invalid_cols, axis=1)
|
|
1341
1341
|
self.data.to_csv(file_name, index=False)
|
|
1342
1342
|
|
|
1343
1343
|
def test_bool(self, array):
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
from multiprocessing import Queue
|
|
2
|
+
from PyQt5.QtWidgets import QDialog, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QLabel, QProgressBar
|
|
3
|
+
from PyQt5.QtCore import QRunnable, QObject, pyqtSignal, QThreadPool, QSize, Qt
|
|
4
|
+
from celldetective.gui.gui_utils import center_window
|
|
5
|
+
import time
|
|
6
|
+
import math
|
|
7
|
+
|
|
8
|
+
class ProgressWindow(QDialog):
|
|
9
|
+
|
|
10
|
+
def __init__(self, process=None, parent_window=None, title="", position_info=True, process_args=None):
|
|
11
|
+
QDialog.__init__(self)
|
|
12
|
+
|
|
13
|
+
self.setWindowTitle(f'{title} Progress')
|
|
14
|
+
self.__process = process
|
|
15
|
+
self.parent_window = parent_window
|
|
16
|
+
self.position_info = position_info
|
|
17
|
+
if self.position_info:
|
|
18
|
+
self.pos_name = self.parent_window.pos_name
|
|
19
|
+
|
|
20
|
+
#self.__btn_run = QPushButton("Start")
|
|
21
|
+
self.__btn_stp = QPushButton("Cancel")
|
|
22
|
+
if self.position_info:
|
|
23
|
+
self.position_label = QLabel(f'Processing position {self.pos_name}...')
|
|
24
|
+
self.__label = QLabel("Idle")
|
|
25
|
+
self.time_left_lbl = QLabel('')
|
|
26
|
+
|
|
27
|
+
self.progress_bar = QProgressBar()
|
|
28
|
+
self.progress_bar.setValue(0)
|
|
29
|
+
|
|
30
|
+
self.__runner = Runner(process=self.__process, progress_bar=self.progress_bar, process_args=process_args, remaining_time_label=self.time_left_lbl)
|
|
31
|
+
self.pool = QThreadPool.globalInstance()
|
|
32
|
+
#print("Multithreading with maximum %d threads" % self.pool.maxThreadCount())
|
|
33
|
+
|
|
34
|
+
#self.__btn_run.clicked.connect(self.__run_net)
|
|
35
|
+
self.__btn_stp.clicked.connect(self.__stp_net)
|
|
36
|
+
self.__runner.signals.finished.connect(self.__on_finished)
|
|
37
|
+
self.__runner.signals.finished.connect(self.__on_error)
|
|
38
|
+
self.__runner.signals.update.connect(self.progress_bar.setValue)
|
|
39
|
+
self.__runner.signals.update_time.connect(self.time_left_lbl.setText)
|
|
40
|
+
|
|
41
|
+
self.__btn_stp.setDisabled(True)
|
|
42
|
+
|
|
43
|
+
self.layout = QVBoxLayout()
|
|
44
|
+
if self.position_info:
|
|
45
|
+
self.layout.addWidget(self.position_label)
|
|
46
|
+
self.layout.addWidget(self.time_left_lbl)
|
|
47
|
+
self.layout.addWidget(self.progress_bar)
|
|
48
|
+
self.btn_layout = QHBoxLayout()
|
|
49
|
+
self.btn_layout.addWidget(self.__btn_stp)
|
|
50
|
+
self.btn_layout.addWidget(self.__label)
|
|
51
|
+
self.layout.addLayout(self.btn_layout)
|
|
52
|
+
|
|
53
|
+
self.setLayout(self.layout)
|
|
54
|
+
self.setFixedSize(QSize(250, 130))
|
|
55
|
+
self.__run_net()
|
|
56
|
+
self.setModal(True)
|
|
57
|
+
center_window(self)
|
|
58
|
+
|
|
59
|
+
def closeEvent(self, evnt):
|
|
60
|
+
# if self._want_to_close:
|
|
61
|
+
# super(MyDialog, self).closeEvent(evnt)
|
|
62
|
+
# else:
|
|
63
|
+
evnt.ignore()
|
|
64
|
+
self.setWindowState(Qt.WindowMinimized)
|
|
65
|
+
|
|
66
|
+
def __run_net(self):
|
|
67
|
+
#self.__btn_run.setDisabled(True)
|
|
68
|
+
self.__btn_stp.setEnabled(True)
|
|
69
|
+
self.__label.setText("Running...")
|
|
70
|
+
self.pool.start(self.__runner)
|
|
71
|
+
|
|
72
|
+
def __stp_net(self):
|
|
73
|
+
self.__runner.close()
|
|
74
|
+
print('\n Job cancelled... Abort.')
|
|
75
|
+
self.reject()
|
|
76
|
+
|
|
77
|
+
def __on_finished(self):
|
|
78
|
+
self.__btn_stp.setDisabled(True)
|
|
79
|
+
self.__label.setText("\nFinished!")
|
|
80
|
+
self.__runner.close()
|
|
81
|
+
self.accept()
|
|
82
|
+
|
|
83
|
+
def __on_error(self):
|
|
84
|
+
self.__btn_stp.setDisabled(True)
|
|
85
|
+
self.__label.setText("\nError")
|
|
86
|
+
self.__runner.close()
|
|
87
|
+
self.accept()
|
|
88
|
+
|
|
89
|
+
class Runner(QRunnable):
|
|
90
|
+
|
|
91
|
+
def __init__(self, process=None, progress_bar=None, remaining_time_label=None, process_args=None):
|
|
92
|
+
QRunnable.__init__(self)
|
|
93
|
+
|
|
94
|
+
print(f"{process_args=}")
|
|
95
|
+
self.progress_bar = progress_bar
|
|
96
|
+
#self.parent_window = parent_window
|
|
97
|
+
self.remaining_time_label = remaining_time_label
|
|
98
|
+
self.__queue = Queue()
|
|
99
|
+
self.__process = process(self.__queue, process_args=process_args)
|
|
100
|
+
self.signals = RunnerSignal()
|
|
101
|
+
|
|
102
|
+
def run(self):
|
|
103
|
+
self.__process.start()
|
|
104
|
+
while True:
|
|
105
|
+
try:
|
|
106
|
+
data = self.__queue.get()
|
|
107
|
+
if len(data)==2:
|
|
108
|
+
progress, time = data
|
|
109
|
+
time = int(time)
|
|
110
|
+
remaining_minutes = time // 60
|
|
111
|
+
remaining_seconds = int(time % 60)
|
|
112
|
+
time_left = "About "+str(remaining_minutes)+" min and "+str(remaining_seconds)+" s remaining"
|
|
113
|
+
if remaining_minutes == 0:
|
|
114
|
+
time_left = "About "+str(remaining_seconds)+" s remaining"
|
|
115
|
+
self.signals.update_time.emit(time_left)
|
|
116
|
+
if isinstance(progress, (float,int)):
|
|
117
|
+
self.signals.update.emit(math.ceil(progress))
|
|
118
|
+
if data == "finished":
|
|
119
|
+
self.signals.finished.emit()
|
|
120
|
+
break
|
|
121
|
+
elif data == "error":
|
|
122
|
+
self.signals.error.emit()
|
|
123
|
+
except Exception as e:
|
|
124
|
+
print(e)
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
def close(self):
|
|
128
|
+
self.__process.end_process()
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class RunnerSignal(QObject):
|
|
132
|
+
|
|
133
|
+
update = pyqtSignal(int)
|
|
134
|
+
update_time = pyqtSignal(str)
|
|
135
|
+
finished = pyqtSignal()
|
|
136
|
+
error = pyqtSignal()
|
celldetective/io.py
CHANGED
|
@@ -19,16 +19,16 @@ from csbdeep.io import save_tiff_imagej_compatible
|
|
|
19
19
|
import skimage.io as skio
|
|
20
20
|
from skimage.measure import regionprops_table, label
|
|
21
21
|
|
|
22
|
-
from scipy.ndimage import zoom
|
|
23
22
|
from btrack.datasets import cell_config
|
|
24
23
|
from magicgui import magicgui
|
|
25
24
|
from pathlib import Path, PurePath
|
|
26
25
|
from shutil import copyfile, rmtree
|
|
27
26
|
|
|
28
|
-
from celldetective.utils import ConfigSectionMap, extract_experiment_channels, _extract_labels_from_config, get_zenodo_files, download_zenodo_file
|
|
29
|
-
from celldetective.utils import _estimate_scale_factor, _extract_channel_indices_from_config, _extract_channel_indices, _extract_nbr_channels_from_config, _get_img_num_per_channel, normalize_per_channel
|
|
27
|
+
from celldetective.utils import _rearrange_multichannel_frame, _fix_no_contrast, zoom_multiframes,ConfigSectionMap, extract_experiment_channels, _extract_labels_from_config, get_zenodo_files, download_zenodo_file
|
|
28
|
+
from celldetective.utils import interpolate_nan_multichannel, _estimate_scale_factor, _extract_channel_indices_from_config, _extract_channel_indices, _extract_nbr_channels_from_config, _get_img_num_per_channel, normalize_per_channel
|
|
30
29
|
|
|
31
30
|
from stardist import fill_label_holes
|
|
31
|
+
from skimage.transform import resize
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
def extract_experiment_from_well(well_path):
|
|
@@ -1190,7 +1190,11 @@ def locate_labels(position, population='target', frames=None):
|
|
|
1190
1190
|
elif isinstance(frames, (int,float, np.int_)):
|
|
1191
1191
|
|
|
1192
1192
|
tzfill = str(int(frames)).zfill(4)
|
|
1193
|
-
|
|
1193
|
+
try:
|
|
1194
|
+
idx = label_names.index(f"{tzfill}.tif")
|
|
1195
|
+
except:
|
|
1196
|
+
idx = -1
|
|
1197
|
+
|
|
1194
1198
|
if idx==-1:
|
|
1195
1199
|
labels = None
|
|
1196
1200
|
else:
|
|
@@ -1200,7 +1204,11 @@ def locate_labels(position, population='target', frames=None):
|
|
|
1200
1204
|
labels = []
|
|
1201
1205
|
for f in frames:
|
|
1202
1206
|
tzfill = str(int(f)).zfill(4)
|
|
1203
|
-
|
|
1207
|
+
try:
|
|
1208
|
+
idx = label_names.index(f"{tzfill}.tif")
|
|
1209
|
+
except:
|
|
1210
|
+
idx = -1
|
|
1211
|
+
|
|
1204
1212
|
if idx==-1:
|
|
1205
1213
|
labels.append(None)
|
|
1206
1214
|
else:
|
|
@@ -2842,7 +2850,7 @@ def get_segmentation_models_list(mode='targets', return_path=False):
|
|
|
2842
2850
|
return available_models, modelpath
|
|
2843
2851
|
|
|
2844
2852
|
|
|
2845
|
-
def locate_segmentation_model(name):
|
|
2853
|
+
def locate_segmentation_model(name, download=True):
|
|
2846
2854
|
|
|
2847
2855
|
"""
|
|
2848
2856
|
Locates a specified segmentation model within the local 'celldetective' directory or
|
|
@@ -2882,13 +2890,15 @@ def locate_segmentation_model(name):
|
|
|
2882
2890
|
if name == m.replace('\\', os.sep).split(os.sep)[-2]:
|
|
2883
2891
|
match = m
|
|
2884
2892
|
return match
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2893
|
+
if download:
|
|
2894
|
+
# else no match, try zenodo
|
|
2895
|
+
files, categories = get_zenodo_files()
|
|
2896
|
+
if name in files:
|
|
2897
|
+
index = files.index(name)
|
|
2898
|
+
cat = categories[index]
|
|
2899
|
+
download_zenodo_file(name, os.sep.join([main_dir, cat]))
|
|
2900
|
+
match = os.sep.join([main_dir, cat, name]) + os.sep
|
|
2901
|
+
|
|
2892
2902
|
return match
|
|
2893
2903
|
|
|
2894
2904
|
|
|
@@ -3306,27 +3316,16 @@ def load_frames(img_nums, stack_path, scale=None, normalize_input=True, dtype=fl
|
|
|
3306
3316
|
f'Error in loading the frame {img_nums} {e}. Please check that the experiment channel information is consistent with the movie being read.')
|
|
3307
3317
|
return None
|
|
3308
3318
|
|
|
3309
|
-
|
|
3310
|
-
# Systematically move channel axis to the end
|
|
3311
|
-
channel_axis = np.argmin(frames.shape)
|
|
3312
|
-
frames = np.moveaxis(frames, channel_axis, -1)
|
|
3313
|
-
|
|
3314
|
-
if frames.ndim==2:
|
|
3315
|
-
frames = frames[:,:,np.newaxis].astype(float)
|
|
3319
|
+
frames = _rearrange_multichannel_frame(frames)
|
|
3316
3320
|
|
|
3317
3321
|
if normalize_input:
|
|
3318
3322
|
frames = normalize_multichannel(frames, **normalize_kwargs)
|
|
3319
3323
|
|
|
3320
3324
|
if scale is not None:
|
|
3321
|
-
frames =
|
|
3322
|
-
frames = np.moveaxis(frames,0,-1)
|
|
3325
|
+
frames = zoom_multiframes(frames, scale)
|
|
3323
3326
|
|
|
3324
3327
|
# add a fake pixel to prevent auto normalization errors on images that are uniform
|
|
3325
|
-
|
|
3326
|
-
for k in range(frames.shape[2]):
|
|
3327
|
-
unique_values = np.unique(frames[:, :, k])
|
|
3328
|
-
if len(unique_values) == 1:
|
|
3329
|
-
frames[0, 0, k] += 1
|
|
3328
|
+
frames = _fix_no_contrast(frames)
|
|
3330
3329
|
|
|
3331
3330
|
return frames.astype(dtype)
|
|
3332
3331
|
|
|
@@ -3532,6 +3531,33 @@ def extract_experiment_folder_output(experiment_folder, destination_folder):
|
|
|
3532
3531
|
for t in tab_path:
|
|
3533
3532
|
copyfile(t, os.sep.join([output_tables_folder, os.path.split(t)[-1]]))
|
|
3534
3533
|
|
|
3534
|
+
def _load_frames_to_segment(file, indices, scale_model=None, normalize_kwargs=None):
|
|
3535
|
+
|
|
3536
|
+
frames = load_frames(indices, file, scale=scale_model, normalize_input=True, normalize_kwargs=normalize_kwargs)
|
|
3537
|
+
frames = interpolate_nan_multichannel(frames)
|
|
3538
|
+
|
|
3539
|
+
if np.any(indices==-1):
|
|
3540
|
+
frames[:,:,np.where(indices==-1)[0]] = 0.
|
|
3541
|
+
|
|
3542
|
+
return frames
|
|
3543
|
+
|
|
3544
|
+
def _load_frames_to_measure(file, indices):
|
|
3545
|
+
return load_frames(indices, file, scale=None, normalize_input=False)
|
|
3546
|
+
|
|
3547
|
+
|
|
3548
|
+
def _check_label_dims(lbl, file=None, template=None):
|
|
3549
|
+
|
|
3550
|
+
if file is not None:
|
|
3551
|
+
template = load_frames(0,file,scale=1,normalize_input=False)
|
|
3552
|
+
elif template is not None:
|
|
3553
|
+
template = template
|
|
3554
|
+
else:
|
|
3555
|
+
return lbl
|
|
3556
|
+
|
|
3557
|
+
if lbl.shape != template.shape[:2]:
|
|
3558
|
+
lbl = resize(lbl, template.shape[:2], order=0)
|
|
3559
|
+
return lbl
|
|
3560
|
+
|
|
3535
3561
|
|
|
3536
3562
|
if __name__ == '__main__':
|
|
3537
3563
|
control_segmentation_napari("/home/limozin/Documents/Experiments/MinimumJan/W4/401/", prefix='Aligned',
|