celldetective 1.1.1.post4__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.post4 → celldetective-1.2.0}/PKG-INFO +1 -1
  2. celldetective-1.2.0/celldetective/__init__.py +3 -0
  3. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/extra_properties.py +62 -34
  4. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/__init__.py +1 -0
  5. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/analyze_block.py +2 -1
  6. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/classifier_widget.py +8 -7
  7. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/control_panel.py +50 -6
  8. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/layouts.py +5 -4
  9. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/neighborhood_options.py +10 -8
  10. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/plot_signals_ui.py +39 -11
  11. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/process_block.py +413 -95
  12. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/retrain_segmentation_model_options.py +17 -4
  13. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/retrain_signal_model_options.py +106 -6
  14. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/signal_annotator.py +25 -5
  15. celldetective-1.2.0/celldetective/gui/signal_annotator2.py +2708 -0
  16. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/signal_annotator_options.py +3 -1
  17. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/survival_ui.py +15 -6
  18. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/tableUI.py +235 -39
  19. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/io.py +537 -421
  20. celldetective-1.2.0/celldetective/measure.py +988 -0
  21. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/neighborhood.py +426 -354
  22. celldetective-1.2.0/celldetective/relative_measurements.py +648 -0
  23. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/scripts/analyze_signals.py +1 -1
  24. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/scripts/measure_cells.py +28 -8
  25. celldetective-1.2.0/celldetective/scripts/measure_relative.py +103 -0
  26. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/scripts/segment_cells.py +5 -5
  27. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/scripts/track_cells.py +4 -1
  28. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/scripts/train_segmentation_model.py +23 -18
  29. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/scripts/train_signal_model.py +33 -0
  30. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/signals.py +402 -8
  31. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/tracking.py +8 -2
  32. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/utils.py +93 -0
  33. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective.egg-info/PKG-INFO +1 -1
  34. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective.egg-info/SOURCES.txt +4 -0
  35. {celldetective-1.1.1.post4 → celldetective-1.2.0}/setup.py +1 -1
  36. celldetective-1.2.0/tests/__init__.py +0 -0
  37. celldetective-1.1.1.post4/celldetective/__init__.py +0 -2
  38. celldetective-1.1.1.post4/celldetective/measure.py +0 -1038
  39. {celldetective-1.1.1.post4 → celldetective-1.2.0}/LICENSE +0 -0
  40. {celldetective-1.1.1.post4 → celldetective-1.2.0}/README.md +0 -0
  41. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/__main__.py +0 -0
  42. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/datasets/segmentation_annotations/blank +0 -0
  43. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/datasets/signal_annotations/blank +0 -0
  44. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/events.py +0 -0
  45. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/filters.py +0 -0
  46. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/about.py +0 -0
  47. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/btrack_options.py +0 -0
  48. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/configure_new_exp.py +0 -0
  49. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/gui_utils.py +0 -0
  50. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/json_readers.py +0 -0
  51. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/measurement_options.py +0 -0
  52. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/plot_measurements.py +0 -0
  53. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/seg_model_loader.py +0 -0
  54. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/styles.py +0 -0
  55. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/thresholds_gui.py +0 -0
  56. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/gui/viewers.py +0 -0
  57. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/icons/logo-large.png +0 -0
  58. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/icons/logo.png +0 -0
  59. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/icons/signals_icon.png +0 -0
  60. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/icons/splash-test.png +0 -0
  61. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/icons/splash.png +0 -0
  62. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/icons/splash0.png +0 -0
  63. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/icons/survival2.png +0 -0
  64. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/icons/vignette_signals2.png +0 -0
  65. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/icons/vignette_signals2.svg +0 -0
  66. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/links/zenodo.json +0 -0
  67. {celldetective-1.1.1.post4/celldetective/models/segmentation_effectors → celldetective-1.2.0/celldetective/models/pair_signal_detection}/blank +0 -0
  68. {celldetective-1.1.1.post4/celldetective/models/segmentation_generic → celldetective-1.2.0/celldetective/models/segmentation_effectors}/blank +0 -0
  69. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/models/segmentation_effectors/primNK_cfse/config_input.json +0 -0
  70. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/models/segmentation_effectors/primNK_cfse/cp-cfse-transfer +0 -0
  71. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/models/segmentation_effectors/primNK_cfse/training_instructions.json +0 -0
  72. {celldetective-1.1.1.post4/celldetective/models/segmentation_targets → celldetective-1.2.0/celldetective/models/segmentation_generic}/blank +0 -0
  73. {celldetective-1.1.1.post4/celldetective/models/signal_detection → celldetective-1.2.0/celldetective/models/segmentation_targets}/blank +0 -0
  74. /celldetective-1.1.1.post4/tests/__init__.py → /celldetective-1.2.0/celldetective/models/signal_detection/blank +0 -0
  75. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/models/tracking_configs/mcf7.json +0 -0
  76. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/models/tracking_configs/ricm.json +0 -0
  77. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/models/tracking_configs/ricm2.json +0 -0
  78. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/preprocessing.py +0 -0
  79. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/scripts/segment_cells_thresholds.py +0 -0
  80. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective/segmentation.py +0 -0
  81. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective.egg-info/dependency_links.txt +0 -0
  82. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective.egg-info/entry_points.txt +0 -0
  83. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective.egg-info/not-zip-safe +0 -0
  84. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective.egg-info/requires.txt +0 -0
  85. {celldetective-1.1.1.post4 → celldetective-1.2.0}/celldetective.egg-info/top_level.txt +0 -0
  86. {celldetective-1.1.1.post4 → celldetective-1.2.0}/setup.cfg +0 -0
  87. {celldetective-1.1.1.post4 → celldetective-1.2.0}/tests/test_events.py +0 -0
  88. {celldetective-1.1.1.post4 → celldetective-1.2.0}/tests/test_filters.py +0 -0
  89. {celldetective-1.1.1.post4 → celldetective-1.2.0}/tests/test_io.py +0 -0
  90. {celldetective-1.1.1.post4 → celldetective-1.2.0}/tests/test_measure.py +0 -0
  91. {celldetective-1.1.1.post4 → celldetective-1.2.0}/tests/test_neighborhood.py +0 -0
  92. {celldetective-1.1.1.post4 → celldetective-1.2.0}/tests/test_preprocessing.py +0 -0
  93. {celldetective-1.1.1.post4 → celldetective-1.2.0}/tests/test_segmentation.py +0 -0
  94. {celldetective-1.1.1.post4 → celldetective-1.2.0}/tests/test_signals.py +0 -0
  95. {celldetective-1.1.1.post4 → celldetective-1.2.0}/tests/test_tracking.py +0 -0
  96. {celldetective-1.1.1.post4 → 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.post4
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
+
@@ -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
 
@@ -375,13 +375,14 @@ class ClassifierWidget(QWidget, Styles):
375
375
  frames = track['FRAME'].to_numpy()
376
376
  t_first = track['t_firstdetection'].to_numpy()[0]
377
377
  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
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
385
386
  if self.irreversible_event_btn.isChecked():
386
387
  self.estimate_time()
387
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
@@ -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(''))
@@ -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)
@@ -424,7 +428,7 @@ class ConfigNeighborhoods(QWidget, Styles):
424
428
  neighborhood_options.update({'event_time_col': event_time_col})
425
429
 
426
430
  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(),
431
+ 'compute_cum_sum': self.cumulated_presence_btn.isChecked(), 'attention_weight': True, 'symmetrize': False,
428
432
  'include_dead_weight': True}
429
433
 
430
434
  neighborhood_options.update({'neighborhood_kwargs': neighborhood_kwargs})
@@ -493,8 +497,6 @@ class ConfigNeighborhoods(QWidget, Styles):
493
497
  neighborhood_kwargs = neigh_instructions['neighborhood_kwargs']
494
498
  if 'compute_cum_sum' in neighborhood_kwargs:
495
499
  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
500
  if 'status' in neighborhood_kwargs:
499
501
  status_options = neighborhood_kwargs['status']
500
502
  status_options = ['--' if s is None else s for s in status_options]
@@ -505,6 +507,6 @@ class ConfigNeighborhoods(QWidget, Styles):
505
507
  if 'not_status_option' in neighborhood_kwargs:
506
508
  not_status_option = neighborhood_kwargs['not_status_option']
507
509
  if not_status_option[0]:
508
- self.switch_not_reference.click()
510
+ self.reference_switch_status_btn.click()
509
511
  if not_status_option[1]:
510
- self.switch_not_neigh.click()
512
+ 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