celldetective 1.3.8.post1__py3-none-any.whl → 1.3.9.post1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. celldetective/_version.py +1 -1
  2. celldetective/extra_properties.py +113 -17
  3. celldetective/filters.py +12 -12
  4. celldetective/gui/btrack_options.py +1 -1
  5. celldetective/gui/control_panel.py +1 -1
  6. celldetective/gui/gui_utils.py +4 -4
  7. celldetective/gui/measurement_options.py +1 -1
  8. celldetective/gui/plot_signals_ui.py +23 -6
  9. celldetective/gui/process_block.py +1 -1
  10. celldetective/gui/processes/measure_cells.py +4 -4
  11. celldetective/gui/processes/segment_cells.py +3 -3
  12. celldetective/gui/processes/track_cells.py +4 -4
  13. celldetective/gui/signal_annotator.py +26 -6
  14. celldetective/gui/signal_annotator2.py +1 -1
  15. celldetective/gui/signal_annotator_options.py +1 -1
  16. celldetective/gui/survival_ui.py +4 -1
  17. celldetective/gui/thresholds_gui.py +6 -5
  18. celldetective/io.py +1 -44
  19. celldetective/measure.py +22 -16
  20. celldetective/regionprops/__init__.py +1 -0
  21. celldetective/regionprops/_regionprops.py +310 -0
  22. celldetective/regionprops/props.json +63 -0
  23. celldetective/scripts/measure_relative.py +2 -20
  24. celldetective/segmentation.py +14 -4
  25. celldetective/utils.py +182 -171
  26. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.post1.dist-info}/METADATA +1 -1
  27. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.post1.dist-info}/RECORD +31 -28
  28. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.post1.dist-info}/LICENSE +0 -0
  29. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.post1.dist-info}/WHEEL +0 -0
  30. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.post1.dist-info}/entry_points.txt +0 -0
  31. {celldetective-1.3.8.post1.dist-info → celldetective-1.3.9.post1.dist-info}/top_level.txt +0 -0
celldetective/_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "1.3.8.post1"
1
+ __version__ = "1.3.9.post1"
@@ -26,29 +26,125 @@ import numpy as np
26
26
  from scipy.ndimage import distance_transform_edt, center_of_mass
27
27
  from scipy.spatial.distance import euclidean
28
28
  from celldetective.utils import interpolate_nan, contour_of_instance_segmentation
29
+ import skimage.measure as skm
30
+ from stardist import fill_label_holes
31
+ from celldetective.segmentation import segment_frame_from_thresholds
32
+
33
+
34
+ # def area_detected_in_ricm(regionmask, intensity_image, target_channel='adhesion_channel'):
35
+
36
+ # instructions = {
37
+ # "thresholds": [
38
+ # 0.02,
39
+ # 1000
40
+ # ],
41
+ # "filters": [
42
+ # [
43
+ # "subtract",
44
+ # 1
45
+ # ],
46
+ # [
47
+ # "abs",
48
+ # 2
49
+ # ],
50
+ # [
51
+ # "gauss",
52
+ # 0.8
53
+ # ]
54
+ # ],
55
+ # #"marker_min_distance": 1,
56
+ # #"marker_footprint_size": 10,
57
+ # "feature_queries": [
58
+ # "eccentricity > 0.99 or area < 60"
59
+ # ],
60
+ # }
61
+
62
+ # lbl = segment_frame_from_thresholds(intensity_image, fill_holes=True, do_watershed=False, equalize_reference=None, edge_exclusion=False, **instructions)
63
+ # lbl[lbl>0] = 1 # instance to binary
64
+ # lbl[~regionmask] = 0 # make sure we don't measure stuff outside cell
65
+
66
+ # return np.sum(lbl)
67
+
68
+ def fraction_of_area_detected_in_ricm(regionmask, intensity_image, target_channel='adhesion_channel'):
69
+
70
+ instructions = {
71
+ "thresholds": [
72
+ 0.02,
73
+ 1000
74
+ ],
75
+ "filters": [
76
+ [
77
+ "subtract",
78
+ 1
79
+ ],
80
+ [
81
+ "abs",
82
+ 2
83
+ ],
84
+ [
85
+ "gauss",
86
+ 0.8
87
+ ]
88
+ ],
89
+ }
90
+
91
+ lbl = segment_frame_from_thresholds(intensity_image, do_watershed=False, fill_holes=True, equalize_reference=None, edge_exclusion=False, **instructions)
92
+ lbl[lbl>0] = 1 # instance to binary
93
+ lbl[~regionmask] = 0 # make sure we don't measure stuff outside cell
94
+
95
+ return float(np.sum(lbl)) / float(np.sum(regionmask))
96
+
97
+ def area_detected_in_ricm(regionmask, intensity_image, target_channel='adhesion_channel'):
98
+
99
+ instructions = {
100
+ "thresholds": [
101
+ 0.02,
102
+ 1000
103
+ ],
104
+ "filters": [
105
+ [
106
+ "subtract",
107
+ 1
108
+ ],
109
+ [
110
+ "abs",
111
+ 2
112
+ ],
113
+ [
114
+ "gauss",
115
+ 0.8
116
+ ]
117
+ ],
118
+ }
119
+
120
+ lbl = segment_frame_from_thresholds(intensity_image, do_watershed=False, fill_holes=True, equalize_reference=None, edge_exclusion=False, **instructions)
121
+ lbl[lbl>0] = 1 # instance to binary
122
+ lbl[~regionmask] = 0 # make sure we don't measure stuff outside cell
123
+
124
+ return float(np.sum(lbl))
29
125
 
