celldetective 1.1.1.post4__tar.gz → 1.2.1__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 (99) hide show
  1. {celldetective-1.1.1.post4 → celldetective-1.2.1}/PKG-INFO +1 -1
  2. celldetective-1.2.1/celldetective/__init__.py +3 -0
  3. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/extra_properties.py +62 -34
  4. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/__init__.py +1 -0
  5. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/analyze_block.py +2 -1
  6. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/classifier_widget.py +15 -9
  7. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/control_panel.py +50 -6
  8. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/layouts.py +5 -4
  9. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/neighborhood_options.py +13 -9
  10. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/plot_signals_ui.py +39 -11
  11. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/process_block.py +413 -95
  12. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/retrain_segmentation_model_options.py +17 -4
  13. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/retrain_signal_model_options.py +106 -6
  14. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/signal_annotator.py +29 -9
  15. celldetective-1.2.1/celldetective/gui/signal_annotator2.py +2708 -0
  16. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/signal_annotator_options.py +3 -1
  17. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/survival_ui.py +15 -6
  18. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/tableUI.py +222 -60
  19. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/io.py +536 -420
  20. celldetective-1.2.1/celldetective/measure.py +988 -0
  21. celldetective-1.2.1/celldetective/models/segmentation_effectors/ricm-bimodal/config_input.json +130 -0
  22. celldetective-1.2.1/celldetective/models/segmentation_effectors/ricm-bimodal/ricm-bimodal +0 -0
  23. celldetective-1.2.1/celldetective/models/segmentation_effectors/ricm-bimodal/training_instructions.json +37 -0
  24. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/neighborhood.py +428 -354
  25. celldetective-1.2.1/celldetective/relative_measurements.py +648 -0
  26. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/scripts/analyze_signals.py +1 -1
  27. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/scripts/measure_cells.py +28 -8
  28. celldetective-1.2.1/celldetective/scripts/measure_relative.py +103 -0
  29. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/scripts/segment_cells.py +5 -5
  30. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/scripts/track_cells.py +4 -1
  31. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/scripts/train_segmentation_model.py +23 -18
  32. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/scripts/train_signal_model.py +33 -0
  33. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/signals.py +405 -8
  34. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/tracking.py +8 -2
  35. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/utils.py +178 -17
  36. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective.egg-info/PKG-INFO +1 -1
  37. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective.egg-info/SOURCES.txt +7 -0
  38. {celldetective-1.1.1.post4 → celldetective-1.2.1}/setup.py +1 -1
  39. celldetective-1.2.1/tests/__init__.py +0 -0
  40. celldetective-1.1.1.post4/celldetective/__init__.py +0 -2
  41. celldetective-1.1.1.post4/celldetective/measure.py +0 -1038
  42. {celldetective-1.1.1.post4 → celldetective-1.2.1}/LICENSE +0 -0
  43. {celldetective-1.1.1.post4 → celldetective-1.2.1}/README.md +0 -0
  44. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/__main__.py +0 -0
  45. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/datasets/segmentation_annotations/blank +0 -0
  46. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/datasets/signal_annotations/blank +0 -0
  47. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/events.py +0 -0
  48. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/filters.py +0 -0
  49. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/about.py +0 -0
  50. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/btrack_options.py +0 -0
  51. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/configure_new_exp.py +0 -0
  52. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/gui_utils.py +0 -0
  53. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/json_readers.py +0 -0
  54. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/measurement_options.py +0 -0
  55. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/plot_measurements.py +0 -0
  56. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/seg_model_loader.py +0 -0
  57. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/styles.py +0 -0
  58. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/thresholds_gui.py +0 -0
  59. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/gui/viewers.py +0 -0
  60. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/icons/logo-large.png +0 -0
  61. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/icons/logo.png +0 -0
  62. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/icons/signals_icon.png +0 -0
  63. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/icons/splash-test.png +0 -0
  64. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/icons/splash.png +0 -0
  65. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/icons/splash0.png +0 -0
  66. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/icons/survival2.png +0 -0
  67. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/icons/vignette_signals2.png +0 -0
  68. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/icons/vignette_signals2.svg +0 -0
  69. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/links/zenodo.json +0 -0
  70. {celldetective-1.1.1.post4/celldetective/models/segmentation_effectors → celldetective-1.2.1/celldetective/models/pair_signal_detection}/blank +0 -0
  71. {celldetective-1.1.1.post4/celldetective/models/segmentation_generic → celldetective-1.2.1/celldetective/models/segmentation_effectors}/blank +0 -0
  72. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/models/segmentation_effectors/primNK_cfse/config_input.json +0 -0
  73. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/models/segmentation_effectors/primNK_cfse/cp-cfse-transfer +0 -0
  74. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/models/segmentation_effectors/primNK_cfse/training_instructions.json +0 -0
  75. {celldetective-1.1.1.post4/celldetective/models/segmentation_targets → celldetective-1.2.1/celldetective/models/segmentation_generic}/blank +0 -0
  76. {celldetective-1.1.1.post4/celldetective/models/signal_detection → celldetective-1.2.1/celldetective/models/segmentation_targets}/blank +0 -0
  77. /celldetective-1.1.1.post4/tests/__init__.py → /celldetective-1.2.1/celldetective/models/signal_detection/blank +0 -0
  78. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/models/tracking_configs/mcf7.json +0 -0
  79. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/models/tracking_configs/ricm.json +0 -0
  80. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/models/tracking_configs/ricm2.json +0 -0
  81. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/preprocessing.py +0 -0
  82. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/scripts/segment_cells_thresholds.py +0 -0
  83. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective/segmentation.py +0 -0
  84. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective.egg-info/dependency_links.txt +0 -0
  85. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective.egg-info/entry_points.txt +0 -0
  86. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective.egg-info/not-zip-safe +0 -0
  87. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective.egg-info/requires.txt +0 -0
  88. {celldetective-1.1.1.post4 → celldetective-1.2.1}/celldetective.egg-info/top_level.txt +0 -0
  89. {celldetective-1.1.1.post4 → celldetective-1.2.1}/setup.cfg +0 -0
  90. {celldetective-1.1.1.post4 → celldetective-1.2.1}/tests/test_events.py +0 -0
  91. {celldetective-1.1.1.post4 → celldetective-1.2.1}/tests/test_filters.py +0 -0
  92. {celldetective-1.1.1.post4 → celldetective-1.2.1}/tests/test_io.py +0 -0
  93. {celldetective-1.1.1.post4 → celldetective-1.2.1}/tests/test_measure.py +0 -0
  94. {celldetective-1.1.1.post4 → celldetective-1.2.1}/tests/test_neighborhood.py +0 -0
  95. {celldetective-1.1.1.post4 → celldetective-1.2.1}/tests/test_preprocessing.py +0 -0
  96. {celldetective-1.1.1.post4 → celldetective-1.2.1}/tests/test_segmentation.py +0 -0
  97. {celldetective-1.1.1.post4 → celldetective-1.2.1}/tests/test_signals.py +0 -0
  98. {celldetective-1.1.1.post4 → celldetective-1.2.1}/tests/test_tracking.py +0 -0
  99. {celldetective-1.1.1.post4 → celldetective-1.2.1}/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.post4
