celldetective 1.3.7.post2__py3-none-any.whl → 1.3.8.post1__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 (37) hide show
  1. celldetective/_version.py +1 -1
  2. celldetective/gui/btrack_options.py +8 -8
  3. celldetective/gui/classifier_widget.py +8 -0
  4. celldetective/gui/configure_new_exp.py +1 -1
  5. celldetective/gui/json_readers.py +2 -4
  6. celldetective/gui/plot_signals_ui.py +38 -29
  7. celldetective/gui/process_block.py +1 -0
  8. celldetective/gui/processes/segment_cells.py +50 -23
  9. celldetective/gui/seg_model_loader.py +71 -25
  10. celldetective/gui/signal_annotator2.py +10 -7
  11. celldetective/gui/signal_annotator_options.py +1 -1
  12. celldetective/gui/tableUI.py +252 -20
  13. celldetective/gui/viewers.py +1 -1
  14. celldetective/io.py +28 -20
  15. celldetective/links/zenodo.json +233 -93
  16. celldetective/models/signal_detection/NucCond/classification_loss.png +0 -0
  17. celldetective/models/signal_detection/NucCond/classifier.h5 +0 -0
  18. celldetective/models/signal_detection/NucCond/config_input.json +1 -0
  19. celldetective/models/signal_detection/NucCond/log_classifier.csv +126 -0
  20. celldetective/models/signal_detection/NucCond/log_regressor.csv +282 -0
  21. celldetective/models/signal_detection/NucCond/regression_loss.png +0 -0
  22. celldetective/models/signal_detection/NucCond/regressor.h5 +0 -0
  23. celldetective/models/signal_detection/NucCond/scores.npy +0 -0
  24. celldetective/models/signal_detection/NucCond/test_confusion_matrix.png +0 -0
  25. celldetective/models/signal_detection/NucCond/test_regression.png +0 -0
  26. celldetective/models/signal_detection/NucCond/validation_confusion_matrix.png +0 -0
  27. celldetective/models/signal_detection/NucCond/validation_regression.png +0 -0
  28. celldetective/segmentation.py +48 -1
  29. celldetective/signals.py +43 -13
  30. celldetective/tracking.py +7 -2
  31. celldetective/utils.py +1 -1
  32. {celldetective-1.3.7.post2.dist-info → celldetective-1.3.8.post1.dist-info}/METADATA +1 -1
  33. {celldetective-1.3.7.post2.dist-info → celldetective-1.3.8.post1.dist-info}/RECORD +37 -25
  34. {celldetective-1.3.7.post2.dist-info → celldetective-1.3.8.post1.dist-info}/LICENSE +0 -0
  35. {celldetective-1.3.7.post2.dist-info → celldetective-1.3.8.post1.dist-info}/WHEEL +0 -0
  36. {celldetective-1.3.7.post2.dist-info → celldetective-1.3.8.post1.dist-info}/entry_points.txt +0 -0
  37. {celldetective-1.3.7.post2.dist-info → celldetective-1.3.8.post1.dist-info}/top_level.txt +0 -0
@@ -166,7 +166,7 @@ class DifferentiateColWidget(QWidget, Styles):
166
166
  layout.addLayout(measurement_layout)
167
167
 
168
168
  self.window_size_slider = QLabeledSlider()
169
- self.window_size_slider.setRange(1,np.nanmax(self.parent_window.data.FRAME.to_numpy()))
169
+ self.window_size_slider.setRange(1,int(np.nanmax(self.parent_window.data.FRAME.to_numpy())))
170
170
  self.window_size_slider.setValue(3)
171
171
  window_layout = QHBoxLayout()
172
172
  window_layout.addWidget(QLabel('window size: '), 25)
@@ -215,6 +215,108 @@ class DifferentiateColWidget(QWidget, Styles):
215
215
  self.parent_window.table_view.setModel(self.parent_window.model)
216
216
  self.close()
217
217
 
