celldetective 1.4.2__py3-none-any.whl → 1.5.0b1__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 (152) hide show
  1. celldetective/__init__.py +25 -0
  2. celldetective/__main__.py +62 -43
  3. celldetective/_version.py +1 -1
  4. celldetective/extra_properties.py +477 -399
  5. celldetective/filters.py +192 -97
  6. celldetective/gui/InitWindow.py +541 -411
  7. celldetective/gui/__init__.py +0 -15
  8. celldetective/gui/about.py +44 -39
  9. celldetective/gui/analyze_block.py +120 -84
  10. celldetective/gui/base/__init__.py +0 -0
  11. celldetective/gui/base/channel_norm_generator.py +335 -0
  12. celldetective/gui/base/components.py +249 -0
  13. celldetective/gui/base/feature_choice.py +92 -0
  14. celldetective/gui/base/figure_canvas.py +52 -0
  15. celldetective/gui/base/list_widget.py +133 -0
  16. celldetective/gui/{styles.py → base/styles.py} +92 -36
  17. celldetective/gui/base/utils.py +33 -0
  18. celldetective/gui/base_annotator.py +900 -767
  19. celldetective/gui/classifier_widget.py +6 -22
  20. celldetective/gui/configure_new_exp.py +777 -671
  21. celldetective/gui/control_panel.py +635 -524
  22. celldetective/gui/dynamic_progress.py +449 -0
  23. celldetective/gui/event_annotator.py +2023 -1662
  24. celldetective/gui/generic_signal_plot.py +1292 -944
  25. celldetective/gui/gui_utils.py +899 -1289
  26. celldetective/gui/interactions_block.py +658 -0
  27. celldetective/gui/interactive_timeseries_viewer.py +447 -0
  28. celldetective/gui/json_readers.py +48 -15
  29. celldetective/gui/layouts/__init__.py +5 -0
  30. celldetective/gui/layouts/background_model_free_layout.py +537 -0
  31. celldetective/gui/layouts/channel_offset_layout.py +134 -0
  32. celldetective/gui/layouts/local_correction_layout.py +91 -0
  33. celldetective/gui/layouts/model_fit_layout.py +372 -0
  34. celldetective/gui/layouts/operation_layout.py +68 -0
  35. celldetective/gui/layouts/protocol_designer_layout.py +96 -0
  36. celldetective/gui/pair_event_annotator.py +3130 -2435
  37. celldetective/gui/plot_measurements.py +586 -267
  38. celldetective/gui/plot_signals_ui.py +724 -506
  39. celldetective/gui/preprocessing_block.py +395 -0
  40. celldetective/gui/process_block.py +1678 -1831
  41. celldetective/gui/seg_model_loader.py +580 -473
  42. celldetective/gui/settings/__init__.py +0 -7
  43. celldetective/gui/settings/_cellpose_model_params.py +181 -0
  44. celldetective/gui/settings/_event_detection_model_params.py +95 -0
  45. celldetective/gui/settings/_segmentation_model_params.py +159 -0
  46. celldetective/gui/settings/_settings_base.py +77 -65
  47. celldetective/gui/settings/_settings_event_model_training.py +752 -526
  48. celldetective/gui/settings/_settings_measurements.py +1133 -964
  49. celldetective/gui/settings/_settings_neighborhood.py +574 -488
  50. celldetective/gui/settings/_settings_segmentation_model_training.py +779 -564
  51. celldetective/gui/settings/_settings_signal_annotator.py +329 -305
  52. celldetective/gui/settings/_settings_tracking.py +1304 -1094
  53. celldetective/gui/settings/_stardist_model_params.py +98 -0
  54. celldetective/gui/survival_ui.py +422 -312
  55. celldetective/gui/tableUI.py +1665 -1701
  56. celldetective/gui/table_ops/_maths.py +295 -0
  57. celldetective/gui/table_ops/_merge_groups.py +140 -0
  58. celldetective/gui/table_ops/_merge_one_hot.py +95 -0
  59. celldetective/gui/table_ops/_query_table.py +43 -0
  60. celldetective/gui/table_ops/_rename_col.py +44 -0
  61. celldetective/gui/thresholds_gui.py +382 -179
  62. celldetective/gui/viewers/__init__.py +0 -0
  63. celldetective/gui/viewers/base_viewer.py +700 -0
  64. celldetective/gui/viewers/channel_offset_viewer.py +331 -0
  65. celldetective/gui/viewers/contour_viewer.py +394 -0
  66. celldetective/gui/viewers/size_viewer.py +153 -0
  67. celldetective/gui/viewers/spot_detection_viewer.py +341 -0
  68. celldetective/gui/viewers/threshold_viewer.py +309 -0
  69. celldetective/gui/workers.py +403 -126
  70. celldetective/log_manager.py +92 -0
  71. celldetective/measure.py +1895 -1478
  72. celldetective/napari/__init__.py +0 -0
  73. celldetective/napari/utils.py +1025 -0
  74. celldetective/neighborhood.py +1914 -1448
  75. celldetective/preprocessing.py +1620 -1220
  76. celldetective/processes/__init__.py +0 -0
  77. celldetective/processes/background_correction.py +271 -0
  78. celldetective/processes/compute_neighborhood.py +894 -0
  79. celldetective/processes/detect_events.py +246 -0
  80. celldetective/processes/downloader.py +137 -0
  81. celldetective/processes/measure_cells.py +565 -0
  82. celldetective/processes/segment_cells.py +760 -0
  83. celldetective/processes/track_cells.py +435 -0
  84. celldetective/processes/train_segmentation_model.py +694 -0
  85. celldetective/processes/train_signal_model.py +265 -0
  86. celldetective/processes/unified_process.py +292 -0
  87. celldetective/regionprops/_regionprops.py +358 -317
  88. celldetective/relative_measurements.py +987 -710
  89. celldetective/scripts/measure_cells.py +313 -212
  90. celldetective/scripts/measure_relative.py +90 -46
  91. celldetective/scripts/segment_cells.py +165 -104
  92. celldetective/scripts/segment_cells_thresholds.py +96 -68
  93. celldetective/scripts/track_cells.py +198 -149
  94. celldetective/scripts/train_segmentation_model.py +324 -201
  95. celldetective/scripts/train_signal_model.py +87 -45
  96. celldetective/segmentation.py +844 -749
  97. celldetective/signals.py +3514 -2861
  98. celldetective/tracking.py +30 -15
  99. celldetective/utils/__init__.py +0 -0
  100. celldetective/utils/cellpose_utils/__init__.py +133 -0
  101. celldetective/utils/color_mappings.py +42 -0
  102. celldetective/utils/data_cleaning.py +630 -0
  103. celldetective/utils/data_loaders.py +450 -0
  104. celldetective/utils/dataset_helpers.py +207 -0
  105. celldetective/utils/downloaders.py +235 -0
  106. celldetective/utils/event_detection/__init__.py +8 -0
  107. celldetective/utils/experiment.py +1782 -0
  108. celldetective/utils/image_augmenters.py +308 -0
  109. celldetective/utils/image_cleaning.py +74 -0
  110. celldetective/utils/image_loaders.py +926 -0
  111. celldetective/utils/image_transforms.py +335 -0
  112. celldetective/utils/io.py +62 -0
  113. celldetective/utils/mask_cleaning.py +348 -0
  114. celldetective/utils/mask_transforms.py +5 -0
  115. celldetective/utils/masks.py +184 -0
  116. celldetective/utils/maths.py +351 -0
  117. celldetective/utils/model_getters.py +325 -0
  118. celldetective/utils/model_loaders.py +296 -0
  119. celldetective/utils/normalization.py +380 -0
  120. celldetective/utils/parsing.py +465 -0
  121. celldetective/utils/plots/__init__.py +0 -0
  122. celldetective/utils/plots/regression.py +53 -0
  123. celldetective/utils/resources.py +34 -0
  124. celldetective/utils/stardist_utils/__init__.py +104 -0
  125. celldetective/utils/stats.py +90 -0
  126. celldetective/utils/types.py +21 -0
  127. {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/METADATA +1 -1
  128. celldetective-1.5.0b1.dist-info/RECORD +187 -0
  129. {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/WHEEL +1 -1
  130. tests/gui/test_new_project.py +129 -117
  131. tests/gui/test_project.py +127 -79
  132. tests/test_filters.py +39 -15
  133. tests/test_notebooks.py +8 -0
  134. tests/test_tracking.py +232 -13
  135. tests/test_utils.py +123 -77
  136. celldetective/gui/base_components.py +0 -23
  137. celldetective/gui/layouts.py +0 -1602
  138. celldetective/gui/processes/compute_neighborhood.py +0 -594
  139. celldetective/gui/processes/downloader.py +0 -111
  140. celldetective/gui/processes/measure_cells.py +0 -360
  141. celldetective/gui/processes/segment_cells.py +0 -499
  142. celldetective/gui/processes/track_cells.py +0 -303
  143. celldetective/gui/processes/train_segmentation_model.py +0 -270
  144. celldetective/gui/processes/train_signal_model.py +0 -108
  145. celldetective/gui/table_ops/merge_groups.py +0 -118
  146. celldetective/gui/viewers.py +0 -1354
  147. celldetective/io.py +0 -3663
  148. celldetective/utils.py +0 -3108
  149. celldetective-1.4.2.dist-info/RECORD +0 -123
  150. {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/entry_points.txt +0 -0
  151. {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/licenses/LICENSE +0 -0
  152. {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/top_level.txt +0 -0
File without changes
@@ -0,0 +1,271 @@
1
+ from multiprocessing import Process
2
+ import time
3
+ import os
4
+ from pathlib import PurePath, Path
5
+ from tifffile import imwrite
6
+ from celldetective import get_logger
7
+ from celldetective.utils.experiment import extract_experiment_channels
8
+ from celldetective.utils.image_loaders import _get_img_num_per_channel
9
+ from celldetective.utils.parsing import config_section_to_dict, _extract_channel_indices_from_config
10
+
11
+ logger = get_logger(__name__)
12
+
13
+
14
+ class BackgroundCorrectionProcess(Process):
15
+
16
+ def __init__(self, queue=None, process_args=None):
17
+
18
+ super().__init__()
19
+
20
+ self.queue = queue
21
+
22
+ if process_args is not None:
23
+ for key, value in process_args.items():
24
+ setattr(self, key, value)
25
+
26
+ self.sum_done = 0
27
+ self.t0 = time.time()
28
+
29
+ def run(self):
30
+
31
+ logger.info("Start background correction process...")
32
+
33
+ try:
34
+ # Load config to get movie length for progress estimation
35
+ self.config = PurePath(self.exp_dir, Path("config.ini"))
36
+ self.len_movie = float(
37
+ config_section_to_dict(self.config, "MovieSettings")["len_movie"]
38
+ )
39
+ self.nbr_channels = len(extract_experiment_channels(self.exp_dir)[0])
40
+ channel_indices = _extract_channel_indices_from_config(
41
+ self.config, [self.target_channel]
42
+ )
43
+ self.img_num_channels = _get_img_num_per_channel(
44
+ channel_indices, self.len_movie, self.nbr_channels
45
+ )
46
+
47
+ logger.info("Process initialized.")
48
+
49
+ except Exception as e:
50
+ logger.error(f"Error initializing process: {e}")
51
+ self.queue.put("error")
52
+ return
53
+
54
+ export = getattr(self, "export", False)
55
+ return_stacks = getattr(self, "return_stacks", True)
56
+ movie_prefix = getattr(self, "movie_prefix", None)
57
+ export_prefix = getattr(self, "export_prefix", "Corrected")
58
+ correction_type = getattr(self, "correction_type", "model")
59
+
60
+ # Timestamps for estimation
61
+ self.t0_well = time.time()
62
+ self.t0_pos = time.time() # resets per well
63
+ self.count_pos = 0 # pos processed in current well
64
+
65
+ def progress_callback(**kwargs):
66
+
67
+ level = kwargs.get("level", None)
68
+ iteration = kwargs.get("iter", 0)
69
+ total = kwargs.get("total", 1)
70
+ stage = kwargs.get("stage", "")
71
+
72
+ current_time = time.time()
73
+ status = kwargs.get("status", None)
74
+ image_preview = kwargs.get("image_preview", None)
75
+ # Legacy support
76
+ if image_preview is None:
77
+ image_preview = kwargs.get("bg_image", None)
78
+
79
+ data = {}
80
+
81
+ if status:
82
+ data["status"] = status
83
+
84
+ if image_preview is not None:
85
+ data["image_preview"] = image_preview
86
+
87
+ if level == "well":
88
+ if iteration == 0:
89
+ self.t0_well = current_time
90
+
91
+ well_progress = ((iteration) / total) * 100
92
+ if well_progress > 100:
93
+ well_progress = 100
94
+ data["well_progress"] = well_progress
95
+
96
+ elapsed = current_time - self.t0_well
97
+ if iteration > 0:
98
+ avg = elapsed / iteration
99
+ rem = total - iteration
100
+ rem_t = rem * avg
101
+ mins = int(rem_t // 60)
102
+ secs = int(rem_t % 60)
103
+ data["well_time"] = f"Estimated: {mins} m {secs} s"
104
+ else:
105
+ data["well_time"] = "Estimating..."
106
+
107
+ # Reset pos timer for new well
108
+ self.t0_pos = current_time
109
+ self.count_pos = 0
110
+
111
+ elif level == "position":
112
+
113
+ self.count_pos = iteration
114
+
115
+ current_stage = getattr(self, "current_stage", None)
116
+ reset_timer = False
117
+ if stage != current_stage:
118
+ self.current_stage = stage
119
+ reset_timer = True
120
+ if iteration == 0:
121
+ reset_timer = True
122
+
123
+ if reset_timer:
124
+ self.t0_pos = current_time
125
+
126
+ pos_progress = ((iteration + 1) / total) * 100
127
+ if pos_progress > 100:
128
+ pos_progress = 100
129
+ data["pos_progress"] = pos_progress
130
+
131
+ elapsed = current_time - self.t0_pos
132
+
133
+ measured_count = iteration
134
+ if measured_count > 0:
135
+ avg = elapsed / measured_count
136
+ rem = total - (iteration + 1)
137
+ rem_t = rem * avg
138
+ mins = int(rem_t // 60)
139
+ secs = int(rem_t % 60)
140
+ data["pos_time"] = f"{stage}: {mins} m {secs} s"
141
+ else:
142
+ data["pos_time"] = f"{stage}..."
143
+
144
+ elif level == "frame":
145
+ if iteration == 0:
146
+ self.t0_frame = current_time
147
+
148
+ frame_progress = ((iteration + 1) / total) * 100
149
+ if frame_progress > 100:
150
+ frame_progress = 100
151
+ data["frame_progress"] = frame_progress
152
+
153
+ elapsed = current_time - getattr(self, "t0_frame", current_time)
154
+ measured_count = iteration
155
+
156
+ if measured_count > 0:
157
+ avg = elapsed / measured_count
158
+ rem = total - (iteration + 1)
159
+ rem_t = rem * avg
160
+ mins = int(rem_t // 60)
161
+ secs = int(rem_t % 60)
162
+ data["frame_time"] = f"{mins} m {secs} s"
163
+ else:
164
+ data["frame_time"] = f"{iteration + 1}/{total} frames"
165
+
166
+ if data:
167
+ self.queue.put(data)
168
+
169
+ try:
170
+ if correction_type == "model-free":
171
+ from celldetective.preprocessing import correct_background_model_free
172
+
173
+ corrected_stacks = correct_background_model_free(
174
+ self.exp_dir,
175
+ well_option=self.well_option,
176
+ position_option=self.position_option,
177
+ target_channel=self.target_channel,
178
+ mode=getattr(self, "mode", "timeseries"),
179
+ threshold_on_std=self.threshold_on_std,
180
+ frame_range=getattr(self, "frame_range", [0, 5]),
181
+ optimize_option=getattr(self, "optimize_option", False),
182
+ opt_coef_range=getattr(self, "opt_coef_range", [0.95, 1.05]),
183
+ opt_coef_nbr=getattr(self, "opt_coef_nbr", 100),
184
+ operation=self.operation,
185
+ clip=self.clip,
186
+ offset=getattr(self, "offset", None),
187
+ export=export,
188
+ return_stacks=return_stacks,
189
+ fix_nan=getattr(self, "fix_nan", False),
190
+ activation_protocol=self.activation_protocol,
191
+ show_progress_per_well=False,
192
+ show_progress_per_pos=False,
193
+ movie_prefix=movie_prefix,
194
+ export_prefix=export_prefix,
195
+ progress_callback=progress_callback,
196
+ )
197
+ elif correction_type == "offset":
198
+ from celldetective.preprocessing import correct_channel_offset
199
+
200
+ corrected_stacks = correct_channel_offset(
201
+ self.exp_dir,
202
+ well_option=self.well_option,
203
+ position_option=self.position_option,
204
+ target_channel=self.target_channel,
205
+ export=export,
206
+ return_stacks=return_stacks,
207
+ show_progress_per_well=False,
208
+ show_progress_per_pos=False,
209
+ movie_prefix=movie_prefix,
210
+ export_prefix=export_prefix,
211
+ progress_callback=progress_callback,
212
+ correction_horizontal=getattr(self, "correction_horizontal", 0),
213
+ correction_vertical=getattr(self, "correction_vertical", 0),
214
+ **self.kwargs if hasattr(self, "kwargs") else {},
215
+ )
216
+ else:
217
+ from celldetective.preprocessing import correct_background_model
218
+
219
+ corrected_stacks = correct_background_model(
220
+ self.exp_dir,
221
+ well_option=self.well_option,
222
+ position_option=self.position_option,
223
+ target_channel=self.target_channel,
224
+ model=self.model,
225
+ threshold_on_std=self.threshold_on_std,
226
+ operation=self.operation,
227
+ clip=self.clip,
228
+ export=export,
229
+ return_stacks=return_stacks,
230
+ activation_protocol=self.activation_protocol,
231
+ show_progress_per_well=False,
232
+ show_progress_per_pos=False,
233
+ movie_prefix=movie_prefix,
234
+ export_prefix=export_prefix,
235
+ progress_callback=progress_callback,
236
+ downsample=getattr(self, "downsample", 10),
237
+ subset_indices=getattr(self, "subset_indices", None),
238
+ )
239
+
240
+ if return_stacks and corrected_stacks and len(corrected_stacks) > 0:
241
+ # If doing a preview (subset_indices is set), return via queue instead of disk
242
+ if getattr(self, "subset_indices", None) is not None:
243
+ self.queue.put({"status": "result", "data": corrected_stacks[0]})
244
+ else:
245
+ temp_path = os.path.join(self.exp_dir, "temp_corrected_stack.tif")
246
+ try:
247
+ imwrite(temp_path, corrected_stacks[0])
248
+ logger.info(f"Saved temp stack to {temp_path}")
249
+ except Exception as temp_e:
250
+ logger.error(f"Failed to save temp stack: {temp_e}")
251
+
252
+ self.queue.put(
253
+ {
254
+ "well_progress": 100,
255
+ "pos_progress": 100,
256
+ "frame_progress": 100,
257
+ "status": "finished",
258
+ }
259
+ )
260
+
261
+ except Exception as e:
262
+ logger.error(f"Error in background correction process: {e}")
263
+ self.queue.put({"status": "error", "message": str(e)})
264
+ return
265
+
266
+ self.queue.put("finished")
267
+ self.queue.close()
268
+
269
+ def end_process(self):
270
+ self.terminate()
271
+ self.queue.put("finished")