3
+ Version: 1.2.1
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
+
@@ -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
 
@@ -279,9 +279,12 @@ class ClassifierWidget(QWidget, Styles):
279
279
  try:
280
280
  if cols_in_df:
281
281
  self.selection = self.df.dropna(subset=cols).query(query).index
282
+ null_selection = self.df[self.df.loc[:,cols].isna().any(axis=1)].index
283
+ self.df.loc[null_selection, self.class_name] = np.nan
284
+ self.df.loc[self.selection, self.class_name] = 0
282
285
  else:
283
- self.selection = self.df.query(query).index
284
- self.df.loc[self.selection, self.class_name] = 0
286
+ self.df.loc[:, self.class_name] = np.nan
287
+
285
288
  except Exception as e:
286
289
  print(e)
287
290
  print(self.df.columns)
@@ -375,14 +378,16 @@ class ClassifierWidget(QWidget, Styles):
375
378
  frames = track['FRAME'].to_numpy()
376
379
  t_first = track['t_firstdetection'].to_numpy()[0]
377
380
  median_status = np.nanmedian(status_values[frames>=t_first])
378
- c = ceil(median_status)
379
- if c==0:
380
- self.df.loc[indices, self.class_name_user] = 1
381
- self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
382
- elif c==1:
383
- self.df.loc[indices, self.class_name_user] = 2
384
- self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
381
+ if median_status==median_status:
382
+ c = ceil(median_status)
383
+ if c==0:
384
+ self.df.loc[indices, self.class_name_user] = 1
385
+ self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
386
+ elif c==1:
387
+ self.df.loc[indices, self.class_name_user] = 2
388
+ self.df.loc[indices, self.class_name_user.replace('class','t')] = -1
385
389
  if self.irreversible_event_btn.isChecked():