218
+
219
+
220
+ class OperationOnColsWidget(QWidget, Styles):
221
+
222
+ def __init__(self, parent_window, column1=None, column2=None, operation='divide'):
223
+
224
+ super().__init__()
225
+ self.parent_window = parent_window
226
+ self.column1 = column1
227
+ self.column2 = column2
228
+ self.operation = operation
229
+
230
+ self.setWindowTitle(self.operation)
231
+ # Create the QComboBox and add some items
232
+ center_window(self)
233
+
234
+ layout = QVBoxLayout(self)
235
+ layout.setContentsMargins(30,30,30,30)
236
+
237
+ self.col1_cb = QComboBox()
238
+ self.col1_cb.addItems(list(self.parent_window.data.columns))
239
+ if self.column1 is not None:
240
+ idx = self.col1_cb.findText(self.column1)
241
+ self.col1_cb.setCurrentIndex(idx)
242
+
243
+ numerator_layout = QHBoxLayout()
244
+ numerator_layout.addWidget(QLabel('column 1: '), 25)
245
+ numerator_layout.addWidget(self.col1_cb, 75)
246
+ layout.addLayout(numerator_layout)
247
+
248
+ self.col2_cb = QComboBox()
249
+ self.col2_cb.addItems(list(self.parent_window.data.columns))
250
+ if self.column2 is not None:
251
+ idx = self.col2_cb.findText(self.column2)
252
+ self.col2_cb.setCurrentIndex(idx)
253
+
254
+ denominator_layout = QHBoxLayout()
255
+ denominator_layout.addWidget(QLabel('column 2: '), 25)
256
+ denominator_layout.addWidget(self.col2_cb, 75)
257
+ layout.addLayout(denominator_layout)
258
+
259
+ self.submit_btn = QPushButton('Compute')
260
+ self.submit_btn.setStyleSheet(self.button_style_sheet)
261
+ self.submit_btn.clicked.connect(self.compute)
262
+ layout.addWidget(self.submit_btn, 30)
263
+
264
+ self.setAttribute(Qt.WA_DeleteOnClose)
265
+
266
+ def compute(self):
267
+
268
+ test = self._check_cols_before_operation()
269
+ if not test:
270
+ msgBox = QMessageBox()
271
+ msgBox.setIcon(QMessageBox.Warning)
272
+ msgBox.setText(f"Operation could not be performed, one of the column types is object...")
273
+ msgBox.setWindowTitle("Warning")
274
+ msgBox.setStandardButtons(QMessageBox.Ok)
275
+ returnValue = msgBox.exec()
276
+ if returnValue == QMessageBox.Ok:
277
+ return None
278
+ else:
279
+ return None
280
+ else:
281
+ if self.operation=='divide':
282
+ name = f"{self.col1_txt}/{self.col2_txt}"
283
+ with np.errstate(divide='ignore', invalid='ignore'):
284
+ res = np.true_divide(self.col1, self.col2)
285
+ res[res == np.inf] = np.nan
286
+ res[self.col1!=self.col1] = np.nan
287
+ res[self.col2!=self.col2] = np.nan
288
+ self.parent_window.data[name] = res
289
+
290
+ elif self.operation=='multiply':
291
+ name = f"{self.col1_txt}*{self.col2_txt}"
292
+ res = np.multiply(self.col1, self.col2)
293
+
294
+ elif self.operation=='add':
295
+ name = f"{self.col1_txt}+{self.col2_txt}"
296
+ res = np.add(self.col1, self.col2)
297
+
298
+ elif self.operation=='subtract':
299
+ name = f"{self.col1_txt}-{self.col2_txt}"
300
+ res = np.subtract(self.col1, self.col2)
301
+
302
+ self.parent_window.data[name] = res
303
+ self.parent_window.model = PandasModel(self.parent_window.data)
304
+ self.parent_window.table_view.setModel(self.parent_window.model)
305
+ self.close()
306
+
307
+ def _check_cols_before_operation(self):
308
+
309
+ self.col1_txt = self.col1_cb.currentText()
310
+ self.col2_txt = self.col2_cb.currentText()
311
+
312
+ self.col1 = self.parent_window.data[self.col1_txt].to_numpy()
313
+ self.col2 = self.parent_window.data[self.col2_txt].to_numpy()
314
+
315
+ test = np.all([self.col1.dtype!='O', self.col2.dtype!='O'])
316
+
317
+ return test
318
+
319
+
218
320
  class CalibrateColWidget(GenericOpColWidget):
219
321
 