30
- # Percentiles
31
-
32
- def custom_area(regionmask):
33
- return np.sum(regionmask)
34
126
 
35
- def intensity_area_under_one(regionmask, intensity_image):
127
+ def area_dark(regionmask, intensity_image, target_channel='adhesion_channel', fill_holes=True): #, target_channel='adhesion_channel'
36
128
 
37
- subregion = regionmask[intensity_image<1]
38
- if len(subregion)>0:
39
- return np.sum(subregion)
40
- else:
41
- return 0.0
129
+ subregion = (intensity_image < 0.95)*regionmask # under one, under 0.8, under 0.6, whatever value!
130
+ if fill_holes:
131
+ subregion = skm.label(subregion, connectivity=2, background=0)
132
+ subregion = fill_label_holes(subregion)
133
+ subregion[subregion>0] = 1
134
+
135
+ return float(np.sum(subregion))
42
136
 
43
- def intensity_fraction_of_area_under_one(regionmask, intensity_image):
44
137
 
45
- subregion = regionmask[intensity_image<1]
46
- area = np.sum(regionmask)
138
+ def fraction_of_area_dark(regionmask, intensity_image, target_channel='adhesion_channel', fill_holes=True): #, target_channel='adhesion_channel'
139
+
140
+ subregion = (intensity_image < 0.95)*regionmask # under one, under 0.8, under 0.6, whatever value!
141
+ if fill_holes:
142
+ subregion = skm.label(subregion, connectivity=2, background=0)
143
+ subregion = fill_label_holes(subregion)
144
+ subregion[subregion>0] = 1
145
+
146
+ return float(np.sum(subregion)) / float(np.sum(regionmask))
47
147
 
48
- if len(subregion) > 0:
49
- return float(np.sum(subregion)) / float(area)
50
- else:
51
- return 0.0
52
148
 
53
149
  def intensity_percentile_ninety_nine(regionmask, intensity_image):
54
150
  return np.nanpercentile(intensity_image[regionmask],99)
celldetective/filters.py CHANGED
@@ -5,14 +5,14 @@ import numpy as np
5
5
 
6
6
  def gauss_filter(img, sigma, interpolate=True, *kwargs):
7
7
 
8
- if interpolate:
8
+ if np.any(img!=img) and interpolate:
9
9
  img = interpolate_nan(img.astype(float))
10
10
 
11
11
  return snd.gaussian_filter(img.astype(float), sigma, *kwargs)
12
12
 
13
13
  def median_filter(img, size, interpolate=True, *kwargs):
14
14
 
15
- if interpolate:
15
+ if np.any(img!=img) and interpolate:
16
16
  img = interpolate_nan(img.astype(float))
17
17
 
18
18
  size = int(size)
@@ -20,19 +20,19 @@ def median_filter(img, size, interpolate=True, *kwargs):
20
20
  return snd.median_filter(img, size, *kwargs)
21
21
 
22
22
  def maximum_filter(img, size, interpolate=True, *kwargs):
23
- if interpolate:
23
+ if np.any(img!=img) and interpolate:
24
24
  img = interpolate_nan(img.astype(float))
25
25
 
26
26
  return snd.maximum_filter(img.astype(float), size, *kwargs)
27
27
 
28
28
  def minimum_filter(img, size, interpolate=True, *kwargs):
29
- if interpolate:
29
+ if np.any(img!=img) and interpolate:
30
30
  img = interpolate_nan(img.astype(float))
31
31
 
32
32
  return snd.minimum_filter(img.astype(float), size, *kwargs)
33
33
 
34
34
  def percentile_filter(img, percentile, size, interpolate=True, *kwargs):
35
- if interpolate:
35
+ if np.any(img!=img) and interpolate:
36
36
  img = interpolate_nan(img.astype(float))
37
37
 
38
38
  return snd.percentile_filter(img.astype(float), percentile, size, *kwargs)