390
+ self.df.loc[self.df[self.class_name_user]!=2, self.class_name_user.replace('class', 't')] = -1
386
391
  self.estimate_time()
387
392
  else:
388
393
  self.group_name_user = 'group_' + self.name_le.text()
@@ -414,6 +419,7 @@ class ClassifierWidget(QWidget, Styles):
414
419
  # reset
415
420
  #self.init_class()
416
421
  #self.update_props_scatter()
422
+ self.parent_window.parent_window.update_position_options()
417
423
  self.close()
418
424
 
419
425
  def switch_to_log(self, i):
@@ -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
@@ -414,26 +414,46 @@ class ControlPanel(QMainWindow, Styles):
414
414
  self.pos = self.position_list.currentText()
415
415
  panels = [self.ProcessEffectors, self.ProcessTargets]
416
416
  if self.position_list.currentText()=="*":
417
+
417
418
  for p in panels:
418
419
  p.check_seg_btn.setEnabled(False)
419
420
  p.check_tracking_result_btn.setEnabled(False)
421
+
420
422
  self.ProcessTargets.view_tab_btn.setEnabled(True)
421
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
+
422
428
  self.ProcessTargets.check_seg_btn.setEnabled(False)
423
429
  self.ProcessEffectors.check_seg_btn.setEnabled(False)
430
+
424
431
  self.ProcessTargets.check_tracking_result_btn.setEnabled(False)
425
432
  self.ProcessEffectors.check_tracking_result_btn.setEnabled(False)
433
+
426
434
  self.ProcessEffectors.check_measurements_btn.setEnabled(False)
427
435
  self.ProcessTargets.check_measurements_btn.setEnabled(False)
428
436
  #self.ProcessTargets.signal_analysis_action.setEnabled(False)
429
437
  #self.ProcessEffectors.signal_analysis_action.setEnabled(False)
438
+
430
439
  self.ProcessTargets.check_signals_btn.setEnabled(False)
431
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
+
432
445
  self.view_stack_btn.setEnabled(False)
433
446
  elif self.well_list.currentText()=='*':
434
447
  self.ProcessTargets.view_tab_btn.setEnabled(True)
435
448
  self.ProcessEffectors.view_tab_btn.setEnabled(True)
436
- 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()
437
457
  else:
438
458
  if not self.well_list.currentText()=="*":
439
459
  self.locate_selected_position()
@@ -453,10 +473,16 @@ class ControlPanel(QMainWindow, Styles):
453
473
  self.ProcessEffectors.check_tracking_result_btn.setEnabled(False)
454
474
 
455
475
  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()
476
+ df = pd.read_csv(os.sep.join([self.pos,'output','tables','trajectories_effectors.csv']), nrows=1)
477
+ id_col = extract_identity_col(df)
457
478
  self.ProcessEffectors.check_measurements_btn.setEnabled(True)
458
- if 'TRACK_ID' in cols:
479
+ if id_col=='TRACK_ID':
459
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
+
460
486
  #self.ProcessEffectors.signal_analysis_action.setEnabled(True)
461
487
  self.ProcessEffectors.view_tab_btn.setEnabled(True)
462
488
  self.ProcessEffectors.classify_btn.setEnabled(True)
@@ -466,12 +492,19 @@ class ControlPanel(QMainWindow, Styles):
466
492
  #self.ProcessEffectors.signal_analysis_action.setEnabled(False)
467
493
  self.ProcessEffectors.view_tab_btn.setEnabled(False)
468
494
  self.ProcessEffectors.classify_btn.setEnabled(False)
495
+ self.ProcessEffectors.delete_tracks_btn.hide()
496
+ self.ProcessEffectors.signal_analysis_action.setEnabled(False)
469
497
 
470
498
  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()