220
322
  def __init__(self, *args, **kwargs):
@@ -563,12 +665,34 @@ class TableUI(QMainWindow, Styles):
563
665
  self.log_action = QAction('&Log (decimal)...', self)
564
666
  self.log_action.triggered.connect(self.take_log_of_selected_feature)
565
667
  #self.derivative_action.setShortcut("Ctrl+D")
566
- self.mathMenu.addAction(self.log_action)
668
+ self.mathMenu.addAction(self.log_action)
669
+
670
+
671
+ self.divide_action = QAction('&Divide...', self)
672
+ self.divide_action.triggered.connect(self.divide_signals)
673
+ #self.derivative_action.setShortcut("Ctrl+D")
674
+ self.mathMenu.addAction(self.divide_action)
675
+
676
+ self.multiply_action = QAction('&Multiply...', self)
677
+ self.multiply_action.triggered.connect(self.multiply_signals)
678
+ #self.derivative_action.setShortcut("Ctrl+D")
679
+ self.mathMenu.addAction(self.multiply_action)
680
+
681
+ self.add_action = QAction('&Add...', self)
682
+ self.add_action.triggered.connect(self.add_signals)
683
+ #self.derivative_action.setShortcut("Ctrl+D")
684
+ self.mathMenu.addAction(self.add_action)
685
+
686
+ self.subtract_action = QAction('&Subtract...', self)
687
+ self.subtract_action.triggered.connect(self.subtract_signals)
688
+ #self.derivative_action.setShortcut("Ctrl+D")
689
+ self.mathMenu.addAction(self.subtract_action)
567
690
 
568
- self.onehot_action = QAction('&One hot to categorical...', self)
569
- self.onehot_action.triggered.connect(self.transform_one_hot_cols_to_categorical)
570
- #self.onehot_action.setShortcut("Ctrl+D")
571
- self.mathMenu.addAction(self.onehot_action)
691
+
692
+ # self.onehot_action = QAction('&One hot to categorical...', self)
693
+ # self.onehot_action.triggered.connect(self.transform_one_hot_cols_to_categorical)
694
+ # #self.onehot_action.setShortcut("Ctrl+D")
695
+ # self.mathMenu.addAction(self.onehot_action)
572
696
 
573
697
  def collapse_pairs_in_neigh(self):
574
698
 
@@ -734,6 +858,96 @@ class TableUI(QMainWindow, Styles):
734
858
  pos_group.to_csv(pos[0]+os.sep.join(['output', 'tables', f'trajectories_{self.population}.csv']), index=False)
735
859
  print("Done...")
736
860
 
