celldetective 1.1.1.post3__tar.gz → 1.2.0__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 (96) hide show
  1. {celldetective-1.1.1.post3 → celldetective-1.2.0}/PKG-INFO +1 -1
  2. celldetective-1.2.0/celldetective/__init__.py +3 -0
  3. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/__main__.py +17 -0
  4. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/extra_properties.py +62 -34
  5. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/__init__.py +1 -0
  6. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/analyze_block.py +2 -1
  7. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/classifier_widget.py +18 -10
  8. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/control_panel.py +57 -6
  9. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/layouts.py +14 -11
  10. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/neighborhood_options.py +21 -13
  11. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/plot_signals_ui.py +39 -11
  12. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/process_block.py +413 -95
  13. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/retrain_segmentation_model_options.py +17 -4
  14. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/retrain_signal_model_options.py +106 -6
  15. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/signal_annotator.py +110 -30
  16. celldetective-1.2.0/celldetective/gui/signal_annotator2.py +2708 -0
  17. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/signal_annotator_options.py +3 -1
  18. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/survival_ui.py +15 -6
  19. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/tableUI.py +248 -43
  20. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/io.py +598 -416
  21. celldetective-1.2.0/celldetective/measure.py +988 -0
  22. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/neighborhood.py +482 -340
  23. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/preprocessing.py +81 -61
  24. celldetective-1.2.0/celldetective/relative_measurements.py +648 -0
  25. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/scripts/analyze_signals.py +1 -1
  26. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/scripts/measure_cells.py +28 -8
  27. celldetective-1.2.0/celldetective/scripts/measure_relative.py +103 -0
  28. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/scripts/segment_cells.py +5 -5
  29. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/scripts/track_cells.py +4 -1
  30. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/scripts/train_segmentation_model.py +23 -18
  31. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/scripts/train_signal_model.py +33 -0
  32. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/segmentation.py +67 -29
  33. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/signals.py +402 -8
  34. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/tracking.py +8 -2
  35. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/utils.py +144 -12
  36. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective.egg-info/PKG-INFO +1 -1
  37. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective.egg-info/SOURCES.txt +4 -0
  38. {celldetective-1.1.1.post3 → celldetective-1.2.0}/setup.py +1 -1
  39. celldetective-1.2.0/tests/__init__.py +0 -0
  40. {celldetective-1.1.1.post3 → celldetective-1.2.0}/tests/test_segmentation.py +1 -1
  41. celldetective-1.1.1.post3/celldetective/__init__.py +0 -2
  42. celldetective-1.1.1.post3/celldetective/measure.py +0 -1038
  43. {celldetective-1.1.1.post3 → celldetective-1.2.0}/LICENSE +0 -0
  44. {celldetective-1.1.1.post3 → celldetective-1.2.0}/README.md +0 -0
  45. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/datasets/segmentation_annotations/blank +0 -0
  46. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/datasets/signal_annotations/blank +0 -0
  47. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/events.py +0 -0
  48. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/filters.py +0 -0
  49. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/about.py +0 -0
  50. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/btrack_options.py +0 -0
  51. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/configure_new_exp.py +0 -0
  52. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/gui_utils.py +0 -0
  53. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/json_readers.py +0 -0
  54. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/measurement_options.py +0 -0
  55. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/plot_measurements.py +0 -0
  56. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/seg_model_loader.py +0 -0
  57. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/styles.py +0 -0
  58. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/thresholds_gui.py +0 -0
  59. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/gui/viewers.py +0 -0
  60. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/icons/logo-large.png +0 -0
  61. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/icons/logo.png +0 -0
  62. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/icons/signals_icon.png +0 -0
  63. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/icons/splash-test.png +0 -0
  64. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/icons/splash.png +0 -0
  65. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/icons/splash0.png +0 -0
  66. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/icons/survival2.png +0 -0
  67. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/icons/vignette_signals2.png +0 -0
  68. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/icons/vignette_signals2.svg +0 -0
  69. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/links/zenodo.json +0 -0
  70. {celldetective-1.1.1.post3/celldetective/models/segmentation_effectors → celldetective-1.2.0/celldetective/models/pair_signal_detection}/blank +0 -0
  71. {celldetective-1.1.1.post3/celldetective/models/segmentation_generic → celldetective-1.2.0/celldetective/models/segmentation_effectors}/blank +0 -0
  72. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/models/segmentation_effectors/primNK_cfse/config_input.json +0 -0
  73. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/models/segmentation_effectors/primNK_cfse/cp-cfse-transfer +0 -0
  74. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/models/segmentation_effectors/primNK_cfse/training_instructions.json +0 -0
  75. {celldetective-1.1.1.post3/celldetective/models/segmentation_targets → celldetective-1.2.0/celldetective/models/segmentation_generic}/blank +0 -0
  76. {celldetective-1.1.1.post3/celldetective/models/signal_detection → celldetective-1.2.0/celldetective/models/segmentation_targets}/blank +0 -0
  77. /celldetective-1.1.1.post3/tests/__init__.py → /celldetective-1.2.0/celldetective/models/signal_detection/blank +0 -0
  78. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/models/tracking_configs/mcf7.json +0 -0
  79. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/models/tracking_configs/ricm.json +0 -0
  80. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/models/tracking_configs/ricm2.json +0 -0
  81. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective/scripts/segment_cells_thresholds.py +0 -0
  82. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective.egg-info/dependency_links.txt +0 -0
  83. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective.egg-info/entry_points.txt +0 -0
  84. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective.egg-info/not-zip-safe +0 -0
  85. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective.egg-info/requires.txt +0 -0
  86. {celldetective-1.1.1.post3 → celldetective-1.2.0}/celldetective.egg-info/top_level.txt +0 -0
  87. {celldetective-1.1.1.post3 → celldetective-1.2.0}/setup.cfg +0 -0
  88. {celldetective-1.1.1.post3 → celldetective-1.2.0}/tests/test_events.py +0 -0
  89. {celldetective-1.1.1.post3 → celldetective-1.2.0}/tests/test_filters.py +0 -0
  90. {celldetective-1.1.1.post3 → celldetective-1.2.0}/tests/test_io.py +0 -0
  91. {celldetective-1.1.1.post3 → celldetective-1.2.0}/tests/test_measure.py +0 -0
  92. {celldetective-1.1.1.post3 → celldetective-1.2.0}/tests/test_neighborhood.py +0 -0
  93. {celldetective-1.1.1.post3 → celldetective-1.2.0}/tests/test_preprocessing.py +0 -0
  94. {celldetective-1.1.1.post3 → celldetective-1.2.0}/tests/test_signals.py +0 -0
  95. {celldetective-1.1.1.post3 → celldetective-1.2.0}/tests/test_tracking.py +0 -0
  96. {celldetective-1.1.1.post3 → celldetective-1.2.0}/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.2.0