@@ -44,7 +44,7 @@ def abs_filter(img, *kwargs):
44
44
  return np.abs(img)
45
45
 
46
46
  def ln_filter(img, interpolate=True, *kwargs):
47
- if interpolate:
47
+ if np.any(img!=img) and interpolate:
48
48
  img = interpolate_nan(img.astype(float))
49
49
 
50
50
  img[np.where(img>0.)] = np.log(img[np.where(img>0.)])
@@ -54,7 +54,7 @@ def ln_filter(img, interpolate=True, *kwargs):
54
54
 
55
55
  def variance_filter(img, size, interpolate=True):
56
56
 
57
- if interpolate:
57
+ if np.any(img!=img) and interpolate:
58
58
  img = interpolate_nan(img.astype(float))
59
59
 
60
60
  size = int(size)
@@ -67,7 +67,7 @@ def variance_filter(img, size, interpolate=True):
67
67
 
68
68
  def std_filter(img, size, interpolate=True):
69
69
 
70
- if interpolate:
70
+ if np.any(img!=img) and interpolate:
71
71
  img = interpolate_nan(img.astype(float))
72
72
 
73
73
  size = int(size)
@@ -84,13 +84,13 @@ def std_filter(img, size, interpolate=True):
84
84
  return img
85
85
 
86
86
  def laplace_filter(img, output=float, interpolate=True, *kwargs):
87
- if interpolate:
87
+ if np.any(img!=img) and interpolate:
88
88
  img = interpolate_nan(img.astype(float))
89
89
  return snd.laplace(img.astype(float), *kwargs)
90
90
 
91
91
  def dog_filter(img, blob_size=None, sigma_low=1, sigma_high=2, interpolate=True, *kwargs):
92
92
 
93
- if interpolate:
93
+ if np.any(img!=img) and interpolate:
94
94
  img = interpolate_nan(img.astype(float))
95
95
  if blob_size is not None:
96
96
  sigma_low = 1.0 / (1.0 + np.sqrt(2)) * blob_size
@@ -121,7 +121,7 @@ def sauvola_filter(img, *kwargs):
121
121
 
122
122
  def log_filter(img, blob_size=None, sigma=1, interpolate=True, *kwargs):
123
123
 
124
- if interpolate:
124
+ if np.any(img!=img) and interpolate:
125
125
  img = interpolate_nan(img.astype(float))
126
126
  if blob_size is not None:
127
127
  sigma_low = 1.0 / (1.0 + np.sqrt(2)) * blob_size
@@ -131,7 +131,7 @@ def log_filter(img, blob_size=None, sigma=1, interpolate=True, *kwargs):
131
131
 
132
132
  def tophat_filter(img, size, connectivity=4, interpolate=True, *kwargs):
133
133
 
134
- if interpolate:
134
+ if np.any(img!=img) and interpolate:
135
135
  img = interpolate_nan(img.astype(float))
136
136
  structure = snd.generate_binary_structure(rank=2, connectivity=connectivity)
137
137
  img = snd.white_tophat(img.astype(float), structure=structure, size=size, *kwargs)
@@ -44,7 +44,7 @@ class ConfigTracking(QMainWindow, Styles):
44
44
 
45
45
  exp_config = self.exp_dir +"config.ini"
46
46
  self.config_path = self.exp_dir + self.config_name
47
- self.channel_names, self.channels = extract_experiment_channels(exp_config)
47
+ self.channel_names, self.channels = extract_experiment_channels(self.exp_dir)
48
48
  self.channel_names = np.array(self.channel_names)
49
49
  self.channels = np.array(self.channels)
50
50
  self.screen_height = self.parent_window.parent_window.parent_window.screen_height
@@ -330,7 +330,7 @@ class ControlPanel(QMainWindow, Styles):
330
330
  self.movie_prefix = ConfigSectionMap(self.exp_config,"MovieSettings")["movie_prefix"]
331
331
 
332
332
  # Read channels
333
- self.exp_channels, channel_indices = extract_experiment_channels(self.exp_config)
333
+ self.exp_channels, channel_indices = extract_experiment_channels(self.exp_dir)
334
334
  self.nbr_channels = len(self.exp_channels)
335
335
 
336
336
  number_of_wells = len(self.wells)
@@ -575,10 +575,10 @@ class FeatureChoice(QWidget, Styles):
575
575
  "intensity_min",
576
576
  ]
577
577
 
578
- extra_props = getmembers(extra_properties, isfunction)
579
- extra_props = [extra_props[i][0] for i in range(len(extra_props))]
580
- if len(extra_props) > 0:
581
- standard_measurements.extend(extra_props)
578
+ members = getmembers(extra_properties, isfunction)
579
+ for o in members:
580
+ if isfunction(o[1]) and o[1].__module__=="celldetective.extra_properties":
581
+ standard_measurements.append(o[0])
582
582
 