861
+ def divide_signals(self):
862
+
863
+ x = self.table_view.selectedIndexes()
864
+ col_idx = np.unique(np.array([l.column() for l in x]))
865
+ if isinstance(col_idx, (list, np.ndarray)):
866
+ cols = np.array(list(self.data.columns))
867
+ if len(col_idx)>0:
868
+ selected_col1 = str(cols[col_idx[0]])
869
+ if len(col_idx)>1:
870
+ selected_col2 = str(cols[col_idx[1]])
871
+ else:
872
+ selected_col2 = None
873
+ else:
874
+ selected_col1 = None
875
+ selected_col2 = None
876
+ else:
877
+ selected_col1 = None
878
+ selected_col2 = None
879
+
880
+ self.divWidget = OperationOnColsWidget(self, column1=selected_col1, column2=selected_col2, operation='divide')
881
+ self.divWidget.show()
882
+
883
+
884
+ def multiply_signals(self):
885
+
886
+ x = self.table_view.selectedIndexes()
887
+ col_idx = np.unique(np.array([l.column() for l in x]))
888
+ if isinstance(col_idx, (list, np.ndarray)):
889
+ cols = np.array(list(self.data.columns))
890
+ if len(col_idx)>0:
891
+ selected_col1 = str(cols[col_idx[0]])
892
+ if len(col_idx)>1:
893
+ selected_col2 = str(cols[col_idx[1]])
894
+ else:
895
+ selected_col2 = None
896
+ else:
897
+ selected_col1 = None
898
+ selected_col2 = None
899
+ else:
900
+ selected_col1 = None
901
+ selected_col2 = None
902
+
903
+ self.mulWidget = OperationOnColsWidget(self, column1=selected_col1, column2=selected_col2, operation='multiply')
904
+ self.mulWidget.show()
905
+
906
+ def add_signals(self):
907
+
908
+ x = self.table_view.selectedIndexes()
909
+ col_idx = np.unique(np.array([l.column() for l in x]))
910
+ if isinstance(col_idx, (list, np.ndarray)):
911
+ cols = np.array(list(self.data.columns))
912
+ if len(col_idx)>0:
913
+ selected_col1 = str(cols[col_idx[0]])
914
+ if len(col_idx)>1:
915
+ selected_col2 = str(cols[col_idx[1]])
916
+ else:
917
+ selected_col2 = None
918
+ else:
919
+ selected_col1 = None
920
+ selected_col2 = None
921
+ else:
922
+ selected_col1 = None
923
+ selected_col2 = None
924
+
925
+ self.addiWidget = OperationOnColsWidget(self, column1=selected_col1, column2=selected_col2, operation='add')
926
+ self.addiWidget.show()
927
+
928
+ def subtract_signals(self):
929
+
930
+ x = self.table_view.selectedIndexes()
931
+ col_idx = np.unique(np.array([l.column() for l in x]))
932
+ if isinstance(col_idx, (list, np.ndarray)):
933
+ cols = np.array(list(self.data.columns))
934
+ if len(col_idx)>0:
935
+ selected_col1 = str(cols[col_idx[0]])
936
+ if len(col_idx)>1:
937
+ selected_col2 = str(cols[col_idx[1]])
938
+ else:
939
+ selected_col2 = None
940
+ else:
941
+ selected_col1 = None
942
+ selected_col2 = None
943
+ else:
944
+ selected_col1 = None
945
+ selected_col2 = None
946
+
947
+ self.subWidget = OperationOnColsWidget(self, column1=selected_col1, column2=selected_col2, operation='subtract')
948
+ self.subWidget.show()
949
+
950
+
737
951
  def differenciate_selected_feature(self):
738
952
 
739
953
  # check only one col selected and assert is numerical
@@ -742,9 +956,12 @@ class TableUI(QMainWindow, Styles):
742
956
 
743
957
  x = self.table_view.selectedIndexes()
744
958
  col_idx = np.unique(np.array([l.column() for l in x]))
745
- if col_idx!=0:
959
+ if isinstance(col_idx, (list, np.ndarray)):
746
960
  cols = np.array(list(self.data.columns))
747
- selected_col = str(cols[col_idx][0])
961
+ if len(col_idx)>0:
962
+ selected_col = str(cols[col_idx[0]])
963
+ else:
964
+ selected_col = None
748
965
  else:
749
966
  selected_col = None
750
967
 
@@ -759,9 +976,12 @@ class TableUI(QMainWindow, Styles):
759
976
 
760
977
  x = self.table_view.selectedIndexes()
761
978
  col_idx = np.unique(np.array([l.column() for l in x]))
762
- if col_idx!=0:
979
+ if isinstance(col_idx, (list, np.ndarray)):
763
980
  cols = np.array(list(self.data.columns))
764
- selected_col = str(cols[col_idx][0])
981
+ if len(col_idx)>0:
982
+ selected_col = str(cols[col_idx[0]])
983
+ else:
984
+ selected_col = None
765
985
  else:
766
986
  selected_col = None
767
987
 
@@ -772,9 +992,12 @@ class TableUI(QMainWindow, Styles):
772
992
 
773
993
  x = self.table_view.selectedIndexes()
774
994
  col_idx = np.unique(np.array([l.column() for l in x]))
775
- if col_idx!=0:
995
+ if isinstance(col_idx, (list, np.ndarray)):
776
996
  cols = np.array(list(self.data.columns))
777
- selected_col = str(cols[col_idx][0])
997
+ if len(col_idx)>0:
998
+ selected_col = str(cols[col_idx[0]])
999
+ else:
1000
+ selected_col = None
778
1001
  else:
779
1002
  selected_col = None
780
1003
 
@@ -790,9 +1013,12 @@ class TableUI(QMainWindow, Styles):
790
1013
 
791
1014
  x = self.table_view.selectedIndexes()
