celldetective 1.1.1.post3__tar.gz → 1.1.1.post4__tar.gz

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 (90) hide show
  1. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/PKG-INFO +1 -1
  2. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/__main__.py +17 -0
  3. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/classifier_widget.py +10 -3
  4. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/control_panel.py +11 -4
  5. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/layouts.py +9 -7
  6. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/neighborhood_options.py +11 -5
  7. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/signal_annotator.py +85 -25
  8. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/tableUI.py +15 -6
  9. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/io.py +69 -3
  10. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/neighborhood.py +96 -26
  11. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/preprocessing.py +81 -61
  12. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/segmentation.py +67 -29
  13. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/utils.py +51 -12
  14. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective.egg-info/PKG-INFO +1 -1
  15. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/setup.py +1 -1
  16. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/tests/test_segmentation.py +1 -1
  17. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/LICENSE +0 -0
  18. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/README.md +0 -0
  19. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/__init__.py +0 -0
  20. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/datasets/segmentation_annotations/blank +0 -0
  21. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/datasets/signal_annotations/blank +0 -0
  22. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/events.py +0 -0
  23. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/extra_properties.py +0 -0
  24. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/filters.py +0 -0
  25. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/__init__.py +0 -0
  26. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/about.py +0 -0
  27. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/analyze_block.py +0 -0
  28. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/btrack_options.py +0 -0
  29. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/configure_new_exp.py +0 -0
  30. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/gui_utils.py +0 -0
  31. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/json_readers.py +0 -0
  32. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/measurement_options.py +0 -0
  33. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/plot_measurements.py +0 -0
  34. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/plot_signals_ui.py +0 -0
  35. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/process_block.py +0 -0
  36. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/retrain_segmentation_model_options.py +0 -0
  37. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/retrain_signal_model_options.py +0 -0
  38. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/seg_model_loader.py +0 -0
  39. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/signal_annotator_options.py +0 -0
  40. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/styles.py +0 -0
  41. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/survival_ui.py +0 -0
  42. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/thresholds_gui.py +0 -0
  43. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/gui/viewers.py +0 -0
  44. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/icons/logo-large.png +0 -0
  45. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/icons/logo.png +0 -0
  46. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/icons/signals_icon.png +0 -0
  47. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/icons/splash-test.png +0 -0
  48. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/icons/splash.png +0 -0
  49. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/icons/splash0.png +0 -0
  50. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/icons/survival2.png +0 -0
  51. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/icons/vignette_signals2.png +0 -0
  52. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/icons/vignette_signals2.svg +0 -0
  53. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/links/zenodo.json +0 -0
  54. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/measure.py +0 -0
  55. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/models/segmentation_effectors/blank +0 -0
  56. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/models/segmentation_effectors/primNK_cfse/config_input.json +0 -0
  57. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/models/segmentation_effectors/primNK_cfse/cp-cfse-transfer +0 -0
  58. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/models/segmentation_effectors/primNK_cfse/training_instructions.json +0 -0
  59. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/models/segmentation_generic/blank +0 -0
  60. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/models/segmentation_targets/blank +0 -0
  61. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/models/signal_detection/blank +0 -0
  62. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/models/tracking_configs/mcf7.json +0 -0
  63. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/models/tracking_configs/ricm.json +0 -0
  64. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/models/tracking_configs/ricm2.json +0 -0
  65. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/scripts/analyze_signals.py +0 -0
  66. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/scripts/measure_cells.py +0 -0
  67. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/scripts/segment_cells.py +0 -0
  68. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/scripts/segment_cells_thresholds.py +0 -0
  69. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/scripts/track_cells.py +0 -0
  70. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/scripts/train_segmentation_model.py +0 -0
  71. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/scripts/train_signal_model.py +0 -0
  72. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/signals.py +0 -0
  73. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective/tracking.py +0 -0
  74. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective.egg-info/SOURCES.txt +0 -0
  75. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective.egg-info/dependency_links.txt +0 -0
  76. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective.egg-info/entry_points.txt +0 -0
  77. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective.egg-info/not-zip-safe +0 -0
  78. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective.egg-info/requires.txt +0 -0
  79. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/celldetective.egg-info/top_level.txt +0 -0
  80. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/setup.cfg +0 -0
  81. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/tests/__init__.py +0 -0
  82. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/tests/test_events.py +0 -0
  83. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/tests/test_filters.py +0 -0
  84. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/tests/test_io.py +0 -0
  85. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/tests/test_measure.py +0 -0
  86. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/tests/test_neighborhood.py +0 -0
  87. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/tests/test_preprocessing.py +0 -0
  88. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/tests/test_signals.py +0 -0
  89. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/tests/test_tracking.py +0 -0
  90. {celldetective-1.1.1.post3 → celldetective-1.1.1.post4}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: celldetective
3
- Version: 1.1.1.post3
3
+ Version: 1.1.1.post4
4
4
  Summary: description
5
5
  Home-page: http://github.com/remyeltorro/celldetective
6
6
  Author: Rémy Torro