583
583
  self.combo_box.addItems(standard_measurements)
584
584
 
@@ -58,7 +58,7 @@ class ConfigMeasurements(QMainWindow, Styles):
58
58
 
59
59
  exp_config = self.exp_dir + "config.ini"
60
60
  self.config_path = self.exp_dir + self.config_name
61
- self.channel_names, self.channels = extract_experiment_channels(exp_config)
61
+ self.channel_names, self.channels = extract_experiment_channels(self.exp_dir)
62
62
  self.channel_names = np.array(self.channel_names)
63
63
  self.channels = np.array(self.channels)
64
64
 
@@ -164,6 +164,18 @@ class ConfigSignalPlot(QWidget, Styles):
164
164
  pool_layout.addWidget(self.pool_option_cb, 66)
165
165
  main_layout.addLayout(pool_layout)
166
166
 
167
+ n_cells_layout = QHBoxLayout()
168
+ n_cells_layout.setContentsMargins(20,3,20,3)
169
+ self.n_cells_slider = QLabeledSlider()
170
+ self.n_cells_slider.setSingleStep(1)
171
+ self.n_cells_slider.setOrientation(1)
172
+ self.n_cells_slider.setRange(1,100)
173
+ self.n_cells_slider.setValue(2)
174
+ n_cells_layout.addWidget(QLabel('min # cells\nfor pool:'), 33)
175
+ n_cells_layout.addWidget(self.n_cells_slider, 66)
176
+ main_layout.addLayout(n_cells_layout)
177
+
178
+
167
179
  self.submit_btn = QPushButton('Submit')
168
180
  self.submit_btn.setStyleSheet(self.button_style_sheet)
169
181
  self.submit_btn.clicked.connect(self.process_signal)
@@ -363,6 +375,11 @@ class ConfigSignalPlot(QWidget, Styles):
363
375
  return None
364
376
 
365
377
  # Per position signal
378
+ self.df = self.df.dropna(subset=['FRAME'])
379
+ if len(self.df)==0:
380
+ print('Warning... The dataset is empty. Please check your filters. Abort...')
381
+ return None
382
+
366
383
  max_time = int(self.df.FRAME.max()) + 1
367
384
  class_col = self.class_columns[self.cbs[1].currentIndex()]
368
385
  time_col = self.time_columns[self.cbs[2].currentIndex()]
@@ -371,9 +388,9 @@ class ConfigSignalPlot(QWidget, Styles):
371
388
 
372
389
  for block,movie_group in self.df.groupby(['well','position']):
373
390
 
374
- well_signal_mean, well_std_mean, timeline_all, matrix_all = mean_signal(movie_group, self.feature_selected, class_col, time_col=time_col, class_value=None, return_matrix=True, forced_max_duration=max_time, projection=self.pool_option_cb.currentText())
375
- well_signal_event, well_std_event, timeline_event, matrix_event = mean_signal(movie_group, self.feature_selected, class_col, time_col=time_col, class_value=[0], return_matrix=True, forced_max_duration=max_time, projection=self.pool_option_cb.currentText())
376
- well_signal_no_event, well_std_no_event, timeline_no_event, matrix_no_event = mean_signal(movie_group, self.feature_selected, class_col, time_col=time_col, class_value=[1], return_matrix=True, forced_max_duration=max_time, projection=self.pool_option_cb.currentText())
391
+ well_signal_mean, well_std_mean, timeline_all, matrix_all = mean_signal(movie_group, self.feature_selected, class_col, time_col=time_col, class_value=None, return_matrix=True, forced_max_duration=max_time, projection=self.pool_option_cb.currentText(), min_nbr_values=self.n_cells_slider.value())
392
+ well_signal_event, well_std_event, timeline_event, matrix_event = mean_signal(movie_group, self.feature_selected, class_col, time_col=time_col, class_value=[0], return_matrix=True, forced_max_duration=max_time, projection=self.pool_option_cb.currentText(), min_nbr_values=self.n_cells_slider.value())
393
+ well_signal_no_event, well_std_no_event, timeline_no_event, matrix_no_event = mean_signal(movie_group, self.feature_selected, class_col, time_col=time_col, class_value=[1], return_matrix=True, forced_max_duration=max_time, projection=self.pool_option_cb.currentText(), min_nbr_values=self.n_cells_slider.value())
377
394
  self.mean_plots_timeline = timeline_all
378
395
 