499
+ df = pd.read_csv(os.sep.join([self.pos,'output','tables','trajectories_targets.csv']), nrows=1)
500
+ id_col = extract_identity_col(df)
472
501
  self.ProcessTargets.check_measurements_btn.setEnabled(True)
473
- if 'TRACK_ID' in cols:
502
+ if id_col=='TRACK_ID':
474
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)
475
508
  #self.ProcessTargets.signal_analysis_action.setEnabled(True)
476
509
  self.ProcessTargets.view_tab_btn.setEnabled(True)
477
510
  self.ProcessTargets.classify_btn.setEnabled(True)
@@ -481,4 +514,15 @@ class ControlPanel(QMainWindow, Styles):
481
514
  #self.ProcessTargets.signal_analysis_action.setEnabled(False)
482
515
  self.ProcessTargets.view_tab_btn.setEnabled(False)
483
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
+
484
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
 
@@ -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(''))
@@ -228,11 +230,12 @@ class ConfigNeighborhoods(QWidget, Styles):
228
230
 
229
231
  status_layout = QHBoxLayout()
230
232
 
231
- status_layout.addWidget(QLabel('status: '), 30)
233
+ #status_layout.addWidget(QLabel('status: '), 30)
232
234
 
233
235
  status_sublayout = QHBoxLayout()
234
236
  self.reference_population_status_cb = QComboBox()
235
237
  self.reference_population_status_cb.setToolTip('Status of the reference population.')
238
+ self.reference_population_status_cb.hide()
236
239
  status_sublayout.addWidget(self.reference_population_status_cb,95)
237
240
 
238
241
  self.reference_switch_status_btn = QPushButton("")
@@ -241,6 +244,7 @@ class ConfigNeighborhoods(QWidget, Styles):
241
244
  self.reference_switch_status_btn.setIconSize(QSize(20, 20))
242
245
  self.reference_switch_status_btn.clicked.connect(self.switch_not_reference)
243
246
  self.reference_switch_status_btn.setToolTip('Invert status values.')
247
+ self.reference_switch_status_btn.hide()
244
248
 
245
249
  status_sublayout.addWidget(self.reference_switch_status_btn, 5)
246
250
 
@@ -305,11 +309,13 @@ class ConfigNeighborhoods(QWidget, Styles):
305
309
 
306
310
  self.cumulated_presence_btn = QCheckBox('cumulated presence')
307
311
  self.cumulated_presence_btn.setToolTip("Compute the cumulated presence time of each neighbor around a reference cell.")
312
+ self.cumulated_presence_btn.setIcon(icon(MDI6.timer_outline, color='black'))
313
+
308
314
  layout.addWidget(self.cumulated_presence_btn)
309
315
 
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)
316
+ # self.symmetrize_btn = QCheckBox('symmetrize')
317
+ # self.symmetrize_btn.setToolTip("Write the neighborhood of the neighbor cells with respect to the reference cells.")
318
+ # layout.addWidget(self.symmetrize_btn)
313
319
 
314
320
  self.fill_cbs_of_neighbor_population()
315
321
  self.neighbor_population_cb.currentIndexChanged.connect(self.fill_cbs_of_neighbor_population)
@@ -424,7 +430,7 @@ class ConfigNeighborhoods(QWidget, Styles):
424
430
  neighborhood_options.update({'event_time_col': event_time_col})
425
431
 
426
432
  neighborhood_kwargs = {'mode': mode, 'status': status_options, 'not_status_option': [self.not_status_reference, self.not_status_neighbor],
427
- 'compute_cum_sum': self.cumulated_presence_btn.isChecked(), 'attention_weight': True, 'symmetrize': self.symmetrize_btn.isChecked(),
433
+ 'compute_cum_sum': self.cumulated_presence_btn.isChecked(), 'attention_weight': True, 'symmetrize': False,
428
434
  'include_dead_weight': True}
429
435
 
430
436
  neighborhood_options.update({'neighborhood_kwargs': neighborhood_kwargs})
@@ -493,8 +499,6 @@ class ConfigNeighborhoods(QWidget, Styles):
493
499
  neighborhood_kwargs = neigh_instructions['neighborhood_kwargs']
494
500
  if 'compute_cum_sum' in neighborhood_kwargs:
495
501
  self.cumulated_presence_btn.setChecked(neighborhood_kwargs['compute_cum_sum'])
496
- if 'symmetrize' in neighborhood_kwargs:
497
- self.symmetrize_btn.setChecked(neighborhood_kwargs['symmetrize'])
498
502
  if 'status' in neighborhood_kwargs:
499
503
  status_options = neighborhood_kwargs['status']
500
504
  status_options = ['--' if s is None else s for s in status_options]
@@ -505,6 +509,6 @@ class ConfigNeighborhoods(QWidget, Styles):
505
509
  if 'not_status_option' in neighborhood_kwargs:
506
510
  not_status_option = neighborhood_kwargs['not_status_option']
507
511
  if not_status_option[0]:
508
- self.switch_not_reference.click()
512
+ self.reference_switch_status_btn.click()
509
513
  if not_status_option[1]:
510
- self.switch_not_neigh.click()
514
+ self.neighbor_switch_status_btn.click()
@@ -6,7 +6,7 @@ from PyQt5.QtGui import QIcon, QDoubleValidator
6
6
  from sklearn.preprocessing import MinMaxScaler
7
7
 
8
8
  from celldetective.gui.gui_utils import center_window, FeatureChoice, ListWidget, QHSeperationLine, FigureCanvas, GeometryChoice, OperationChoice
9
- from superqt import QLabeledSlider
9
+ from superqt import QLabeledSlider, QColormapComboBox
10
10
  from superqt.fonticon import icon
11
11
  from fonticon_mdi6 import MDI6
12
12
  from celldetective.utils import extract_experiment_channels, get_software_location, _extract_labels_from_config
@@ -33,6 +33,7 @@ from matplotlib.cm import viridis, tab10
33
33
  import math
34
34
  from celldetective.gui import Styles
35
35
  from matplotlib import colormaps
36
+ import matplotlib.cm as mcm
36
37
 
37
38
 
38
39
 