4
4
  Summary: description
5
5
  Home-page: http://github.com/remyeltorro/celldetective
6
6
  Author: Rémy Torro
@@ -0,0 +1,3 @@
1
+ #from .tracking import track, clean_trajectories
2
+ #from .measure import contour_of_instance_segmentation
3
+
@@ -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
@@ -1,15 +1,31 @@
1
1
  """
2
- Add extra properties to directly to regionprops
3
- Functions must take regionmask as first argument and optionally intensity_image as second argument
4
- If intensity is in function name, it will be replaced by the name of the channel. These measurements are applied automatically to all channels
2
+ Copyright © 2022 Laboratoire Adhesion et Inflammation, Authored by Remy Torro,
3
+ Ksenija Dervanova.
4
+
5
+ Add extra properties to measure using regionprops (measure_features).
6
+ The functions must take "regionmask" as the first argument, corresponding
7
+ to the binary mask of each cell of interest generated by regionprops
8
+ and an optional "intensity_image" second argument, i.e. the associated
9
+ multichannel image crop. The measurement will be named after the function
10
+ name. If a function function() has several outputs (e.g. due to a multichannel image),
11
+ they will be labelled with an index as function-0, function-1... A routine
12
+ automatically replacements keywords "intensity-0", "intensity-1" and so on,
13
+ with the actual name of the intensity channel (e.g. "intensity-0" ->
14
+ "brightfield_channel"). Due to this indexing behavior, avoid using digits
15
+ in the function names and prefer text instead: "intensity-99()" -->
16
+ "intensity-ninety-nine()".
17
+
18
+ New functions appear automatically in the ConfigMeasurements widget of the
19
+ GUI.
5
20
 
6
21
  """