379
396
  self.df_pos_info.loc[self.df_pos_info['pos_path'] == block[1], 'signal'] = [
@@ -386,9 +403,9 @@ class ConfigSignalPlot(QWidget, Styles):
386
403
  # Per well
387
404
  for well,well_group in self.df.groupby('well'):
388
405
 
389
- well_signal_mean, well_std_mean, timeline_all, matrix_all = mean_signal(well_group, self.feature_selected, class_col, time_col=time_col, class_value=None, return_matrix=True, forced_max_duration=max_time, projection=self.pool_option_cb.currentText())
390
- well_signal_event, well_std_event, timeline_event, matrix_event = mean_signal(well_group, self.feature_selected, class_col, time_col=time_col, class_value=[0], return_matrix=True, forced_max_duration=max_time, projection=self.pool_option_cb.currentText())
391
- well_signal_no_event, well_std_no_event, timeline_no_event, matrix_no_event = mean_signal(well_group, self.feature_selected, class_col, time_col=time_col, class_value=[1], return_matrix=True, forced_max_duration=max_time, projection=self.pool_option_cb.currentText())
406
+ well_signal_mean, well_std_mean, timeline_all, matrix_all = mean_signal(well_group, self.feature_selected, class_col, time_col=time_col, class_value=None, return_matrix=True, forced_max_duration=max_time, projection=self.pool_option_cb.currentText(), min_nbr_values=self.n_cells_slider.value())
407
+ well_signal_event, well_std_event, timeline_event, matrix_event = mean_signal(well_group, self.feature_selected, class_col, time_col=time_col, class_value=[0], return_matrix=True, forced_max_duration=max_time, projection=self.pool_option_cb.currentText(), min_nbr_values=self.n_cells_slider.value())
408
+ well_signal_no_event, well_std_no_event, timeline_no_event, matrix_no_event = mean_signal(well_group, self.feature_selected, class_col, time_col=time_col, class_value=[1], return_matrix=True, forced_max_duration=max_time, projection=self.pool_option_cb.currentText(), min_nbr_values=self.n_cells_slider.value())
392
409
 
393
410
  self.df_well_info.loc[self.df_well_info['well_path']==well,'signal'] = [{'mean_all': well_signal_mean, 'std_all': well_std_mean,'matrix_all': matrix_all,'mean_event': well_signal_event, 'std_event': well_std_event,
394
411
  'matrix_event': matrix_event,'mean_no_event': well_signal_no_event, 'std_no_event': well_std_no_event, 'matrix_no_event': matrix_no_event, 'timeline': self.mean_plots_timeline}]
@@ -1503,7 +1503,7 @@ class PreprocessingPanel(QFrame, Styles):
1503
1503
  self.exp_dir = self.parent_window.exp_dir
1504
1504
  self.wells = np.array(self.parent_window.wells,dtype=str)
1505
1505
  exp_config = self.exp_dir + "config.ini"
1506
- self.channel_names, self.channels = extract_experiment_channels(exp_config)
1506
+ self.channel_names, self.channels = extract_experiment_channels(self.exp_dir)
1507
1507
  self.channel_names = np.array(self.channel_names)
1508
1508
  self.background_correction = []
1509
1509
  self.onlyFloat = QDoubleValidator()
@@ -75,7 +75,7 @@ class MeasurementProcess(Process):
75
75
  def read_measurement_instructions(self):
76
76
 
77
77
  print('Looking for measurement instruction file...')
78
- instr_path = PurePath(self.expfolder,Path(f"{self.instruction_file}"))
78
+ instr_path = PurePath(self.exp_dir,Path(f"{self.instruction_file}"))
79
79
  if os.path.exists(instr_path):
80
80
  with open(instr_path, 'r') as f:
81
81
  self.instructions = json.load(f)
@@ -175,14 +175,14 @@ class MeasurementProcess(Process):
175
175
  self.shape_x = int(ConfigSectionMap(self.config,"MovieSettings")["shape_x"])
176
176
  self.shape_y = int(ConfigSectionMap(self.config,"MovieSettings")["shape_y"])
177
177
 
178
- self.channel_names, self.channel_indices = extract_experiment_channels(self.config)
178
+ self.channel_names, self.channel_indices = extract_experiment_channels(self.exp_dir)
179
179
  self.nbr_channels = len(self.channel_names)
180
180
 
181
181
  def locate_experiment_config(self):
182
182
 
183
183
  parent1 = Path(self.pos).parent
184
- self.expfolder = parent1.parent
185
- self.config = PurePath(self.expfolder,Path("config.ini"))
184
+ self.exp_dir = parent1.parent
185
+ self.config = PurePath(self.exp_dir,Path("config.ini"))
186
186
 
187
187
  if not os.path.exists(self.config):
188
188
  print('The configuration file for the experiment was not found...')
@@ -66,13 +66,13 @@ class BaseSegmentProcess(Process):
66
66
  self.len_movie = float(ConfigSectionMap(self.config,"MovieSettings")["len_movie"])
67
67
  self.movie_prefix = ConfigSectionMap(self.config,"MovieSettings")["movie_prefix"]
68
68
  self.nbr_channels = _extract_nbr_channels_from_config(self.config)
69
- self.channel_names, self.channel_indices = extract_experiment_channels(self.config)
69
+ self.channel_names, self.channel_indices = extract_experiment_channels(self.exp_dir)
70
70
 
71
71
  def locate_experiment_config(self):
72
72
 
73
73
  parent1 = Path(self.pos).parent
74
- expfolder = parent1.parent
75
- self.config = PurePath(expfolder,Path("config.ini"))
74
+ self.exp_dir = parent1.parent
75
+ self.config = PurePath(self.exp_dir,Path("config.ini"))
76
76
 
77
77
  if not os.path.exists(self.config):
78
78
  print('The configuration file for the experiment could not be located. Abort.')
@@ -60,7 +60,7 @@ class TrackingProcess(Process):
60
60
 
61
61
  def read_tracking_instructions(self):
62
62
 
63
- instr_path = PurePath(self.expfolder,Path(f"{self.instruction_file}"))
63
+ instr_path = PurePath(self.exp_dir,Path(f"{self.instruction_file}"))
64
64
  if os.path.exists(instr_path):
65
65
  print(f"Tracking instructions for the {self.mode} population have been successfully loaded...")
66
66
  with open(instr_path, 'r') as f:
@@ -156,14 +156,14 @@ class TrackingProcess(Process):
156
156
  self.shape_x = int(ConfigSectionMap(self.config,"MovieSettings")["shape_x"])
157
157
  self.shape_y = int(ConfigSectionMap(self.config,"MovieSettings")["shape_y"])
158
158
 
159
- self.channel_names, self.channel_indices = extract_experiment_channels(self.config)
159
+ self.channel_names, self.channel_indices = extract_experiment_channels(self.exp_dir)
160
160
  self.nbr_channels = len(self.channel_names)
161
161
 
162
162
  def locate_experiment_config(self):
163
163
 
164
164
  parent1 = Path(self.pos).parent
165
- self.expfolder = parent1.parent
166
- self.config = PurePath(self.expfolder,Path("config.ini"))
165
+ self.exp_dir = parent1.parent
166
+ self.config = PurePath(self.exp_dir,Path("config.ini"))
167
167
 
168
168
  if not os.path.exists(self.config):
169
169
  print('The configuration file for the experiment was not found...')
@@ -7,7 +7,7 @@ from celldetective.gui.gui_utils import center_window, color_from_state
7
7
  from superqt import QLabeledDoubleSlider, QLabeledDoubleRangeSlider, QSearchableComboBox
8
8
  from celldetective.utils import extract_experiment_channels, get_software_location, _get_img_num_per_channel
9
9
  from celldetective.io import auto_load_number_of_frames, load_frames, \
10
- load_napari_data, get_experiment_metadata
10
+ load_napari_data, get_experiment_metadata, get_experiment_labels
11
11
  from celldetective.gui.gui_utils import FigureCanvas, color_from_status, color_from_class, ExportPlotBtn
12
12
  import json
13
13
  import numpy as np
@@ -687,7 +687,7 @@ class SignalAnnotator(QMainWindow, Styles):
687
687
  if len_movie_auto is not None:
688
688
  self.len_movie = len_movie_auto
689
689
  exp_config = self.exp_dir + "config.ini"
690
- self.channel_names, self.channels = extract_experiment_channels(exp_config)
690
+ self.channel_names, self.channels = extract_experiment_channels(self.exp_dir)
691
691
  self.channel_names = np.array(self.channel_names)
692
692
  self.channels = np.array(self.channels)
693
693
  self.nbr_channels = len(self.channels)
@@ -780,6 +780,7 @@ class SignalAnnotator(QMainWindow, Styles):
780
780
 
781
781
  self.MinMaxScaler = MinMaxScaler()
782
782
  self.columns_to_rescale = list(self.df_tracks.columns)
783
+ #self.columns_to_rescale = self.df_tracks.select_dtypes(exclude=['object']).columns
783
784
 
784
785
  # is_number = np.vectorize(lambda x: np.issubdtype(x, np.number))
785
786
  # is_number_test = is_number(self.df_tracks.dtypes)
@@ -788,17 +789,25 @@ class SignalAnnotator(QMainWindow, Styles):
788
789
 
789
790
  cols_to_remove = ['status', 'status_color', 'class_color', 'TRACK_ID', 'FRAME', 'x_anim', 'y_anim', 't',
790
791
  'state', 'generation', 'root', 'parent', 'class_id', 'class', 't0', 'POSITION_X',
791
- 'POSITION_Y', 'position', 'well', 'well_index', 'well_name', 'pos_name', 'index',
792
- 'concentration', 'cell_type', 'antibody', 'pharmaceutical_agent'] + self.class_cols
792
+ 'POSITION_Y', 'position', 'well', 'well_index', 'well_name', 'pos_name', 'index',] + self.class_cols
793
+
793
794
  meta = get_experiment_metadata(self.exp_dir)