@@ -117,9 +118,11 @@ class ConfigSignalPlot(QWidget, Styles):
117
118
  """)
118
119
  main_layout.addWidget(panel_title, alignment=Qt.AlignCenter)
119
120
 
120
- labels = [QLabel('population: '), QLabel('class: '), QLabel('time of\ninterest: ')]
121
- self.cb_options = [['targets','effectors'],['class'], ['t0']]
121
+ labels = [QLabel('population: '), QLabel('class: '), QLabel('time of\ninterest: '), QLabel('cmap: ')]
122
+ self.cb_options = [['targets','effectors'],['class'], ['t0'], list(plt.colormaps())]
122
123
  self.cbs = [QComboBox() for i in range(len(labels))]
124
+ self.cbs[-1] = QColormapComboBox()
125
+
123
126
  self.cbs[0].currentIndexChanged.connect(self.set_classes_and_times)
124
127
 
125
128
  choice_layout = QVBoxLayout()
@@ -128,9 +131,15 @@ class ConfigSignalPlot(QWidget, Styles):
128
131
  hbox = QHBoxLayout()
129
132
  hbox.addWidget(labels[i], 33)
130
133
  hbox.addWidget(self.cbs[i],66)
131
- self.cbs[i].addItems(self.cb_options[i])
134
+ if i < len(labels)-1:
135
+ self.cbs[i].addItems(self.cb_options[i])
132
136
  choice_layout.addLayout(hbox)
133
-
137
+
138
+ for cm in list(colormaps):
139
+ try:
140
+ self.cbs[-1].addColormap(cm)
141
+ except:
142
+ pass
134
143
 
135
144
  self.cbs[0].setCurrentIndex(1)
136
145
  self.cbs[0].setCurrentIndex(0)
@@ -150,6 +159,11 @@ class ConfigSignalPlot(QWidget, Styles):
150
159
 
151
160
  self.abs_time_checkbox.stateChanged.connect(self.switch_ref_time_mode)
152
161
 
162
+ select_layout = QHBoxLayout()
163
+ select_layout.addWidget(QLabel('select cells\nwith query: '), 33)
164
+ self.query_le = QLineEdit()
165
+ select_layout.addWidget(self.query_le, 66)
166
+ main_layout.addLayout(select_layout)
153
167
 
154
168
  time_calib_layout = QHBoxLayout()
155
169
  time_calib_layout.setContentsMargins(20,20,20,20)
@@ -266,9 +280,18 @@ class ConfigSignalPlot(QWidget, Styles):
266
280
  self.feature_two_cb.setEnabled(True)
267
281
  else:
268
282
  self.feature_two_cb.setEnabled(False)
283
+
269
284
  def compute_signals(self):
270
285
 
271
286
  if self.df is not None:
287
+
288
+ try:
289
+ query_text = self.query_le.text()
290
+ if query_text != '':
291
+ self.df = self.df.query(query_text)
292
+ except Exception as e:
293
+ print(e, ' The query is misunderstood and will not be applied...')
294
+
272
295
  self.feature_selected = self.feature_cb.currentText()
273
296
  if self.checkBox_feature.isChecked():
274
297
  self.second_feature_selected=self.feature_two_cb.currentText()
@@ -334,6 +357,10 @@ class ConfigSignalPlot(QWidget, Styles):
334
357
  self.initialize_axis()
335
358
  plt.tight_layout()
336
359
 
360
+ cmap_lbl = self.cbs[-1].currentText()
361
+ self.cmap = getattr(mcm, cmap_lbl)
362
+ self.ax.set_prop_cycle('color',[self.cmap(i) for i in np.linspace(0, 1, len(self.well_indices))])
363
+
337
364
 
338
365
  self.fig.set_facecolor('none') # or 'None'
339
366
  self.fig.canvas.setStyleSheet("background-color: transparent;")
@@ -353,7 +380,6 @@ class ConfigSignalPlot(QWidget, Styles):
353
380
  radio_hbox.addWidget(self.plot_options[i], 33, alignment=Qt.AlignCenter)
354
381
  self.plot_btn_group.buttonClicked[int].connect(self.plot_survivals)
355
382
 
356
- print(self.well_indices, self.position_indices)
357
383
  if self.position_indices is not None:
358
384
  if len(self.well_indices)>1 and len(self.position_indices)==1:
359
385
  self.plot_btn_group.buttons()[0].click()
@@ -725,10 +751,10 @@ class ConfigSignalPlot(QWidget, Styles):
725
751
  second_matrix='second_matrix_no_event'
726
752
 
727
753
 
728
- colors = np.array([tab10(i / len(self.df_pos_info)) for i in range(len(self.df_pos_info))])
754
+ colors = np.array([self.cmap(i / len(self.df_pos_info)) for i in range(len(self.df_pos_info))])
729
755
  if self.checkBox_feature.isChecked():
730
- second_colors = tab10(np.linspace(0.5, 1.5, len(self.df_pos_info)))
731
- well_color = [tab10(i / len(self.df_well_info)) for i in range(len(self.df_well_info))]
756
+ second_colors = self.cmap(np.linspace(0.5, 1.5, len(self.df_pos_info)))
757
+ well_color = [self.cmap(i / len(self.df_well_info)) for i in range(len(self.df_well_info))]
732
758
 
733
759
  if self.plot_mode=='pos':
734
760
  self.initialize_axis()
@@ -801,6 +827,8 @@ class ConfigSignalPlot(QWidget, Styles):
801
827
  self.survival_window.canvas.draw()
802
828
 
803
829
  def plot_line(self, line, color, label, mean_signal, ci_option=True, cell_lines_option=False, alpha_ci=0.5, alpha_cell_lines=0.5, std_signal=None, matrix=None):
830
+
831
+
804
832
  try:
805
833
  if 'second' in str(mean_signal):
806
834
  self.ax2.plot(line['timeline'] * self.FrameToMin, line[mean_signal], color=color, label=label)
@@ -1005,10 +1033,10 @@ class ConfigSignalPlot(QWidget, Styles):
1005
1033
  def switch_ref_time_mode(self):
1006
1034
  if self.abs_time_checkbox.isChecked():
1007
1035
  self.frame_slider.setEnabled(True)
1008
- self.cbs[-1].setEnabled(False)
1036
+ self.cbs[-2].setEnabled(False)
1009
1037
  else:
1010
1038
  self.frame_slider.setEnabled(False)
1011
- self.cbs[-1].setEnabled(True)
1039
+ self.cbs[-2].setEnabled(True)
1012
1040
 
1013
1041
  def switch_ci(self):
1014
1042