22
+
7
23
  import warnings
8
24
 
9
25
  import numpy as np
10
26
  from scipy.ndimage import distance_transform_edt, center_of_mass
11
27
  from scipy.spatial.distance import euclidean
12
-
28
+ from celldetective.utils import interpolate_nan, contour_of_instance_segmentation
13
29
 
14
30
  # Percentiles
15
31
 
@@ -44,6 +60,9 @@ def intensity_nanmean(regionmask, intensity_image):
44
60
  return np.nanmean(intensity_image[regionmask])
45
61
 
46
62
  def intensity_centre_of_mass_displacement(regionmask, intensity_image):
63
+
64
+ intensity_image = interpolate_nan(intensity_image.copy())
65
+
47
66
  y, x = np.mgrid[:regionmask.shape[0], :regionmask.shape[1]]
48
67
  xtemp = x.copy()
49
68
  ytemp = y.copy()
@@ -51,60 +70,69 @@ def intensity_centre_of_mass_displacement(regionmask, intensity_image):
51
70
  centroid_x = intensity_weighted_center[1]
52
71
  centroid_y = intensity_weighted_center[0]
53
72
 
73
+ #centroid_x = np.sum(xtemp * intensity_image) / np.sum(intensity_image)
54
74
  geometric_centroid_x = np.sum(xtemp * regionmask) / np.sum(regionmask)
55
75
  geometric_centroid_y = np.sum(ytemp * regionmask) / np.sum(regionmask)
56
- distance = euclidean(np.array((geometric_centroid_y, geometric_centroid_x)), np.array((centroid_y, centroid_x)))
76
+ try:
77
+ distance = euclidean(np.array((geometric_centroid_y, geometric_centroid_x)), np.array((centroid_y, centroid_x)))
78
+ except:
79
+ distance = np.nan
80
+
57
81
  delta_x = geometric_centroid_x - centroid_x
58
82
  delta_y = geometric_centroid_y - centroid_y
59
83
  direction_arctan = np.arctan2(delta_y, delta_x) * 180 / np.pi
60
84
  if direction_arctan < 0:
61
85
  direction_arctan += 360
62
- return distance, direction_arctan
86
+
87
+ return distance, direction_arctan, centroid_x - geometric_centroid_x, centroid_y - geometric_centroid_y
63
88
 
64
89
  def intensity_radial_gradient(regionmask, intensity_image):
65
- warnings.filterwarnings('ignore', message="Polyfit may be poorly conditioned")
66
- cell_mask = regionmask.copy()
67
- intensity = intensity_image.copy()
68
- y = intensity[cell_mask].flatten()
69
- x = distance_transform_edt(cell_mask)
70
- x = x[cell_mask].flatten()
71
- params = np.polyfit(x, y, 1)
72
- line = np.poly1d(params)
73
90
 
74
- return line.coefficients[0], line.coefficients[1]
91
+ try:
92
+ warnings.filterwarnings('ignore', message="Polyfit may be poorly conditioned")
93
+ cell_mask = regionmask.copy()
94
+ intensity = intensity_image.copy()
95
+ y = intensity[cell_mask].flatten()
96
+ x = distance_transform_edt(cell_mask)
97
+ x = x[cell_mask].flatten()
98
+ params = np.polyfit(x, y, 1)
99
+ line = np.poly1d(params)
100
+
101
+ return line.coefficients[0], line.coefficients[1]
102
+ except Exception as e:
103
+ print(e)
104
+ return np.nan, np.nan
75
105
 
76
106
 
77
107
  def intensity_centre_of_mass_displacement_edge(regionmask, intensity_image):