794
795
  if meta is not None:
795
796
  keys = list(meta.keys())
796
797
  cols_to_remove.extend(keys)
797
798
 
799
+ labels = get_experiment_labels(self.exp_dir)
800
+ if labels is not None:
801
+ keys = list(labels.keys())
802
+ cols_to_remove.extend(labels)
803
+
798
804
  cols = np.array(list(self.df_tracks.columns))
799
805
  time_cols = np.array([c.startswith('t_') for c in cols])
800
806
  time_cols = list(cols[time_cols])
801
807
  cols_to_remove += time_cols
808
+ #cols_to_remove.extend(self.df_tracks.select_dtypes(include=['object']).columns)
809
+
810
+ print(f"{cols_to_remove=}")
802
811
 
803
812
  for tr in cols_to_remove:
804
813
  try:
@@ -2273,12 +2282,23 @@ class MeasureAnnotator(SignalAnnotator):
2273
2282
  'state', 'generation', 'root', 'parent', 'class_id', 'class', 't0', 'POSITION_X',
2274
2283
  'POSITION_Y', 'position', 'well', 'well_index', 'well_name', 'pos_name', 'index',
2275
2284
  'concentration', 'cell_type', 'antibody', 'pharmaceutical_agent', 'ID'] + self.class_cols
2276
- cols = np.array(list(self.df_tracks.columns))
2285
+
2286
+ meta = get_experiment_metadata(self.exp_dir)
2287
+ if meta is not None:
2288
+ keys = list(meta.keys())
2289
+ cols_to_remove.extend(keys)
2290
+
2291
+ labels = get_experiment_labels(self.exp_dir)
2292
+ if labels is not None:
2293
+ keys = list(labels.keys())
2294
+ cols_to_remove.extend(labels)
2295
+
2277
2296
  for tr in cols_to_remove:
2278
2297
  try:
2279
2298
  self.columns_to_rescale.remove(tr)
2280
2299
  except:
2281
2300
  pass
2301
+
2282
2302
  # print(f'column {tr} could not be found...')
2283
2303
 
2284
2304
  x = self.df_tracks[self.columns_to_rescale].values
@@ -2496,7 +2516,7 @@ class MeasureAnnotator(SignalAnnotator):
2496
2516
  if len_movie_auto is not None:
2497
2517
  self.len_movie = len_movie_auto
2498
2518
  exp_config = self.exp_dir + "config.ini"
2499
- self.channel_names, self.channels = extract_experiment_channels(exp_config)
2519
+ self.channel_names, self.channels = extract_experiment_channels(self.exp_dir)
2500
2520
  self.channel_names = np.array(self.channel_names)
2501
2521
  self.channels = np.array(self.channels)
2502
2522
  self.nbr_channels = len(self.channels)
@@ -1043,7 +1043,7 @@ class SignalAnnotator2(QMainWindow,Styles):
1043
1043
  if len_movie_auto is not None:
1044
1044
  self.len_movie = len_movie_auto
1045
1045
  exp_config = self.exp_dir +"config.ini"
1046
- self.channel_names, self.channels = extract_experiment_channels(exp_config)
1046
+ self.channel_names, self.channels = extract_experiment_channels(self.exp_dir)
1047
1047
  self.channel_names = np.array(self.channel_names)
1048
1048
  self.channels = np.array(self.channels)
1049
1049
  self.nbr_channels = len(self.channels)
@@ -39,7 +39,7 @@ class ConfigSignalAnnotator(QMainWindow, Styles):
39
39
 
40
40
  exp_config = self.exp_dir +"config.ini"
41
41
  #self.config_path = self.exp_dir + self.config_name
42
- self.channel_names, self.channels = extract_experiment_channels(exp_config)
42
+ self.channel_names, self.channels = extract_experiment_channels(self.exp_dir)
43
43
  self.channel_names = np.array(self.channel_names)
44
44
  self.channels = np.array(self.channels)
45
45
  self.log_option = False
@@ -153,7 +153,10 @@ class ConfigSurvival(QWidget, Styles):
153
153
  all_cms = list(colormaps)
154
154
  for cm in all_cms:
155
155
  if hasattr(matplotlib.cm, str(cm).lower()):
156
- self.cbs[-1].addColormap(cm.lower())
156
+ try:
157
+ self.cbs[-1].addColormap(cm.lower())
158
+ except:
159
+ pass
157
160
  #try:
158
161
  # self.cbs[-1].addColormap(cm)