792
1015
  col_idx = np.unique(np.array([l.column() for l in x]))
793
- if col_idx!=0:
1016
+ if isinstance(col_idx, (list, np.ndarray)):
794
1017
  cols = np.array(list(self.data.columns))
795
- selected_col = str(cols[col_idx][0])
1018
+ if len(col_idx)>0:
1019
+ selected_col = str(cols[col_idx[0]])
1020
+ else:
1021
+ selected_col = None
796
1022
  else:
797
1023
  selected_col = None
798
1024
 
@@ -804,11 +1030,14 @@ class TableUI(QMainWindow, Styles):
804
1030
 
805
1031
  x = self.table_view.selectedIndexes()
806
1032
  col_idx = np.unique(np.array([l.column() for l in x]))
807
- if list(col_idx):
1033
+ if isinstance(col_idx, (list, np.ndarray)):
808
1034
  cols = np.array(list(self.data.columns))
809
- selected_cols = cols[col_idx]
1035
+ if len(col_idx)>0:
1036
+ selected_col = str(cols[col_idx[0]])
1037
+ else:
1038
+ selected_col = None
810
1039
  else:
811
- selected_cols = None
1040
+ selected_col = None
812
1041
 
813
1042
  self.mergewidget = MergeOneHotWidget(self, selected_columns=selected_cols)
814
1043
  self.mergewidget.show()
@@ -874,7 +1103,7 @@ class TableUI(QMainWindow, Styles):
874
1103
  self.projection_option.setChecked(True)
875
1104
  self.projection_option.toggled.connect(self.enable_projection_options)
876
1105
  self.projection_op_cb = QComboBox()
877
- self.projection_op_cb.addItems(['mean','median','min','max', 'prod', 'sum'])
1106
+ self.projection_op_cb.addItems(['mean','median','min','max','first','last','prod','sum'])
878
1107
 
879
1108
  projection_layout = QHBoxLayout()
880
1109
  projection_layout.addWidget(self.projection_option, 33)
@@ -1044,8 +1273,11 @@ class TableUI(QMainWindow, Styles):
1044
1273
  all_cms = list(colormaps)
1045
1274
  for cm in all_cms:
1046
1275
  if hasattr(matplotlib.cm, str(cm).lower()):
1047
- self.cmap_cb.addColormap(cm.lower())
1048
-
1276
+ try:
1277
+ self.cmap_cb.addColormap(cm.lower())
1278
+ except:
1279
+ pass
1280
+
1049
1281
  hbox = QHBoxLayout()
1050
1282
  hbox.addWidget(QLabel('colormap: '), 33)
1051
1283
  hbox.addWidget(self.cmap_cb, 66)
@@ -932,7 +932,7 @@ class CellSizeViewer(StackVisualizer):
932
932
  with interactive sliders for diameter adjustment and circle display.