78
- edt = distance_transform_edt(regionmask)
79
- min_distance = 0
80
- max_distance = 0.1*edt.max()
81
- thresholded = (edt <= max_distance) * (edt > min_distance)
82
- edge_mask = np.copy(regionmask)
83
- edge_mask[np.where(thresholded == 0)] = 0
84
- y, x = np.mgrid[:edge_mask.shape[0], :edge_mask.shape[1]]
85
- xtemp = x.copy()
86
- ytemp = y.copy()
87
- intensity_edge = intensity_image.copy()
88
- intensity_edge[np.where(edge_mask == 0)] = 0.
89
- sum_intensity_edge = np.sum(intensity_edge)
90
- sum_regionmask = np.sum(regionmask)
108
+
109
+ intensity_image = interpolate_nan(intensity_image.copy())
110
+ edge_mask = contour_of_instance_segmentation(regionmask, 3)
91
111
 
92
- if sum_intensity_edge != 0 and sum_regionmask != 0:
93
- y, x = np.mgrid[:regionmask.shape[0], :regionmask.shape[1]]
112
+ if np.sum(edge_mask)>0:
113
+
114
+ y, x = np.mgrid[:edge_mask.shape[0], :edge_mask.shape[1]]
94
115
  xtemp = x.copy()
95
116
  ytemp = y.copy()
96
- intensity_weighted_center = center_of_mass(intensity_image, regionmask)
117
+ intensity_weighted_center = center_of_mass(intensity_image, edge_mask)
97
118
  centroid_x = intensity_weighted_center[1]
98
119
  centroid_y = intensity_weighted_center[0]
99
120
 
121
+ #centroid_x = np.sum(xtemp * intensity_image) / np.sum(intensity_image)
100
122
  geometric_centroid_x = np.sum(xtemp * regionmask) / np.sum(regionmask)
101
123
  geometric_centroid_y = np.sum(ytemp * regionmask) / np.sum(regionmask)
102
- distance = euclidean(np.array((geometric_centroid_y, geometric_centroid_x)), np.array((centroid_y, centroid_x)))
124
+
125
+ try:
126
+ distance = euclidean(np.array((geometric_centroid_y, geometric_centroid_x)), np.array((centroid_y, centroid_x)))
127
+ except:
128
+ distance = np.nan
129
+
103
130
  delta_x = geometric_centroid_x - centroid_x
104
131
  delta_y = geometric_centroid_y - centroid_y
105
132
  direction_arctan = np.arctan2(delta_y, delta_x) * 180 / np.pi
106
133
  if direction_arctan < 0:
107
134
  direction_arctan += 360
108
- return distance, direction_arctan
135
+
136
+ return distance, direction_arctan, centroid_x - geometric_centroid_x, centroid_y - geometric_centroid_y
109
137
  else:
110
- return np.nan, np.nan
138
+ return np.nan, np.nan, np.nan, np.nan
@@ -9,6 +9,7 @@ from .survival_ui import ConfigSurvival
9
9
  from .plot_signals_ui import ConfigSignalPlot
10
10
  from .signal_annotator_options import ConfigSignalAnnotator
11
11
  from .signal_annotator import SignalAnnotator
12
+ from .signal_annotator2 import SignalAnnotator2
12
13
  from .retrain_signal_model_options import ConfigSignalModelTraining
13
14
  from .retrain_segmentation_model_options import ConfigSegmentationModelTraining
14
15
  from .thresholds_gui import ThresholdConfigWizard
@@ -1,4 +1,5 @@
1
- from PyQt5.QtWidgets import QFrame, QGridLayout, QComboBox, QSizePolicy, QSpacerItem, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, QMessageBox
1
+ from PyQt5.QtWidgets import QFrame, QGridLayout, QComboBox, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, \
2
+ QMessageBox, QSpacerItem, QSizePolicy
2
3
  from PyQt5.QtCore import Qt, QSize
3
4
  from PyQt5.QtGui import QIcon
4
5
 
@@ -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)
@@ -368,13 +375,14 @@ class ClassifierWidget(QWidget, Styles):
368
375
  frames = track['FRAME'].to_numpy()
369
376
  t_first = track['t_firstdetection'].to_numpy()[0]
370
377
  median_status = np.nanmedian(status_values[frames>=t_first])