159
162
  # except:
@@ -181,7 +181,7 @@ class ThresholdConfigWizard(QMainWindow, Styles):
181
181
  self.threshold_slider.setSingleStep(0.00001)
182
182
  self.threshold_slider.setTickInterval(0.00001)
183
183
  self.threshold_slider.setOrientation(1)
184
- self.threshold_slider.setDecimals(3)
184
+ self.threshold_slider.setDecimals(5)
185
185
  self.threshold_slider.setRange(np.amin(self.img[self.img==self.img]), np.amax(self.img[self.img==self.img]))
186
186
  self.threshold_slider.setValue([np.percentile(self.img.flatten(), 90), np.amax(self.img)])
187
187
  self.threshold_slider.valueChanged.connect(self.threshold_changed)
@@ -422,7 +422,7 @@ class ThresholdConfigWizard(QMainWindow, Styles):
422
422
  if len_movie_auto is not None:
423
423
  self.len_movie = len_movie_auto
424
424
  exp_config = self.exp_dir + "config.ini"
425
- self.channel_names, self.channels = extract_experiment_channels(exp_config)
425
+ self.channel_names, self.channels = extract_experiment_channels(self.exp_dir)
426
426
  self.channel_names = np.array(self.channel_names)
427
427
  self.channels = np.array(self.channels)
428
428
  self.nbr_channels = len(self.channels)
@@ -861,11 +861,12 @@ class ThresholdConfigWizard(QMainWindow, Styles):
861
861
 
862
862
  filters = threshold_instructions['filters']
863
863
  items_to_add = [f[0] + '_filter' for f in filters]
864
- self.filters_qlist.list_widget.clear()
865
- self.filters_qlist.list_widget.addItems(items_to_add)
864
+ self.preprocessing.list.list_widget.clear()
865
+ self.preprocessing.list.list_widget.addItems(items_to_add)
866
866
  self.preprocessing.list.items = filters
867
867
 
868
- self.apply_filters_btn.click()
868
+ self.preprocessing.apply_btn.click()
869
+ #self.apply_filters_btn.click()
869
870
 
870
871
  thresholds = threshold_instructions['thresholds']
871
872
  self.threshold_slider.setValue(thresholds)
celldetective/io.py CHANGED
@@ -25,7 +25,7 @@ from pathlib import Path, PurePath
25
25
  from shutil import copyfile, rmtree
26
26
 
27
27
  from celldetective.utils import _rearrange_multichannel_frame, _fix_no_contrast, zoom_multiframes,ConfigSectionMap, extract_experiment_channels, _extract_labels_from_config, get_zenodo_files, download_zenodo_file
28
- from celldetective.utils import interpolate_nan_multichannel, _estimate_scale_factor, _extract_channel_indices_from_config, _extract_channel_indices, _extract_nbr_channels_from_config, _get_img_num_per_channel, normalize_per_channel
28
+ from celldetective.utils import interpolate_nan_multichannel, _estimate_scale_factor, _extract_channel_indices_from_config, _extract_channel_indices, _extract_nbr_channels_from_config, _get_img_num_per_channel, normalize_per_channel, get_config
29
29
 
30
30
  from stardist import fill_label_holes
31
31
  from skimage.transform import resize
@@ -261,49 +261,6 @@ def get_experiment_wells(experiment):
261
261
  return np.array(wells, dtype=str)
262
262
 
263
263
 
264
- def get_config(experiment):
265
-
266
- """
267
- Retrieves the path to the configuration file for a given experiment.
268
-
269
- Parameters
270
- ----------
271
- experiment : str
272
- The file system path to the experiment directory.
273
-
274
- Returns
275
- -------
276
- str
277
- The full path to the configuration file (`config.ini`) within the experiment directory.
278
-
279
- Raises
280
- ------
281
- AssertionError
282
- If the `config.ini` file does not exist in the specified experiment directory.
283
-
284
- Notes
285
- -----
286
- - The function ensures that the provided experiment path ends with the appropriate file separator (`os.sep`)
287
- before appending `config.ini` to locate the configuration file.
288
- - The configuration file is expected to be named `config.ini` and located at the root of the experiment directory.
289
-
290
- Example
291
- -------
292
- >>> experiment = "/path/to/experiment"
293
- >>> config_path = get_config(experiment)
294
- >>> print(config_path)
295
- '/path/to/experiment/config.ini'
296
-
297
- """
298
-
299
- if not experiment.endswith(os.sep):
300
- experiment += os.sep
301
-
302
- config = experiment + 'config.ini'
303
- config = rf"{config}"
304
- assert os.path.exists(config), 'The experiment configuration could not be located...'
305
- return config
306
-
307
264
 
308
265
  def get_spatial_calibration(experiment):
309
266