933
933
  """
934
934
 
935
- def __init__(self, initial_diameter=40, set_radius_in_list=False, diameter_slider_range=(0,200), parent_le=None, parent_list_widget=None, *args, **kwargs):
935
+ def __init__(self, initial_diameter=40, set_radius_in_list=False, diameter_slider_range=(0,500), parent_le=None, parent_list_widget=None, *args, **kwargs):
936
936
  # Initialize the widget and its attributes
937
937
 
938
938
  super().__init__(*args, **kwargs)
celldetective/io.py CHANGED
@@ -16,7 +16,7 @@ import concurrent.futures
16
16
  from csbdeep.utils import normalize_mi_ma
17
17
  from csbdeep.io import save_tiff_imagej_compatible
18
18
 
19
- import skimage.io as skio
19
+ import imageio.v2 as imageio
20
20
  from skimage.measure import regionprops_table, label
21
21
 
22
22
  from btrack.datasets import cell_config
@@ -400,6 +400,23 @@ def get_experiment_metadata(experiment):
400
400
  metadata = ConfigSectionMap(config, "Metadata")
401
401
  return metadata
402
402
 
403
+ def get_experiment_labels(experiment):
404
+
405
+ config = get_config(experiment)
406
+ wells = get_experiment_wells(experiment)
407
+ nbr_of_wells = len(wells)
408
+
409
+ labels = ConfigSectionMap(config, "Labels")
410
+ for k in list(labels.keys()):
411
+ values = labels[k].split(',')
412
+ if nbr_of_wells != len(values):
413
+ values = [str(s) for s in np.linspace(0, nbr_of_wells - 1, nbr_of_wells)]
414
+ if np.all([s.isnumeric() for s in values]):
415
+ values = [float(s) for s in values]
416
+ labels.update({k: values})
417
+
418
+ return labels
419
+
403
420
 
404
421
  def get_experiment_concentrations(experiment, dtype=str):
405
422
 
@@ -982,10 +999,8 @@ def load_experiment_tables(experiment, population='targets', well_option='*', po
982
999
  wells = get_experiment_wells(experiment)
983
1000
 
984
1001
  movie_prefix = ConfigSectionMap(config, "MovieSettings")["movie_prefix"]
985
- concentrations = get_experiment_concentrations(experiment, dtype=float)
986
- cell_types = get_experiment_cell_types(experiment)
987
- antibodies = get_experiment_antibodies(experiment)
988
- pharmaceutical_agents = get_experiment_pharmaceutical_agents(experiment)
1002
+
1003
+ labels = get_experiment_labels(experiment)
989
1004
  metadata = get_experiment_metadata(experiment) # None or dict of metadata
990
1005
  well_labels = _extract_labels_from_config(config, len(wells))
991
1006
 
@@ -1001,14 +1016,8 @@ def load_experiment_tables(experiment, population='targets', well_option='*', po
1001
1016
 
1002
1017
  well_name, well_number = extract_well_name_and_number(well_path)
1003
1018
  widx = well_indices[k]
1004
-
1005
1019
  well_alias = well_labels[widx]
1006
1020
 
1007
- well_concentration = concentrations[widx]
1008
- well_antibody = antibodies[widx]
1009
- well_cell_type = cell_types[widx]
1010
- well_pharmaceutical_agent = pharmaceutical_agents[widx]
1011
-
1012
1021
  positions = get_positions_in_well(well_path)
1013
1022
  if position_indices is not None:
1014
1023
  try:
@@ -1037,10 +1046,13 @@ def load_experiment_tables(experiment, population='targets', well_option='*', po
1037
1046
  df_pos['well_name'] = well_name
1038
1047
  df_pos['pos_name'] = pos_name
1039
1048
 
1040
- df_pos['concentration'] = well_concentration
1041
- df_pos['antibody'] = well_antibody
1042
- df_pos['cell_type'] = well_cell_type
1043
- df_pos['pharmaceutical_agent'] = well_pharmaceutical_agent
1049
+ for k in list(labels.keys()):
1050
+ values = labels[k]
1051
+ try:
1052
+ df_pos[k] = values[widx]
1053
+ except Exception as e:
1054
+ print(f"{e=}")
1055
+
1044
1056
  if metadata is not None:
1045
1057
  keys = list(metadata.keys())
1046
1058
  for k in keys:
@@ -1052,10 +1064,6 @@ def load_experiment_tables(experiment, population='targets', well_option='*', po
1052
1064
  pos_dict = {'pos_path': pos_path, 'pos_index': real_pos_index, 'pos_name': pos_name, 'table_path': table,
1053
1065
  'stack_path': stack_path,'well_path': well_path, 'well_index': real_well_index, 'well_name': well_name,
1054
1066
  'well_number': well_number, 'well_alias': well_alias}
1055
- # if metadata is not None:
1056
- # keys = list(metadata.keys())
1057
- # for k in keys:
1058
- # pos_dict.update({k: metadata[k]})
1059
1067
 
1060
1068
  df_pos_info.append(pos_dict)
1061
1069
 
@@ -3335,7 +3343,7 @@ def load_frames(img_nums, stack_path, scale=None, normalize_input=True, dtype=fl
3335
3343
  """
3336
3344
 
3337
3345
  try:
3338
- frames = skio.imread(stack_path, key=img_nums, plugin="tifffile")
3346
+ frames = imageio.imread(stack_path, key=img_nums)
3339
3347
  except Exception as e:
3340
3348
  print(
3341
3349
  f'Error in loading the frame {img_nums} {e}. Please check that the experiment channel information is consistent with the movie being read.')