371
- c = ceil(median_status)
372
- if c==0:
373
- self.df.loc[indices, self.class_name_user] = 1
374
- self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
375
- elif c==1:
376
- self.df.loc[indices, self.class_name_user] = 2
377
- self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
378
+ if median_status==median_status:
379
+ c = ceil(median_status)
380
+ if c==0:
381
+ self.df.loc[indices, self.class_name_user] = 1
382
+ self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
383
+ elif c==1:
384
+ self.df.loc[indices, self.class_name_user] = 2
385
+ self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
378
386
  if self.irreversible_event_btn.isChecked():
379
387
  self.estimate_time()
380
388
  else:
@@ -3,7 +3,7 @@ from PyQt5.QtWidgets import QMainWindow, QComboBox, QPushButton, QHBoxLayout, QL
3
3
  from PyQt5.QtCore import Qt, QSize
4
4
  from PyQt5.QtGui import QIcon
5
5
  from celldetective.gui.gui_utils import center_window, QHSeperationLine
6
- from celldetective.utils import _extract_labels_from_config, ConfigSectionMap, extract_experiment_channels
6
+ from celldetective.utils import _extract_labels_from_config, ConfigSectionMap, extract_experiment_channels, extract_identity_col
7
7
  from celldetective.gui import ConfigEditor, ProcessPanel, PreprocessingPanel, AnalysisPanel, NeighPanel
8
8
  from natsort import natsorted
9
9
  from glob import glob
@@ -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
 
@@ -413,26 +414,46 @@ class ControlPanel(QMainWindow, Styles):
413
414
  self.pos = self.position_list.currentText()
414
415
  panels = [self.ProcessEffectors, self.ProcessTargets]
415
416
  if self.position_list.currentText()=="*":
417
+
416
418
  for p in panels:
417
419
  p.check_seg_btn.setEnabled(False)
418
420
  p.check_tracking_result_btn.setEnabled(False)
421
+
419
422
  self.ProcessTargets.view_tab_btn.setEnabled(True)
420
423
  self.ProcessEffectors.view_tab_btn.setEnabled(True)
424
+ self.NeighPanel.view_tab_btn.setEnabled(True)
425
+ self.ProcessEffectors.signal_analysis_action.setEnabled(True)
426
+ self.ProcessTargets.signal_analysis_action.setEnabled(True)
427
+
421
428
  self.ProcessTargets.check_seg_btn.setEnabled(False)
422
429
  self.ProcessEffectors.check_seg_btn.setEnabled(False)
430
+
423
431
  self.ProcessTargets.check_tracking_result_btn.setEnabled(False)
424
432
  self.ProcessEffectors.check_tracking_result_btn.setEnabled(False)
433
+
425
434
  self.ProcessEffectors.check_measurements_btn.setEnabled(False)
426
435
  self.ProcessTargets.check_measurements_btn.setEnabled(False)
427
436
  #self.ProcessTargets.signal_analysis_action.setEnabled(False)
428
437
  #self.ProcessEffectors.signal_analysis_action.setEnabled(False)
438
+
429
439
  self.ProcessTargets.check_signals_btn.setEnabled(False)
430
440
  self.ProcessEffectors.check_signals_btn.setEnabled(False)
441
+ self.NeighPanel.check_signals_btn.setEnabled(False)
442
+ self.ProcessTargets.delete_tracks_btn.hide()
443
+ self.ProcessEffectors.delete_tracks_btn.hide()
444
+
431
445
  self.view_stack_btn.setEnabled(False)
432
446
  elif self.well_list.currentText()=='*':
433
447
  self.ProcessTargets.view_tab_btn.setEnabled(True)
434
448
  self.ProcessEffectors.view_tab_btn.setEnabled(True)
435
- self.view_stack_btn.setEnabled(False)
449
+ self.NeighPanel.view_tab_btn.setEnabled(True)
450
+ self.view_stack_btn.setEnabled(False)
451
+ self.ProcessEffectors.signal_analysis_action.setEnabled(True)
452
+ self.ProcessTargets.signal_analysis_action.setEnabled(True)
453
+
454
+ self.delete_tracks_btn.hide()
455
+ self.ProcessTargets.delete_tracks_btn.hide()
456
+ self.ProcessEffectors.delete_tracks_btn.hide()
436
457
  else:
437
458
  if not self.well_list.currentText()=="*":
438
459
  self.locate_selected_position()