@@ -108,6 +108,10 @@ class AppInitWindow(QMainWindow):
108
108
  OptionsMenu.addAction(self.MemoryAndThreadsAction)
109
109
  menuBar.addMenu(OptionsMenu)
110
110
 
111
+ PluginsMenu = QMenu("Plugins", self)
112
+ PluginsMenu.addAction(self.CorrectAnnotationAction)
113
+ menuBar.addMenu(PluginsMenu)
114
+
111
115
  helpMenu = QMenu("Help", self)
112
116
  helpMenu.clear()
113
117
  helpMenu.addAction(self.DocumentationAction)
@@ -130,6 +134,8 @@ class AppInitWindow(QMainWindow):
130
134
 
131
135
  self.MemoryAndThreadsAction = QAction('Memory & Threads...')
132
136
 
137
+ self.CorrectAnnotationAction = QAction('Correct a segmentation annotation...')
138
+
133
139
  self.newExpAction = QAction('New', self)
134
140
  self.newExpAction.setShortcut("Ctrl+N")
135
141
  self.newExpAction.setShortcutVisibleInContextMenu(True)
@@ -156,6 +162,7 @@ class AppInitWindow(QMainWindow):
156
162
  self.openModels.triggered.connect(self.open_models_folder)
157
163
  self.AboutAction.triggered.connect(self.open_about_window)
158
164
  self.MemoryAndThreadsAction.triggered.connect(self.set_memory_and_threads)
165
+ self.CorrectAnnotationAction.triggered.connect(self.correct_seg_annotation)
159
166
 
160
167
  self.DocumentationAction.triggered.connect(self.open_documentation)
161
168
 
@@ -186,6 +193,15 @@ class AppInitWindow(QMainWindow):
186
193
  for r in self.recentFileActs:
187
194
  r.triggered.connect(lambda checked, item=r: self.load_recent_exp(item.text()))
188
195
 
196
+ def correct_seg_annotation(self):
197
+
198
+ self.filename,_ = QFileDialog.getOpenFileName(self,"Open Image", "/home/", "TIF Files (*.tif)")
199
+ if self.filename!='':
200
+ print('Opening ',self.filename,' in napari...')
201
+ correct_annotation(self.filename)
202
+ else:
203
+ return None
204
+
189
205
  def set_memory_and_threads(self):
190
206
 
191
207
  print('setting memory and threads')
@@ -405,6 +421,7 @@ if __name__ == "__main__":
405
421
  import subprocess
406
422
  import os
407
423
  from celldetective.gui.about import AboutWidget
424
+ from celldetective.io import correct_annotation
408
425
  import psutil
409
426
  import subprocess
410
427
  import json
@@ -12,6 +12,7 @@ from sklearn.metrics import r2_score
12
12
  from scipy.optimize import curve_fit
13
13
  from celldetective.gui import Styles
14
14
  from math import ceil
15
+ from celldetective.utils import extract_cols_from_query
15
16
 
16
17
  def step_function(t, t_shift, dt):
17
18
  return 1/(1+np.exp(-(t-t_shift)/dt))
@@ -263,17 +264,23 @@ class ClassifierWidget(QWidget, Styles):
263
264
  self.propscanvas.canvas.draw_idle()
264
265
 
265
266
  def apply_property_query(self):
267
+
266
268
  query = self.property_query_le.text()
267
269
  self.df[self.class_name] = 1
268
270
 
269
- print(query, self.class_name)
271
+ cols = extract_cols_from_query(query)
272
+ print(cols)
273
+ cols_in_df = np.all([c in list(self.df.columns) for c in cols], axis=0)
274
+ print(f'Testing if columns from query are in the dataframe: {cols_in_df}...')
270
275
 
271
276
  if query=='':
272
277
  print('empty query')
273
278
  else:
274
279
  try:
275
- self.selection = self.df.query(query).index
276
- print(self.selection)
280
+ if cols_in_df:
281
+ self.selection = self.df.dropna(subset=cols).query(query).index
282
+ else:
283
+ self.selection = self.df.query(query).index
277
284
  self.df.loc[self.selection, self.class_name] = 0
278
285
  except Exception as e:
279
286
  print(e)
@@ -16,6 +16,7 @@ import subprocess
16
16
  from celldetective.gui.viewers import StackVisualizer
17
17
  from celldetective.utils import extract_experiment_channels
18
18
  from celldetective.gui import Styles
19
+ import pandas as pd
19
20
 
20
21
  class ControlPanel(QMainWindow, Styles):
21
22
 
@@ -452,26 +453,32 @@ class ControlPanel(QMainWindow, Styles):
452
453
  self.ProcessEffectors.check_tracking_result_btn.setEnabled(False)
453
454
 
454
455
  if os.path.exists(os.sep.join([self.pos,'output','tables','trajectories_effectors.csv'])):
456
+ cols = pd.read_csv(os.sep.join([self.pos,'output','tables','trajectories_effectors.csv']), nrows=1).columns.tolist()
455
457
  self.ProcessEffectors.check_measurements_btn.setEnabled(True)
