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.
Files changed (38) hide show
  1. celldetective/_version.py +1 -1
  2. celldetective/events.py +4 -0
  3. celldetective/gui/InitWindow.py +23 -9
  4. celldetective/gui/control_panel.py +19 -11
  5. celldetective/gui/generic_signal_plot.py +5 -0
  6. celldetective/gui/help/DL-segmentation-strategy.json +17 -17
  7. celldetective/gui/help/Threshold-vs-DL.json +11 -11
  8. celldetective/gui/help/cell-populations.json +5 -5
  9. celldetective/gui/help/exp-structure.json +15 -15
  10. celldetective/gui/help/feature-btrack.json +5 -5
  11. celldetective/gui/help/neighborhood.json +7 -7
  12. celldetective/gui/help/prefilter-for-segmentation.json +7 -7
  13. celldetective/gui/help/preprocessing.json +19 -19
  14. celldetective/gui/help/propagate-classification.json +7 -7
  15. celldetective/gui/plot_signals_ui.py +13 -9
  16. celldetective/gui/process_block.py +63 -14
  17. celldetective/gui/retrain_segmentation_model_options.py +21 -8
  18. celldetective/gui/retrain_signal_model_options.py +12 -2
  19. celldetective/gui/signal_annotator.py +9 -0
  20. celldetective/gui/signal_annotator2.py +8 -0
  21. celldetective/gui/styles.py +1 -0
  22. celldetective/gui/tableUI.py +1 -1
  23. celldetective/gui/workers.py +136 -0
  24. celldetective/io.py +53 -27
  25. celldetective/measure.py +112 -14
  26. celldetective/scripts/measure_cells.py +10 -35
  27. celldetective/scripts/segment_cells.py +15 -62
  28. celldetective/scripts/segment_cells_thresholds.py +1 -2
  29. celldetective/scripts/track_cells.py +16 -19
  30. celldetective/segmentation.py +16 -62
  31. celldetective/signals.py +11 -7
  32. celldetective/utils.py +587 -67
  33. {celldetective-1.3.6.post2.dist-info → celldetective-1.3.7.dist-info}/METADATA +1 -1
  34. {celldetective-1.3.6.post2.dist-info → celldetective-1.3.7.dist-info}/RECORD +38 -37
  35. {celldetective-1.3.6.post2.dist-info → celldetective-1.3.7.dist-info}/LICENSE +0 -0
  36. {celldetective-1.3.6.post2.dist-info → celldetective-1.3.7.dist-info}/WHEEL +0 -0
  37. {celldetective-1.3.6.post2.dist-info → celldetective-1.3.7.dist-info}/entry_points.txt +0 -0
  38. {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("Open a dialog box to create a new target segmentation model.")
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
- segment_from_threshold_at_position(self.pos, self.mode, self.threshold_config, threads=self.parent_window.parent_window.n_threads)
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
- segment_at_position(self.pos, self.mode, self.model_name, stack_prefix=self.parent_window.movie_prefix, use_gpu=self.parent_window.parent_window.use_gpu, threads=self.parent_window.parent_window.n_threads)
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
- track_at_position(self.pos, self.mode, threads=self.parent_window.parent_window.n_threads)
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
- measure_at_position(self.pos, self.mode, threads=self.parent_window.parent_window.n_threads)
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.cellpose_calibrated = False
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(1, 5)
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
- train_segmentation_model(model_folder+"training_instructions.json", use_gpu=self.parent_window.parent_window.parent_window.use_gpu)
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
- train_signal_model(model_folder+"training_instructions.json")
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
  """
@@ -125,6 +125,7 @@ class Styles(object):
125
125
  font-size: 8px;
126
126
  }
127
127
  QPushButton:hover {
128
+ color: white;
128
129
  background-color: #2070EB;
129
130
  }
130
131
  QPushButton:pressed {
@@ -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
- idx = label_names.index(f"{tzfill}.tif")
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
- idx = label_names.index(f"{tzfill}.tif")
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
- # else no match, try zenodo
2886
- files, categories = get_zenodo_files()
2887
- if name in files:
2888
- index = files.index(name)
2889
- cat = categories[index]
2890
- download_zenodo_file(name, os.sep.join([main_dir, cat]))
2891
- match = os.sep.join([main_dir, cat, name]) + os.sep
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
- if frames.ndim == 3:
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 = [zoom(frames[:,:,c].copy(), [scale,scale], order=3, prefilter=False) for c in range(frames.shape[-1])]
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
- # to revisit
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',