@@ -452,26 +473,56 @@ class ControlPanel(QMainWindow, Styles):
452
473
  self.ProcessEffectors.check_tracking_result_btn.setEnabled(False)
453
474
 
454
475
  if os.path.exists(os.sep.join([self.pos,'output','tables','trajectories_effectors.csv'])):
476
+ df = pd.read_csv(os.sep.join([self.pos,'output','tables','trajectories_effectors.csv']), nrows=1)
477
+ id_col = extract_identity_col(df)
455
478
  self.ProcessEffectors.check_measurements_btn.setEnabled(True)
456
- self.ProcessEffectors.check_signals_btn.setEnabled(True)
479
+ if id_col=='TRACK_ID':
480
+ self.ProcessEffectors.check_signals_btn.setEnabled(True)
481
+ self.ProcessEffectors.delete_tracks_btn.show()
482
+ self.ProcessEffectors.signal_analysis_action.setEnabled(True)
483
+ else:
484
+ self.ProcessEffectors.signal_analysis_action.setEnabled(False)
485
+
457
486
  #self.ProcessEffectors.signal_analysis_action.setEnabled(True)
458
487
  self.ProcessEffectors.view_tab_btn.setEnabled(True)
459
-
488
+ self.ProcessEffectors.classify_btn.setEnabled(True)
460
489
  else:
461
490
  self.ProcessEffectors.check_measurements_btn.setEnabled(False)
462
491
  self.ProcessEffectors.check_signals_btn.setEnabled(False)
463
492
  #self.ProcessEffectors.signal_analysis_action.setEnabled(False)
464
493
  self.ProcessEffectors.view_tab_btn.setEnabled(False)
494
+ self.ProcessEffectors.classify_btn.setEnabled(False)
495
+ self.ProcessEffectors.delete_tracks_btn.hide()
496
+ self.ProcessEffectors.signal_analysis_action.setEnabled(False)
465
497
 
466
498
  if os.path.exists(os.sep.join([self.pos,'output','tables','trajectories_targets.csv'])):
499
+ df = pd.read_csv(os.sep.join([self.pos,'output','tables','trajectories_targets.csv']), nrows=1)
500
+ id_col = extract_identity_col(df)
467
501
  self.ProcessTargets.check_measurements_btn.setEnabled(True)
468
- self.ProcessTargets.check_signals_btn.setEnabled(True)
502
+ if id_col=='TRACK_ID':
503
+ self.ProcessTargets.check_signals_btn.setEnabled(True)
504
+ self.ProcessTargets.signal_analysis_action.setEnabled(True)
505
+ self.ProcessTargets.delete_tracks_btn.show()
506
+ else:
507
+ self.ProcessTargets.signal_analysis_action.setEnabled(False)
469
508
  #self.ProcessTargets.signal_analysis_action.setEnabled(True)
470
509
  self.ProcessTargets.view_tab_btn.setEnabled(True)
471
-
510
+ self.ProcessTargets.classify_btn.setEnabled(True)
472
511
  else:
473
512
  self.ProcessTargets.check_measurements_btn.setEnabled(False)
474
513
  self.ProcessTargets.check_signals_btn.setEnabled(False)
475
514
  #self.ProcessTargets.signal_analysis_action.setEnabled(False)
476
515
  self.ProcessTargets.view_tab_btn.setEnabled(False)
516
+ self.ProcessTargets.classify_btn.setEnabled(False)
517
+ self.ProcessTargets.signal_analysis_action.setEnabled(False)
518
+ self.ProcessTargets.delete_tracks_btn.hide()
519
+ self.ProcessTargets.signal_analysis_action.setEnabled(False)
520
+
521
+ if os.path.exists(os.sep.join([self.pos,'output','tables','trajectories_pairs.csv'])):
522
+ self.NeighPanel.view_tab_btn.setEnabled(True)
523
+ self.NeighPanel.check_signals_btn.setEnabled(True)
524
+ else:
525
+ self.NeighPanel.view_tab_btn.setEnabled(False)
526
+ self.NeighPanel.check_signals_btn.setEnabled(False)
527
+
477
528
 
@@ -73,7 +73,7 @@ class ChannelNormGenerator(QVBoxLayout, Styles):
73
73
  for tab in tables:
74
74
  cols = pd.read_csv(tab, nrows=1).columns.tolist()
75
75
  all_measurements.extend(cols)
76
- all_measurements = np.unique(all_measurements)
76
+ all_measurements = np.unique(all_measurements)
77
77
 
78
78
  if self.mode=='signals':
79
79
  generic_measurements = ['brightfield_channel', 'live_nuclei_channel', 'dead_nuclei_channel',
@@ -245,13 +245,14 @@ class ChannelNormGenerator(QVBoxLayout, Styles):
245
245
 
246
246
  def check_valid_channels(self):
247
247
 
248
- if np.all([cb.currentText()=='--' for cb in self.channel_cbs]):
249
- self.parent_window.submit_btn.setEnabled(False)
248
+ if hasattr(self.parent_window, "submit_btn"):
249
+ if np.all([cb.currentText()=='--' for cb in self.channel_cbs]):
250
+ self.parent_window.submit_btn.setEnabled(False)
250
251
 
251
252
  if hasattr(self.parent_window, "spatial_calib_le"):
252
253
  if self.parent_window.spatial_calib_le.text()!='--':
253
254
  self.parent_window.submit_btn.setEnabled(True)
254
- else:
255
+ elif hasattr(self.parent_window, "submit_btn"):
255
256
  self.parent_window.submit_btn.setEnabled(True)
256
257
 
257
258
 
@@ -990,10 +991,12 @@ class BackgroundModelFreeCorrectionLayout(QGridLayout, Styles):
990
991
  )
991
992
  bg = bg[0]
992
993
  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()
994
+ print(bg)
995
+ if len(bg)>0:
996
+
997
+ self.viewer = StackVisualizer(
998
+ stack=[bg],
999
+ window_title='Reconstructed background',
1000
+ frame_slider = False,
1001
+ )
1002
+ self.viewer.show()
@@ -90,6 +90,8 @@ class ConfigNeighborhoods(QWidget, Styles):
90
90
 
91
91
  self.clear_previous_btn = QCheckBox('clear previous neighborhoods')
92
92
  self.clear_previous_btn.setToolTip('Clear all previous neighborhood measurements.')
93
+ self.clear_previous_btn.setIcon(icon(MDI6.broom, color='black'))
94
+
93
95
  main_layout.addWidget(self.clear_previous_btn, alignment=Qt.AlignRight)
94
96
 
95
97
  main_layout.addWidget(QLabel(''))
@@ -305,11 +307,13 @@ class ConfigNeighborhoods(QWidget, Styles):
305
307
 
306
308
  self.cumulated_presence_btn = QCheckBox('cumulated presence')
307
309
  self.cumulated_presence_btn.setToolTip("Compute the cumulated presence time of each neighbor around a reference cell.")
310
+ self.cumulated_presence_btn.setIcon(icon(MDI6.timer_outline, color='black'))
311
+
308
312
  layout.addWidget(self.cumulated_presence_btn)
309
313
 
310
- self.symmetrize_btn = QCheckBox('symmetrize')
311
- self.symmetrize_btn.setToolTip("Write the neighborhood of the neighbor cells with respect to the reference cells.")
312
- layout.addWidget(self.symmetrize_btn)
314
+ # self.symmetrize_btn = QCheckBox('symmetrize')
315
+ # self.symmetrize_btn.setToolTip("Write the neighborhood of the neighbor cells with respect to the reference cells.")
316
+ # layout.addWidget(self.symmetrize_btn)
313
317
 
314
318
  self.fill_cbs_of_neighbor_population()
315
319
  self.neighbor_population_cb.currentIndexChanged.connect(self.fill_cbs_of_neighbor_population)
@@ -319,16 +323,16 @@ class ConfigNeighborhoods(QWidget, Styles):
319
323
  def fill_cbs_of_neighbor_population(self):
320
324
 
321
325
  population = self.neighbor_population_cb.currentText()
322
- class_cols, status_cols, time_cols = self.locate_population_specific_columns(population)
326
+ class_cols, status_cols, group_cols, time_cols = self.locate_population_specific_columns(population)
323
327
  self.neighbor_population_status_cb.clear()