456
- self.ProcessEffectors.check_signals_btn.setEnabled(True)
458
+ if 'TRACK_ID' in cols:
459
+ self.ProcessEffectors.check_signals_btn.setEnabled(True)
457
460
  #self.ProcessEffectors.signal_analysis_action.setEnabled(True)
458
461
  self.ProcessEffectors.view_tab_btn.setEnabled(True)
459
-
462
+ self.ProcessEffectors.classify_btn.setEnabled(True)
460
463
  else:
461
464
  self.ProcessEffectors.check_measurements_btn.setEnabled(False)
462
465
  self.ProcessEffectors.check_signals_btn.setEnabled(False)
463
466
  #self.ProcessEffectors.signal_analysis_action.setEnabled(False)
464
467
  self.ProcessEffectors.view_tab_btn.setEnabled(False)
468
+ self.ProcessEffectors.classify_btn.setEnabled(False)
465
469
 
466
470
  if os.path.exists(os.sep.join([self.pos,'output','tables','trajectories_targets.csv'])):
471
+ cols = pd.read_csv(os.sep.join([self.pos,'output','tables','trajectories_targets.csv']), nrows=1).columns.tolist()
467
472
  self.ProcessTargets.check_measurements_btn.setEnabled(True)
468
- self.ProcessTargets.check_signals_btn.setEnabled(True)
473
+ if 'TRACK_ID' in cols:
474
+ self.ProcessTargets.check_signals_btn.setEnabled(True)
469
475
  #self.ProcessTargets.signal_analysis_action.setEnabled(True)
470
476
  self.ProcessTargets.view_tab_btn.setEnabled(True)
471
-
477
+ self.ProcessTargets.classify_btn.setEnabled(True)
472
478
  else:
473
479
  self.ProcessTargets.check_measurements_btn.setEnabled(False)
474
480
  self.ProcessTargets.check_signals_btn.setEnabled(False)
475
481
  #self.ProcessTargets.signal_analysis_action.setEnabled(False)
476
482
  self.ProcessTargets.view_tab_btn.setEnabled(False)
483
+ self.ProcessTargets.classify_btn.setEnabled(False)
477
484
 
@@ -990,10 +990,12 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
990
990
  )
991
991
  bg = bg[0]
992
992
  bg = bg['bg']
993
-
994
- self.viewer = StackVisualizer(
995
- stack=[bg],
996
- window_title='Reconstructed background',
997
- frame_slider = False,
998
- )
999
- self.viewer.show()
993
+ print(bg)
994
+ if len(bg)>0:
995
+
996
+ self.viewer = StackVisualizer(
997
+ stack=[bg],
998
+ window_title='Reconstructed background',
999
+ frame_slider = False,
1000
+ )
1001
+ self.viewer.show()
@@ -319,16 +319,16 @@ class ConfigNeighborhoods(QWidget, Styles):
319
319
  def fill_cbs_of_neighbor_population(self):
320
320
 
321
321
  population = self.neighbor_population_cb.currentText()
322
- class_cols, status_cols, time_cols = self.locate_population_specific_columns(population)
322
+ class_cols, status_cols, group_cols, time_cols = self.locate_population_specific_columns(population)
323
323
  self.neighbor_population_status_cb.clear()
324
- self.neighbor_population_status_cb.addItems(['--','class', 'status']+class_cols+status_cols)
324
+ self.neighbor_population_status_cb.addItems(['--','class', 'status']+class_cols+status_cols+group_cols)
325
325
 
326
326
  def fill_cbs_of_reference_population(self):
327
327
 
328
328
  population = self.reference_population_cb.currentText()
329
- class_cols, status_cols, time_cols = self.locate_population_specific_columns(population)
329
+ class_cols, status_cols, group_cols, time_cols = self.locate_population_specific_columns(population)
330
330
  self.reference_population_status_cb.clear()
331
- self.reference_population_status_cb.addItems(['--','class', 'status']+class_cols+status_cols)
331
+ self.reference_population_status_cb.addItems(['--','class', 'status']+class_cols+status_cols+group_cols)
332
332
  self.event_time_cb.addItems(['--', 't0']+time_cols)
333
333
 
334
334
  def switch_not_reference(self):
@@ -364,6 +364,7 @@ class ConfigNeighborhoods(QWidget, Styles):
364
364
 
365
365
  class_idx = np.array([s.startswith('class_') for s in self.all_columns])
366
366
  status_idx = np.array([s.startswith('status_') for s in self.all_columns])
367
+ group_idx = np.array([s.startswith('group_') for s in self.all_columns])
367
368
  time_idx = np.array([s.startswith('t_') for s in self.all_columns])
368
369
 
369
370
  if len(class_idx)>0:
@@ -379,12 +380,17 @@ class ConfigNeighborhoods(QWidget, Styles):
379
380
  else:
380
381
  status_columns = []
381
382
 
383
+ if len(group_idx)>0:
384
+ group_columns = list(self.all_columns[group_idx])
385
+ else:
386
+ group_columns = []
387
+
382
388
  if len(time_idx)>0:
383
389
  time_columns = list(self.all_columns[time_idx])
384
390
  else:
385
391
  time_columns = []
386
392
 
387
- return class_columns, status_columns, time_columns
393
+ return class_columns, status_columns, group_columns, time_columns
388
394
 
389
395
  def write_instructions(self):
390
396
 
@@ -27,6 +27,7 @@ from matplotlib.cm import tab10
27
27
  import pandas as pd
28
28
  from sklearn.preprocessing import MinMaxScaler
29
29
  from celldetective.gui import Styles
30
+ from celldetective.measure import contour_of_instance_segmentation
30
31
 
31
32
  class SignalAnnotator(QMainWindow, Styles):
32
33
  """
@@ -76,9 +77,9 @@ class SignalAnnotator(QMainWindow, Styles):
76
77
 
77
78
  self.populate_widget()
78
79
 
79
- self.setMinimumWidth(int(0.8 * self.screen_width))
80
+ #self.setMinimumWidth(int(0.8 * self.screen_width))
80
81
  # self.setMaximumHeight(int(0.8*self.screen_height))
81
- self.setMinimumHeight(int(0.8 * self.screen_height))
82
+ #self.setMinimumHeight(int(0.8 * self.screen_height))
82
83
  # self.setMaximumHeight(int(0.8*self.screen_height))
83
84
 
84
85
  self.setAttribute(Qt.WA_DeleteOnClose)
@@ -119,7 +120,6 @@ class SignalAnnotator(QMainWindow, Styles):
119
120
 
120
121
  self.class_choice_cb.addItems(self.class_cols)
121
122
  self.class_choice_cb.currentIndexChanged.connect(self.compute_status_and_colors)
122
- self.class_choice_cb.setCurrentIndex(0)
123
123
 
124
124
  class_hbox.addWidget(self.class_choice_cb, 70)
125
125
 
@@ -346,6 +346,8 @@ class SignalAnnotator(QMainWindow, Styles):
346
346
  main_layout.addLayout(self.right_panel, 65)
347
347
  self.button_widget.adjustSize()
348
348
 
349
+ self.compute_status_and_colors(0)
350
+
349
351
  self.setCentralWidget(self.button_widget)
350
352
  self.show()
351
353
 
@@ -462,6 +464,7 @@ class SignalAnnotator(QMainWindow, Styles):
462
464
  self.newClassWidget.close()
463
465
 
464
466
  def compute_status_and_colors(self, i):
467
+
465
468
  self.class_name = self.class_choice_cb.currentText()
466
469
  self.expected_status = 'status'
467
470
  suffix = self.class_name.replace('class', '').replace('_', '', 1)
@@ -474,11 +477,15 @@ class SignalAnnotator(QMainWindow, Styles):
474
477
  self.status_name = self.expected_status
475
478
 
476
479
  print('selection and expected names: ', self.class_name, self.expected_time, self.expected_status)
480
+ cols = list(self.df_tracks.columns)
477
481
 
478
- if self.time_name in self.df_tracks.columns and self.class_name in self.df_tracks.columns and not self.status_name in self.df_tracks.columns:
482
+ if self.time_name in cols and self.class_name in cols and not self.status_name in cols:
479
483
  # only create the status column if it does not exist to not erase static classification results
480
484
  self.make_status_column()
481
- elif self.time_name in self.df_tracks.columns and self.class_name in self.df_tracks.columns:
485
+ elif self.time_name in cols and self.class_name in cols and self.df_tracks[self.status_name].isnull().all():
486
+ print('this is the case!', )
487
+ self.make_status_column()
488
+ elif self.time_name in cols and self.class_name in cols:
482
489
  # all good, do nothing
483
490
  pass
484
491
  else:
@@ -855,7 +862,7 @@ class SignalAnnotator(QMainWindow, Styles):
855
862
  self.cell_ax.legend()
856
863
  self.cell_fcanvas.canvas.draw()
857
864
  except Exception as e:
858
- print(f"{e=}")
865
+ print(f"Plot signals: {e=}")
859
866
 
860
867
  def extract_scatter_from_trajectories(self):
861
868
 
@@ -1109,7 +1116,7 @@ class SignalAnnotator(QMainWindow, Styles):
1109
1116
  if len(min_values) > 0:
1110
1117
  self.cell_ax.set_ylim(np.amin(min_values), np.amax(max_values))
1111
1118
  except Exception as e:
1112
- print(e)
1119
+ print('Ylim error:',e)
1113
1120
 
1114
1121
  def draw_frame(self, framedata):
1115
1122
 
@@ -1301,7 +1308,9 @@ class SignalAnnotator(QMainWindow, Styles):
1301
1308
 
1302
1309
 
1303
1310
  class MeasureAnnotator(SignalAnnotator):
1311
+
1304
1312
  def __init__(self, parent_window=None):
1313
+
1305
1314
  QMainWindow.__init__(self)
1306
1315
  self.parent_window = parent_window
1307
1316
  self.setWindowTitle("Signal annotator")
@@ -1315,11 +1324,11 @@ class MeasureAnnotator(SignalAnnotator):
1315
1324
  self.int_validator = QIntValidator()
1316
1325
  self.current_alpha=0.5
1317
1326
  if self.mode == "targets":
1318
- self.instructions_path = self.exp_dir + "configs/signal_annotator_config_targets.json"
1319
- self.trajectories_path = self.pos + 'output/tables/trajectories_targets.csv'
1327
+ self.instructions_path = self.exp_dir + os.sep.join(['configs','signal_annotator_config_targets.json'])
1328
+ self.trajectories_path = self.pos + os.sep.join(['output','tables','trajectories_targets.csv'])
1320
1329
  elif self.mode == "effectors":
1321
- self.instructions_path = self.exp_dir + "configs/signal_annotator_config_effectors.json"
1322
- self.trajectories_path = self.pos + 'output/tables/trajectories_effectors.csv'
1330
+ self.instructions_path = self.exp_dir + os.sep.join(['configs','signal_annotator_config_effectors.json'])
1331
+ self.trajectories_path = self.pos + os.sep.join(['output','tables','trajectories_effectors.csv'])
1323
1332
 
1324
1333
  self.screen_height = self.parent_window.parent_window.parent_window.screen_height
1325
1334
  self.screen_width = self.parent_window.parent_window.parent_window.screen_width
@@ -1330,8 +1339,13 @@ class MeasureAnnotator(SignalAnnotator):
1330
1339
  center_window(self)
1331
1340
 
1332
1341
  self.locate_stack()
1342
+
1333
1343
  data, properties, graph, labels, _ = load_napari_data(self.pos, prefix=None, population=self.mode,return_stack=False)
1334
- self.labels = relabel_segmentation(labels,data,properties)
1344
+ if data is not None:
1345
+ self.labels = relabel_segmentation(labels,data,properties)
1346
+ else:
1347
+ self.labels = labels
1348
+
1335
1349
  self.current_channel = 0
1336
1350
 
1337
1351
  self.locate_tracks()
@@ -1577,8 +1591,10 @@ class MeasureAnnotator(SignalAnnotator):
1577
1591
  self.class_choice_cb = QComboBox()
1578
1592
 
1579
1593
  cols = np.array(self.df_tracks.columns)
1580
- self.class_cols = np.array([c.startswith('group') for c in list(self.df_tracks.columns)])
1594
+ self.class_cols = np.array([c.startswith('group') or c.startswith('status') for c in list(self.df_tracks.columns)])
1581
1595
  self.class_cols = list(cols[self.class_cols])
1596
+ print(self.class_cols)
1597
+
1582
1598
  try:
1583
1599
  self.class_cols.remove('group_id')
1584
1600
  except Exception:
@@ -1711,6 +1727,14 @@ class MeasureAnnotator(SignalAnnotator):
1711
1727
  btn_hbox.addWidget(self.save_btn, 90)
1712
1728
  self.left_panel.addLayout(btn_hbox)
1713
1729
 
1730
+ self.export_btn = QPushButton('')
1731
+ self.export_btn.setStyleSheet(self.button_select_all)
1732
+ self.export_btn.clicked.connect(self.export_measurements)
1733
+ self.export_btn.setIcon(icon(MDI6.export, color="black"))
1734
+ self.export_btn.setIconSize(QSize(25, 25))
1735
+ btn_hbox.addWidget(self.export_btn, 10)
1736
+ self.left_panel.addLayout(btn_hbox)
1737
+
1714
1738
  # Animation
1715
1739
  animation_buttons_box = QHBoxLayout()
1716
1740
 
@@ -1810,6 +1834,28 @@ class MeasureAnnotator(SignalAnnotator):
1810
1834
  # del self.img
1811
1835
  gc.collect()
1812
1836
 
1837
+
1838
+ def export_measurements(self):
1839
+
1840
+ auto_dataset_name = self.pos.split(os.sep)[-4] + '_' + self.pos.split(os.sep)[-2] + f'_{str(self.current_frame).zfill(3)}' + f'_{self.status_name}.npy'
1841
+
1842
+ if self.normalized_signals:
1843
+ self.normalize_features_btn.click()
1844
+
1845
+ subdf = self.df_tracks.loc[self.df_tracks['FRAME']==self.current_frame,:]
1846
+ subdf['class'] = subdf[self.status_name]
1847
+ dico = subdf.to_dict('records')
1848
+
1849
+ pathsave = QFileDialog.getSaveFileName(self, "Select file name", self.exp_dir + auto_dataset_name, ".npy")[0]
1850
+ if pathsave != '':
1851
+ if not pathsave.endswith(".npy"):
1852
+ pathsave += ".npy"
1853
+ try:
1854
+ np.save(pathsave, dico)
1855
+ print(f'File successfully written in {pathsave}.')
1856
+ except Exception as e:
1857
+ print(f"Error {e}...")
1858
+
1813
1859
  def set_next_frame(self):
1814
1860
 
1815
1861
  self.current_frame = self.current_frame + 1
@@ -1887,13 +1933,17 @@ class MeasureAnnotator(SignalAnnotator):
1887
1933
 
1888
1934
  def give_cell_information(self):
1889
1935
 
1890
- cell_selected = f"cell: {self.track_of_interest}\n"
1891
- if 'TRACK_ID' in self.df_tracks.columns:
1892
- cell_status = f"phenotype: {self.df_tracks.loc[self.df_tracks['TRACK_ID'] == self.track_of_interest, self.status_name].to_numpy()[0]}\n"
1893
- else:
1894
- cell_status = f"phenotype: {self.df_tracks.loc[self.df_tracks['ID'] == self.track_of_interest, self.status_name].to_numpy()[0]}\n"
1895
- self.cell_info.setText(cell_selected + cell_status)
1896
-
1936
+ try:
1937
+ cell_selected = f"cell: {self.track_of_interest}\n"
1938
+ if 'TRACK_ID' in self.df_tracks.columns:
1939
+ cell_status = f"phenotype: {self.df_tracks.loc[self.df_tracks['TRACK_ID'] == self.track_of_interest, self.status_name].to_numpy()[0]}\n"
1940
+ else:
1941
+ cell_status = f"phenotype: {self.df_tracks.loc[self.df_tracks['ID'] == self.track_of_interest, self.status_name].to_numpy()[0]}\n"
1942
+ self.cell_info.setText(cell_selected + cell_status)
1943
+ except Exception as e:
1944
+ print('Cell info:',e)
1945
+ print(self.track_of_interest, self.status_name)
1946
+
1897
1947
  def create_new_event_class(self):
1898
1948
 
1899
1949
  # display qwidget to name the event
@@ -1978,8 +2028,14 @@ class MeasureAnnotator(SignalAnnotator):
1978
2028
  self.frame_lbl.setText(f'position: {self.framedata}')
1979
2029
  self.im.set_array(self.img)
1980
2030
  self.status_scatter.set_offsets(self.positions[self.framedata])
1981
- self.status_scatter.set_edgecolors(self.colors[self.framedata][:, 0])
2031
+ try:
2032
+ self.status_scatter.set_edgecolors(self.colors[self.framedata][:, 0])
2033
+ except Exception as e:
2034
+ print('L1993: ',e)
2035
+
1982
2036
  self.current_label = self.labels[self.current_frame]
2037
+ self.current_label = contour_of_instance_segmentation(self.current_label, 5)
2038
+
1983
2039
  self.im_mask.remove()
1984
2040
  self.im_mask = self.ax.imshow(np.ma.masked_where(self.current_label == 0, self.current_label),
1985
2041
  cmap='viridis', interpolation='none',alpha=self.current_alpha,vmin=0,vmax=np.nanmax(self.labels.flatten()))
@@ -2108,9 +2164,9 @@ class MeasureAnnotator(SignalAnnotator):
2108
2164
 
2109
2165
  self.extract_scatter_from_trajectories()
2110
2166
  if 'TRACK_ID' in self.df_tracks.columns:
2111
- self.track_of_interest = self.df_tracks['TRACK_ID'].min()
2167
+ self.track_of_interest = self.df_tracks.dropna(subset='TRACK_ID')['TRACK_ID'].min()
2112
2168
  else:
2113
- self.track_of_interest = self.df_tracks['ID'].min()
2169
+ self.track_of_interest = self.df_tracks.dropna(subset='ID')['ID'].min()
2114
2170
 
2115
2171
  self.loc_t = []
2116
2172
  self.loc_idx = []
@@ -2172,9 +2228,13 @@ class MeasureAnnotator(SignalAnnotator):
2172
2228
  """