324
- self.neighbor_population_status_cb.addItems(['--','class', 'status']+class_cols+status_cols)
328
+ self.neighbor_population_status_cb.addItems(['--','class', 'status']+class_cols+status_cols+group_cols)
325
329
 
326
330
  def fill_cbs_of_reference_population(self):
327
331
 
328
332
  population = self.reference_population_cb.currentText()
329
- class_cols, status_cols, time_cols = self.locate_population_specific_columns(population)
333
+ class_cols, status_cols, group_cols, time_cols = self.locate_population_specific_columns(population)
330
334
  self.reference_population_status_cb.clear()
331
- self.reference_population_status_cb.addItems(['--','class', 'status']+class_cols+status_cols)
335
+ self.reference_population_status_cb.addItems(['--','class', 'status']+class_cols+status_cols+group_cols)
332
336
  self.event_time_cb.addItems(['--', 't0']+time_cols)
333
337
 
334
338
  def switch_not_reference(self):
@@ -364,6 +368,7 @@ class ConfigNeighborhoods(QWidget, Styles):
364
368
 
365
369
  class_idx = np.array([s.startswith('class_') for s in self.all_columns])
366
370
  status_idx = np.array([s.startswith('status_') for s in self.all_columns])
371
+ group_idx = np.array([s.startswith('group_') for s in self.all_columns])
367
372
  time_idx = np.array([s.startswith('t_') for s in self.all_columns])
368
373
 
369
374
  if len(class_idx)>0:
@@ -379,12 +384,17 @@ class ConfigNeighborhoods(QWidget, Styles):
379
384
  else:
380
385
  status_columns = []
381
386
 
387
+ if len(group_idx)>0:
388
+ group_columns = list(self.all_columns[group_idx])
389
+ else:
390
+ group_columns = []
391
+
382
392
  if len(time_idx)>0:
383
393
  time_columns = list(self.all_columns[time_idx])
384
394
  else:
385
395
  time_columns = []
386
396
 
387
- return class_columns, status_columns, time_columns
397
+ return class_columns, status_columns, group_columns, time_columns
388
398
 
389
399
  def write_instructions(self):
390
400
 
@@ -418,7 +428,7 @@ class ConfigNeighborhoods(QWidget, Styles):
418
428
  neighborhood_options.update({'event_time_col': event_time_col})
419
429
 
420
430
  neighborhood_kwargs = {'mode': mode, 'status': status_options, 'not_status_option': [self.not_status_reference, self.not_status_neighbor],
421
- 'compute_cum_sum': self.cumulated_presence_btn.isChecked(), 'attention_weight': True, 'symmetrize': self.symmetrize_btn.isChecked(),
431
+ 'compute_cum_sum': self.cumulated_presence_btn.isChecked(), 'attention_weight': True, 'symmetrize': False,
422
432
  'include_dead_weight': True}
423
433
 
424
434
  neighborhood_options.update({'neighborhood_kwargs': neighborhood_kwargs})
@@ -487,8 +497,6 @@ class ConfigNeighborhoods(QWidget, Styles):
487
497
  neighborhood_kwargs = neigh_instructions['neighborhood_kwargs']
488
498
  if 'compute_cum_sum' in neighborhood_kwargs:
489
499
  self.cumulated_presence_btn.setChecked(neighborhood_kwargs['compute_cum_sum'])
490
- if 'symmetrize' in neighborhood_kwargs:
491
- self.symmetrize_btn.setChecked(neighborhood_kwargs['symmetrize'])
492
500
  if 'status' in neighborhood_kwargs:
493
501
  status_options = neighborhood_kwargs['status']
494
502
  status_options = ['--' if s is None else s for s in status_options]
@@ -499,6 +507,6 @@ class ConfigNeighborhoods(QWidget, Styles):
499
507
  if 'not_status_option' in neighborhood_kwargs:
500
508
  not_status_option = neighborhood_kwargs['not_status_option']
501
509
  if not_status_option[0]:
502
- self.switch_not_reference.click()
510
+ self.reference_switch_status_btn.click()
503
511
  if not_status_option[1]:
504
- self.switch_not_neigh.click()
512
+ self.neighbor_switch_status_btn.click()