2173
2229
  self.current_frame = self.frame_slider.value()
2174
2230
  self.reload_frame()
2175
- if 'ID' in self.df_tracks.columns:
2231
+ if 'TRACK_ID' in list(self.df_tracks.columns):
2232
+ pass
2233
+ elif 'ID' in list(self.df_tracks.columns):
2234
+ print('ID in cols... change class of interest... ')
2176
2235
  self.track_of_interest = self.df_tracks[self.df_tracks['FRAME'] == self.current_frame]['ID'].min()
2177
2236
  self.modify()
2237
+
2178
2238
  self.draw_frame(self.current_frame)
2179
2239
  self.fcanvas.canvas.draw()
2180
2240
  self.plot_signals()
@@ -2302,7 +2362,7 @@ class MeasureAnnotator(SignalAnnotator):
2302
2362
  try:
2303
2363
  self.selection.pop(0)
2304
2364
  except Exception as e:
2305
- print(e)
2365
+ print('Cancel selection: ',e)
2306
2366
 
2307
2367
  try:
2308
2368
  for k, (t, idx) in enumerate(zip(self.loc_t, self.loc_idx)):
@@ -65,9 +65,9 @@ class QueryWidget(QWidget):
65
65
 
66
66
  def filter_table(self):
67
67
  try:
68
- query_text = self.query_le.text().replace('class', '`class`')
68
+ query_text = self.query_le.text() #.replace('class', '`class`')
69
69
  tab = self.parent_window.data.query(query_text)
70
- self.subtable = TableUI(tab, query_text, plot_mode="scatter")
70
+ self.subtable = TableUI(tab, query_text, plot_mode="static")
71
71
  self.subtable.show()
72
72
  self.close()
73
73
  except Exception as e:
@@ -268,7 +268,6 @@ class RenameColWidget(QWidget):
268
268
  old_name = self.column
269
269
  new_name = self.new_col_name.text()
270
270
  self.parent_window.data = self.parent_window.data.rename(columns={old_name: new_name})
271
- print(self.parent.data.columns)
272
271
 
273
272
  self.parent_window.model = PandasModel(self.parent_window.data)
274
273
  self.parent_window.table_view.setModel(self.parent_window.model)
@@ -583,18 +582,20 @@ class TableUI(QMainWindow, Styles):
583
582
  layout.addWidget(QLabel('Representations: '))
584
583
  self.hist_check = QCheckBox('histogram')
585
584
  self.kde_check = QCheckBox('KDE plot')
586
- self.count_check = QCheckBox('Countplot')
585
+ self.count_check = QCheckBox('countplot')
587
586
  self.ecdf_check = QCheckBox('ECDF plot')
587
+ self.scat_check = QCheckBox('scatter plot')
588
588
  self.swarm_check = QCheckBox('swarm')
589
589
  self.violin_check = QCheckBox('violin')
590
590
  self.strip_check = QCheckBox('strip')
591
- self.box_check = QCheckBox('Boxplot')
592
- self.boxenplot_check = QCheckBox('Boxenplot')
591
+ self.box_check = QCheckBox('boxplot')
592
+ self.boxenplot_check = QCheckBox('boxenplot')
593
593
 
594
594
  layout.addWidget(self.hist_check)
595
595
  layout.addWidget(self.kde_check)
596
596
  layout.addWidget(self.count_check)
597
597
  layout.addWidget(self.ecdf_check)
598
+ layout.addWidget(self.scat_check)
598
599
  layout.addWidget(self.swarm_check)
599
600
  layout.addWidget(self.violin_check)
600
601
  layout.addWidget(self.strip_check)
@@ -707,6 +708,14 @@ class TableUI(QMainWindow, Styles):
707
708
  if self.ecdf_check.isChecked():
708
709
  sns.ecdfplot(data=self.data, x=self.x, hue=hue_variable, legend=legend, ax=self.ax, palette=colors)
709
710
  legend = False
711
+
712
+ if self.scat_check.isChecked():
713
+ if self.x_option:
714
+ sns.scatterplot(data=self.data, x=self.x,y=self.y, hue=hue_variable,legend=legend, ax=self.ax, palette=colors)
715
+ legend = False
716
+ else:
717
+ print('please provide a -x variable...')
718
+ pass
710
719
 
711
720
  if self.swarm_check.isChecked():
712
721
  if self.x_option:
@@ -26,6 +26,7 @@ from stardist import fill_label_holes
26
26
  from celldetective.utils import interpolate_nan
27
27
  from scipy.interpolate import griddata
28
28
 
29
+
29
30
  def get_experiment_wells(experiment):
30
31
 
31
32
  """
@@ -801,6 +802,10 @@ def auto_load_number_of_frames(stack_path):
801
802
  """
802
803
 
803
804
  # Try to estimate automatically # frames
805
+
806
+ if stack_path is None:
807
+ return None
808
+
804
809
  stack_path = stack_path.replace('\\','/')
805
810
 
806
811
  with TiffFile(stack_path) as tif:
@@ -1370,6 +1375,14 @@ def control_segmentation_napari(position, prefix='Aligned', population="target",
1370
1375
  exp_name = os.path.split(expfolder)[-1]
1371
1376
  print(exp_name)
1372
1377
 
1378
+ wells = get_experiment_wells(expfolder)
1379
+ well_idx = list(wells).index(str(parent1)+os.sep)
1380
+ ab = get_experiment_antibodies(expfolder)[well_idx]
1381
+ conc = get_experiment_concentrations(expfolder)[well_idx]
1382
+ ct = get_experiment_cell_types(expfolder)[well_idx]
1383
+ pa = get_experiment_pharmaceutical_agents(expfolder)[well_idx]
1384
+
1385
+
1373
1386
  spatial_calibration = float(ConfigSectionMap(config,"MovieSettings")["pxtoum"])
1374
1387
  channel_names, channel_indices = extract_experiment_channels(config)
1375
1388
 
@@ -1424,7 +1437,7 @@ def control_segmentation_napari(position, prefix='Aligned', population="target",
1424
1437
  multichannel = np.array(multichannel)
1425
1438
  save_tiff_imagej_compatible(annotation_folder + f"{exp_name}_{position.split(os.sep)[-2]}_{str(t).zfill(4)}_roi_{xmin}_{xmax}_{ymin}_{ymax}_labelled.tif", labels_layer[xmin:xmax,ymin:ymax].astype(np.int16), axes='YX')
1426
1439
  save_tiff_imagej_compatible(annotation_folder + f"{exp_name}_{position.split(os.sep)[-2]}_{str(t).zfill(4)}_roi_{xmin}_{xmax}_{ymin}_{ymax}.tif", multichannel, axes='CYX')
1427
- info = {"spatial_calibration": spatial_calibration, "channels": list(channel_names)}
1440
+ info = {"spatial_calibration": spatial_calibration, "channels": list(channel_names), 'cell_type': ct, 'antibody': ab, 'concentration': conc, 'pharmaceutical_agent': pa}
1428
1441
  info_name = annotation_folder + f"{exp_name}_{position.split(os.sep)[-2]}_{str(t).zfill(4)}_roi_{xmin}_{xmax}_{ymin}_{ymax}.json"
1429
1442
  with open(info_name, 'w') as f:
1430
1443
  json.dump(info, f, indent=4)
@@ -1441,7 +1454,7 @@ def control_segmentation_napari(position, prefix='Aligned', population="target",
1441
1454
  multichannel = np.array(multichannel)
1442
1455
  save_tiff_imagej_compatible(annotation_folder + f"{exp_name}_{position.split(os.sep)[-2]}_{str(t).zfill(4)}_labelled.tif", labels_layer, axes='YX')
1443
1456
  save_tiff_imagej_compatible(annotation_folder + f"{exp_name}_{position.split(os.sep)[-2]}_{str(t).zfill(4)}.tif", multichannel, axes='CYX')
1444
- info = {"spatial_calibration": spatial_calibration, "channels": list(channel_names)}
1457
+ info = {"spatial_calibration": spatial_calibration, "channels": list(channel_names), 'cell_type': ct, 'antibody': ab, 'concentration': conc, 'pharmaceutical_agent': pa}
1445
1458
  info_name = annotation_folder + f"{exp_name}_{position.split(os.sep)[-2]}_{str(t).zfill(4)}.json"
1446
1459
  with open(info_name, 'w') as f:
1447
1460
  json.dump(info, f, indent=4)
@@ -1481,6 +1494,59 @@ def control_segmentation_napari(position, prefix='Aligned', population="target",
1481
1494
  del labels
1482
1495
  gc.collect()
1483
1496
 
1497
+ def correct_annotation(filename):
1498
+
1499
+ """
1500
+ New function to reannotate an annotation image in post, using napari and save update inplace.
1501
+ """
1502
+
1503
+ def export_labels():
1504
+ labels_layer = viewer.layers['segmentation'].data
1505
+ for t,im in enumerate(tqdm(labels_layer)):
1506
+
1507
+ try:
1508
+ im = auto_correct_masks(im)
1509
+ except Exception as e:
1510
+ print(e)
1511
+
1512
+ save_tiff_imagej_compatible(existing_lbl, im.astype(np.int16), axes='YX')
1513
+ print("The labels have been successfully rewritten.")
1514
+
1515
+ @magicgui(call_button='Save the modified labels')
1516
+ def save_widget():
1517
+ return export_labels()
1518
+
1519
+ img = imread(filename.replace('\\','/'))
1520
+ if img.ndim==3:
1521
+ img = np.moveaxis(img, 0, -1)
1522
+ elif img.ndim==2:
1523
+ img = img[:,:,np.newaxis]
1524
+
1525
+ existing_lbl = filename.replace('.tif','_labelled.tif')
1526
+ if os.path.exists(existing_lbl):
1527
+ labels = imread(existing_lbl)[np.newaxis,:,:].astype(int)
1528
+ else:
1529
+ labels = np.zeros_like(img[:,:,0]).astype(int)[np.newaxis,:,:]
1530
+
1531
+ stack = img[np.newaxis,:,:,:]
1532
+
1533
+ viewer = napari.Viewer()
1534
+ viewer.add_image(stack,channel_axis=-1,colormap=["gray"]*stack.shape[-1])
1535
+ viewer.add_labels(labels, name='segmentation',opacity=0.4)
1536
+ viewer.window.add_dock_widget(save_widget, area='right')
1537
+ viewer.show(block=True)
1538
+
1539
+ # temporary fix for slight napari memory leak
1540
+ for i in range(100):
1541
+ try:
1542
+ viewer.layers.pop()
1543
+ except:
1544
+ pass
1545
+ del viewer
1546
+ del stack
1547
+ del labels
1548
+ gc.collect()
1549
+
1484
1550
 
1485
1551
  def _view_on_napari(tracks=None, stack=None, labels=None):
1486
1552
 
@@ -1628,7 +1694,7 @@ def locate_segmentation_model(name):
1628
1694
  """
1629
1695
 
1630
1696
  main_dir = os.sep.join([os.path.split(os.path.dirname(os.path.realpath(__file__)))[0],"celldetective"])
1631
- modelpath = os.sep.join([main_dir, "models", "segmentation*", os.sep])
1697
+ modelpath = os.sep.join([main_dir, "models", "segmentation*"]) + os.sep
1632
1698
  print(f'Looking for {name} in {modelpath}')
1633
1699
  models = glob(modelpath+f'*{os.sep}')
1634
1700